Minimum-Energy Crossing Points (MECP / MECI)#
A minimum-energy crossing point is the lowest-energy molecular geometry at which two electronic potential energy surfaces are degenerate. It plays the role in spin-forbidden and photochemical reactions that a transition state plays in ordinary chemistry: in nonadiabatic transition state theory the rate depends on the crossing energy through a Boltzmann factor [Lykhin et al., 2016], so the lowest crossing point is the one that sets the rate.
Two cases, distinguished by whether the two states can couple:
States |
Degeneracy conditions |
Seam dimension |
|
|---|---|---|---|
MECP (spin crossing) |
different multiplicity — e.g. singlet/triplet |
|
3N−7 |
MECI (conical intersection) |
same multiplicity |
|
3N−8 |
Written as an optimization problem this is a small, dense, continuous, nonconvex NLP with one or two nonlinear equality constraints:
which is exactly what discopt’s spatial branch-and-bound is for. What discopt adds over the established MECP algorithms — gradient projection [Bearpark et al., 1994], the Lagrange–Newton method [Yarkony, 1993], the smooth penalty method [Levine et al., 2008], branching-plane updating [Maeda et al., 2010] — is a certificate: all of those are local methods, and the state of the art for finding multiple crossing points is stochastic exploration [Lindner et al., 2019]. None of them can tell you the crossing point you found is the lowest one.
Warning
How you write the problem matters enormously — far more than for a local solver, where all the forms below are equivalent. The same physics spans about four orders of magnitude in tractability depending on the formulation. The two mistakes to avoid are in “Two things not to do” below: the penalty objective, and free Cartesian coordinates.
A spin-crossing MECP#
For states of different multiplicity the interstate coupling vanishes by spin symmetry, so the adiabatic surfaces are the diabatic ones and the problem above needs no reformulation. Here are two Morse-oscillator surfaces over the three bond distances of a triatomic — the singlet deeper and shorter-bonded, the triplet shallower and longer-bonded:
import discopt.modeling as dm
D1, a1, b1 = 4.0, 1.55, 1.15 # lower state: depth, range, r_eq
D2, a2, b2, dE = 2.5, 1.00, 1.66, -0.40 # upper state, offset down
TILT = [0.10, -0.06, 0.03] # identical in both states (see note below)
def surface(d, D, a, b, dE):
"""Sum of Morse oscillators over the bond distances ``d``."""
val = dE
for k, dk in enumerate(d):
val = val + D * (1.0 - dm.exp(-a * (dk - b))) ** 2 + TILT[k] * dk
return val
m = dm.Model("mecp_triatomic")
d = [m.continuous(f"d{k}", lb=0.70, ub=3.20) for k in range(3)]
W1 = surface(d, D1, a1, b1, 0.0)
W2 = surface(d, D2, a2, b2, dE)
m.minimize(W1) # minimize the energy ...
m.subject_to(W1 - W2 == 0) # ... on the crossing seam
result = m.solve(time_limit=60)
print(result.status, result.objective, result.bound, result.gap_certified)
print({k: float(v) for k, v in result.x.items()})
optimal 0.8199959475426919 0.8199170904357771 True
{'d0': 1.3244686148294158, 'd1': 1.3410473365240474, 'd2': 1.33158614366199}
Five branch-and-bound nodes, under a second, gap_certified=True. That last flag is
the whole point: the reported gap is a proof, so this is the lowest crossing
point anywhere in the box, not merely the one nearest the starting guess.
Note
TILT is added to both surfaces. A term common to both states cancels
from the crossing condition W₁ − W₂ = 0 while still changing the energy
along the seam — a convenient way to break a model’s permutation symmetry
without moving the seam. Physically it plays the role of a substituent effect
or an external field.
Why the certificate is worth having#
Consider a spin-crossing model whose lower state has two conformer wells (an
inversion or torsion coordinate), so the seam has two low-lying basins at
different energies — the ordinary situation for a real molecule. A brute-force
grid-and-bisect search over the 2-coordinate version finds the global crossing
point at E = 1.270685 and a second one at E = 1.548537.
Running the two standard local algorithms from 200 uniformly random starting geometries:
Method |
reached the seam |
found the global basin |
|---|---|---|
Smooth penalty [Levine et al., 2008] (σ escalated 0.5→128, L-BFGS-B) |
200/200 |
127/200 (63.5 %) |
Direct constrained (SLSQP on |
199/200 |
78/199 (39.2 %) |
Both reliably reach the seam. Neither reliably reaches the bottom of it: a local search reports a crossing point 0.278 too high in 37 % to 61 % of runs, and SLSQP sometimes lands in a third basin higher still. discopt returns the global basin with a proof in 0.5 s and 5 nodes.
If the energy unit is read as eV, that 0.278 basin error is a factor of
exp(0.278 / 0.02585) ≈ 4.6 × 10⁴ in a nonadiabatic-TST rate constant at
300 K. A local MECP optimizer that converges to the second-lowest crossing
point does not give a slightly worse answer — it gives a rate wrong by four to
five orders of magnitude.
Two things not to do#
1. Do not hand the solver the penalty objective#
The penalty method [Levine et al., 2008] is the standard MECP algorithm in quantum chemistry, and it minimizes an unconstrained objective:
discopt can express this — dm.abs and division are both supported — and it is
exactly the wrong thing to give a relaxation-based solver. The numerator is
squared and the denominator’s lower bound is the small smoothing parameter α,
so the McCormick relaxation of that quotient is enormous. On the
two-variable model above:
σ, α |
status |
objective |
dual bound |
relative gap |
|---|---|---|---|---|
3.5, 0.025 |
|
3.3125 |
−11177.6 |
3.4 × 10³ |
20, 0.005 |
|
10.5175 |
−212877.3 |
2.0 × 10⁴ |
Compare 5 nodes and a certificate for the constrained form. Use the constrained form.
The penalty objective is the right choice in one place: solver="direct",
the derivative-free search, needs an unconstrained problem. There it works
well — but it returns no bound, and reports gap_certified=False.
2. Do not use free Cartesian coordinates#
Production MECP codes work in 3N Cartesians and project out the six translation and rotation modes. For a local optimizer that redundancy is harmless. For branch-and-bound it is fatal: every translation and rotation of the MECP is also an MECP, so the set of global optima is a 6-dimensional manifold, no finite subdivision isolates an optimum, and the dual bound never closes.
The same triatomic MECP, three ways:
Coordinates |
free vars |
certified |
dual bound |
nodes |
wall |
|---|---|---|---|---|---|
internal (3 bond distances) |
3 |
yes |
0.819917 |
5 |
0.96 s |
free Cartesian (9, 6 redundant) |
9 |
no — 93 % gap at 180 s |
−0.109858 |
341 |
180 s |
gauge-fixed Cartesian |
3 |
yes |
0.819964 |
329 |
14.6 s |
All three find the identical geometry and energy — this is purely about
certifiability. Gauge fixing (atom 0 at the origin, atom 1 on the +x axis,
atom 2 in the xy half-plane) restores it, at 66× the nodes of the
internal-coordinate form because the energies then reach the coordinates
through sqrt of a sum of squares rather than directly.
Pose MECP problems in internal coordinates, or in a gauge-fixed Cartesian frame.
Conical intersections: avoiding the radical#
For two states of the same multiplicity the adiabatic energies are the eigenvalues of a 2×2 matrix,
and the radical vanishes at the solution, so E_± are not differentiable to
any order exactly where the answer is. That non-differentiability is the reason
the field’s algorithms are built the way they are, and the modern answer is to
work with smooth quantities instead [Fdez. Galván and Lindh, 2023, Richings and Habershon, 2023].
discopt has dm.sqrt, so the adiabatic form is expressible and does solve. But
two reformulations are better.
Diabatic form — the cheapest#
Degeneracy of a 2×2 symmetric matrix is W₁₁ = W₂₂ and W₁₂ = 0. In a
diabatic representation the radical disappears entirely:
m = dm.Model("meci_diabatic")
qt = m.continuous("qt", lb=-30, ub=30) # tuning mode
qc = m.continuous("qc", lb=-30, ub=30) # coupling mode
# Two-state linear vibronic coupling Hamiltonian (Koeppel/Domcke/Cederbaum)
common = 0.5 * 0.020 * qt**2 + 0.5 * 0.015 * qc**2
W11 = 0.00 + 0.12 * qt + common
W22 = 0.30 - 0.18 * qt + common
W12 = 0.08 * qc
m.minimize(0.5 * (W11 + W22))
m.subject_to(W11 - W22 == 0)
m.subject_to(W12 == 0)
result = m.solve(time_limit=60)
print(result.status, result.objective, result.gap_certified, result.node_count)
optimal 0.13 True 0
Zero nodes: the convexity detector disposes of it at the root. This model’s
MECI is analytic (qt = 1, qc = 0, E = 0.13), and that is what comes back.
For comparison, the same problem posed adiabatically — min E₋ s.t. E₊−E₋ = 0
with dm.sqrt — also certifies and also lands on 0.13, but takes 1.53 s
instead of 0.03 s. The radical costs a 50× wall-clock factor for the same
answer, because the tangent facet of sqrt at zero has infinite slope and is
dropped from the envelope.
Characteristic-polynomial form — no diabatization needed#
The coefficients of the characteristic polynomial of the potential matrix are smooth through the intersection even though its eigenvalues are not [Richings and Habershon, 2023]:
Degeneracy is the vanishing of the discriminant, and at degeneracy
E₁ = E₂ = T/2:
m.minimize(0.5 * T)
m.subject_to(T**2 - 4 * D <= 0) # (E₁-E₂)² ≤ 0, so ≤ is exact here
The inequality is valid because T² − 4D = (E₁−E₂)² ≥ 0 identically, and it
solves faster than the equality version (0.85 s vs 2.48 s for the same 7-node
tree). This form needs no diabatization at all — T and D are computable
from the adiabatic energies directly, which is why they are what the ML-seam
literature fits.
Warning
The characteristic-polynomial form is not free, and on a spin crossing it
is a bad trade: squaring the surfaces widens the relaxation. On the two-basin
model where W₁ − W₂ = 0 certified in 5 nodes and 0.5 s, T² − 4D ≤ 0 did not
certify in 300 s (3353 nodes, 1 % gap). Use the plain difference whenever the
coupling vanishes; reach for the characteristic polynomial only for a
same-spin conical intersection.
How large a molecule?#
A molecule with N atoms has 3N−6 internal coordinates. Certified solve cost on the two-basin spin-crossing model, 300 s limit:
coordinates |
3N−6 ⇒ |
nodes |
wall |
certified |
|---|---|---|---|---|
3 |
triatomic |
47 |
1.5 s |
yes |
6 |
4 atoms |
1113 |
43 s |
yes |
8 |
— |
7709 |
194 s |
yes |
9 |
5 atoms |
13585 |
300 s |
no — 0.6 % gap when the clock ran out |
Cost grows about ×2.7 per added coordinate, which is the expected exponential for spatial branch-and-bound. So certified global MECP is available outright for triatomics through roughly 5-atom systems, and for larger molecules only in a reduced set of active coordinates with the rest frozen. That is how MECP scans on large systems are done anyway — but the certificate then applies to the reduced subspace, not the full geometry, and should be described that way.
Several states: which pair crosses lowest?#
A molecule usually has more than two low-lying states, and the crossing that matters is the lowest one over any pair. That is a disjunction, so the problem becomes an MINLP — binaries choose the active pair:
m.minimize(E)
m.subject_to(sum(y) == 1) # exactly one pair is active
for p, (i, j) in enumerate(PAIRS):
slack = BIGM * (1 - y[p])
m.subject_to(w[i] - w[j] <= slack) # y_p=1 => states i,j degenerate
m.subject_to(w[j] - w[i] <= slack)
m.subject_to(E - w[i] <= slack) # y_p=1 => E is that energy
m.subject_to(w[i] - E <= slack)
Over three states (three candidate pairs, two coordinates), against the obvious baseline of enumerating the pairs as separate continuous solves:
Route |
energy |
certified |
nodes |
wall |
|---|---|---|---|---|
enumeration (3 separate solves) |
1.270684 |
yes |
11 |
2.14 s |
disjunctive MINLP (1 solve) |
1.270684 |
yes |
51 |
5.70 s |
Both certify and agree. At three states, enumeration wins — three tiny independent problems beat one MINLP carrying binaries and big-M slack for all of them. The disjunctive formulation is the interesting one when the number of states grows, since pairs grow as O(K²) and a single tree can prune a whole pair on a bound instead of solving it; note that in the run above one pair (the two upper states) never crosses inside the box at all, and enumeration spends a solve finding that out. Where the crossover lies has not been measured.
Note
The example uses a hand-rolled big-M taken from a grid maximum, validated after
the fact by checking the selected pair really is degenerate at the solution
(|W₀−W₁| = 5.3e-15). For production use, derive the constant from interval
bounds, or hand the disjunction to discopt’s GDP path via gdp_method instead
of writing big-M by hand.
Real surfaces are black boxes#
Everything above assumes an algebraic expression for the energies. Real E₁
and E₂ come from an electronic-structure program, so a certified answer is
necessarily a statement about a surrogate, not about the true surface.
Three routes, in decreasing order of what they give you:
Write the surrogate symbolically. If the fit has a known functional form — Morse, polynomial, Gaussian, permutationally-invariant-polynomial — build it from
dm.*primitives, optionally packaged withdm.udf. This is the only route that certifies efficiently (47 nodes / 1.6 s on a 3-coordinate problem), anddm.udfis exactly as fast as inline primitives because it is a documented pass-through.A trained network via
discopt.ml. Fitting two tanh networks to the two surfaces and embedding them withadd_predictor()models the problem correctly and returns a good geometry, but does not currently certify: two 12×12 nets over three coordinates gave a dual bound of −20.6 against an optimum near 1.6 after 601 s. Useful for the answer, not for the proof.solver="direct"on a genuinely opaque body. Wrap the electronic-structure call withdm.customand use the derivative-free global search (see direct_global). Fast and good in practice — 1.2 s to within 1.4 × 10⁻⁴ of the certified optimum on the model above — with no bound, honestly reported asgap_certified=False.
Note what a surrogate certificate does and does not mean: it certifies the
global optimum of the fit. Turning that into a statement about the true
surface needs a validated error bound |E_true − E_surrogate| ≤ ε, so that
optimizing the surrogate ± ε brackets the true MECP. discopt has nothing that
produces such a bound for a data-fitted surrogate. (Its Taylor/Chebyshev remainder
models for factorable functions were retired in #1873 with the McCormick-NLP route
they served.) That gap is open.
Summary#
If you have |
Use |
Certified? |
|---|---|---|
Two spin-different surfaces, algebraic |
|
yes, cheaply |
Same-spin, diabatic representation available |
|
yes, cheapest |
Same-spin, adiabatic energies only |
|
yes, more expensive |
A numerical tolerance on the gap |
|
yes |
A trained-network surrogate |
no — good answer, no proof |
|
A true black box |
|
no, by design |
— |
the penalty objective in branch-and-bound |
no — do not |
— |
free Cartesian coordinates |
no — do not |
The measurements behind every number on this page, and the gaps that remain,
are in docs/dev/mecp-readiness-2026-08-22.md; the probe scripts are in
scratchpad/mecp/.