From Singularities to Dynkin Diagrams

Arnold’s ADE Classification

1. The Setup: Critical Points of Smooth Functions

Consider a smooth function \(f: \mathbb{R}^n \to \mathbb{R}\). A point \(x_0\) is a critical point if all partial derivatives vanish:

\[\nabla f(x_0) = 0\]

The critical point is non-degenerate if the Hessian matrix

\[H_{ij} = \frac{\partial^2 f}{\partial x_i \partial x_j}(x_0)\]

has full rank (\(\det H \neq 0\)). By the Morse Lemma, every non-degenerate critical point can be brought to the normal form

\[f(x) = \pm x_1^2 \pm x_2^2 \pm \cdots \pm x_n^2\]

by a smooth change of coordinates. Non-degenerate critical points are completely classified by a single integer (the index = number of minus signs), and they are stable: small perturbations of \(f\) don’t change the local picture.

The interesting story begins with degenerate critical points, where \(\det H = 0\).

2. Equivalence of Singularities

Two function germs \(f\) and \(g\) (functions defined near the origin with \(f(0) = g(0) = 0\), \(\nabla f(0) = \nabla g(0) = 0\)) are right-equivalent if there exists a smooth change of coordinates \(\phi\) near 0 such that:

\[g(x) = f(\phi(x))\]

This is the natural notion of “same singularity” – if you can deform the domain to turn one function into the other, they have the same type.

Example. \(f(x,y) = x^3 + y^2\) and \(g(x,y) = x^3 + 2y^2\) are right-equivalent (via \(\phi(x,y) = (x, y/\sqrt{2})\)), but \(f(x,y) = x^3 + y^2\) and \(h(x,y) = x^4 + y^2\) are not.

How do we prove inequivalence?

Showing that two singularities are equivalent is straightforward: exhibit the coordinate change \(\phi\). But how can we be certain that no smooth change of coordinates exists?

The answer is invariants – quantities that are computable from \(f\) and that remain unchanged under any smooth coordinate substitution. If an invariant differs between \(f\) and \(g\), no coordinate change can relate them.

Invariant 1: The Milnor number. The most basic invariant is the Milnor number \(\mu\), defined as the dimension of the local algebra (also called the Jacobian algebra):

\[\mu(f) = \dim_\mathbb{R} \frac{\mathcal{O}_n}{J(f)}, \qquad J(f) = \left\langle \frac{\partial f}{\partial x_1}, \ldots, \frac{\partial f}{\partial x_n} \right\rangle\]

Here \(\mathcal{O}_n\) is the ring of smooth function germs at the origin, and \(J(f)\) is the ideal generated by the partial derivatives. The quotient has finite dimension precisely when the singularity is isolated.

The Milnor number is invariant under coordinate changes because if \(g = f \circ \phi\), the chain rule gives \(J(g) = J(f) \circ \phi\) (up to an invertible Jacobian factor), so the quotient algebras are isomorphic.

The mutation_game library can compute this:

>>> from sympy import symbols
>>> from mutation_game import milnor_number
>>> x, y = symbols('x y')  # define the symbolic variables

>>> milnor_number(x**3 + y**2, (x, y))  # A2 singularity: mu = 2
2
>>> milnor_number(x**4 + y**2, (x, y))  # A3 singularity: mu = 3
3

Example. For \(f = x^3 + y^2\) (type \(A_2\)):

\[J(f) = \langle 3x^2, 2y \rangle\]

The quotient \(\mathcal{O}_2 / \langle x^2, y \rangle\) has basis \(\{1, x\}\), so \(\mu = 2\).

For \(h = x^4 + y^2\) (type \(A_3\)):

\[J(h) = \langle 4x^3, 2y \rangle\]

The quotient \(\mathcal{O}_2 / \langle x^3, y \rangle\) has basis \(\{1, x, x^2\}\), so \(\mu = 3\).

Since \(\mu(f) = 2 \neq 3 = \mu(h)\), no smooth coordinate change can transform \(f\) into \(h\).

Invariant 2: The corank. The corank of a singularity is the number of zero eigenvalues of the Hessian matrix at the critical point (equivalently, \(n\) minus the rank of \(H\)). This is invariant because the rank of the Hessian is preserved under smooth coordinate changes (which act on \(H\) by congruence: \(H \mapsto J^\top H J\) where \(J\) is the Jacobian of \(\phi\), which is invertible).

The corank separates the families immediately:

  • Corank 1: \(A_n\) singularities (one degenerate direction)

  • Corank 2: \(D_n\) and \(E_n\) singularities (two degenerate directions)

>>> from sympy import symbols
>>> from mutation_game import corank
>>> x, y = symbols('x y')

>>> corank(x**3 + y**2, (x, y))    # A2: one degenerate direction
1
>>> corank(x**2*y + y**3, (x, y))  # D4: two degenerate directions
2

Invariant 3: The local algebra itself. The most powerful invariant is the full isomorphism class of the local algebra \(\mathcal{O}_n / J(f)\). The Mather–Yau theorem (1982) states that for isolated singularities, this is a complete invariant:

Two function germs \(f\) and \(g\) with isolated singularities are right-equivalent if and only if their local algebras are isomorphic as \(\mathbb{R}\)-algebras.

This is a deep result: it says the algebraic structure of the quotient ring captures everything about the geometry of the singularity. The ADE normal forms are mutually inequivalent because their local algebras are pairwise non-isomorphic (distinct \(\mu\) values, except \(D_n\) and \(A_n\) at the same \(n\), which are separated by the corank).

The classify function automates the full classification:

>>> from sympy import symbols
>>> from mutation_game import classify
>>> x, y = symbols('x y')

>>> classify(x**3 + y**2, (x, y))    # uses mu and corank to determine type
'A2'
>>> classify(x**2*y + y**3, (x, y))  # corank 2, mu=4 -> D4
'D4'
>>> classify(x**3 + y**4, (x, y))    # corank 2, mu=6, pure cube 3-jet -> E6
'E6'

How do we construct the coordinate change explicitly?

Knowing two singularities are equivalent (same invariants) doesn’t hand us the map \(\phi\). There are three main techniques for constructing it.

Method 1: The homotopy method (Moser’s path method).

Given \(f\) and \(g\) with the same type, define a one-parameter family:

\[F_t = (1-t) f + t g, \qquad t \in [0, 1]\]

We seek a family of diffeomorphisms \(\phi_t\) satisfying \(F_t \circ \phi_t = f\) for all \(t\), with \(\phi_0 = \text{id}\). If we can write:

\[g(x) - f(x) = \sum_{i=1}^n a_i(x) \frac{\partial F_t}{\partial x_i}(x)\]

then \(\dot{\phi}_t = -(a_1, \ldots, a_n)\) defines a flow, and integrating from \(t = 0\) to \(t = 1\) gives \(\phi = \phi_1\).

>>> from sympy import symbols, expand
>>> from mutation_game import homotopy_equivalence
>>> x, y = symbols('x y')

>>> f = x**3 + y**2                           # source singularity
>>> g = x**3 + 5*y**2                          # target (same type A2)
>>> result = homotopy_equivalence(f, g, (x, y))  # find coord change

>>> result['coordinate_change']  # the map phi such that g(phi(x,y)) = f(x,y)
{x: x, y: sqrt(5)*y/5}

>>> # verify: substituting phi into g recovers f
>>> expand(g.subs(y, result['coordinate_change'][y]))
x**3 + y**2

The coordinate change \(\phi(x,y) = (x, y/\sqrt{5})\) transforms \(g\) back into \(f\).

Method 2: Jet determination (finite order Taylor matching).

For finitely determined singularities, one eliminates higher-order terms order by order via polynomial coordinate substitutions:

Type

Normal form

Determinacy

\(A_n\)

\(x^{n+1}\)

\((n+1)\)-determined

\(D_n\)

\(x^2 y + y^{n-1}\)

\((n-1)\)-determined

\(E_6\)

\(x^3 + y^4\)

4-determined

\(E_7\)

\(x^3 + xy^3\)

4-determined

\(E_8\)

\(x^3 + y^5\)

5-determined

>>> from sympy import symbols
>>> from mutation_game import jet_reduce
>>> x = symbols('x')

>>> # reduce x^3 + x^4 to A2 normal form u^3
>>> # by finding a coordinate change x = phi(u) that kills higher terms
>>> result = jet_reduce(x**3 + x**4, x, 3)

>>> result['normal_form']  # the target normal form
u**3
>>> result['coordinate_change']  # x = u - u^2/3 + u^3/3 - ...
-35*u**4/81 + u**3/3 - u**2/3 + u

Method 3: The Splitting Lemma.

Separates non-degenerate directions by completing the square:

>>> from sympy import symbols
>>> from mutation_game import splitting_lemma
>>> x, y = symbols('x y')

>>> # split f = x^2 + x*y^2 + y^4 into non-degenerate + residual parts
>>> result = splitting_lemma(x**2 + x*y**2 + y**4, (x, y))

>>> result['quadratic_part']  # non-degenerate direction (Morse part)
u0**2
>>> result['residual']        # remaining singularity in fewer variables
3*y**4/4
>>> result['corank']          # number of degenerate directions
1

The function \(x^2 + xy^2 + y^4\) splits into \(u_0^2 + \frac{3}{4}y^4\), separating the non-degenerate \(x\)-direction from the \(A_3\) singularity in \(y\).

3. Simple Singularities

A singularity is simple if a sufficiently small neighborhood of \(f\) in function space contains only finitely many equivalence classes of singularities.

Arnold’s Theorem (1972). The simple singularities are exactly:

Type

Normal form

Milnor number \(\mu\)

\(A_n\) (\(n \geq 1\))

\(x^{n+1}\)

\(n\)

\(D_n\) (\(n \geq 4\))

\(x^2 y + y^{n-1}\)

\(n\)

\(E_6\)

\(x^3 + y^4\)

6

\(E_7\)

\(x^3 + xy^3\)

7

\(E_8\)

\(x^3 + y^5\)

8

The Milnor number counts the number of non-degenerate critical points the singularity splits into under a generic perturbation.

Note

The normal forms are written in the minimal number of variables. In higher dimensions, add a non-degenerate quadratic: \(x_1^{n+1} \pm x_2^2 \pm \cdots \pm x_m^2\). This doesn’t affect the classification (by the Splitting Lemma).

4. Why ADE? The Milnor Fiber and the Intersection Form

The connection to Dynkin diagrams is through topology. Here we work in the complex setting, considering \(f: \mathbb{C}^2 \to \mathbb{C}\).

The Milnor Fiber

Given a singularity \(f\) with \(f(0) = 0\), perturb it slightly to get \(f_t\) (a Morsification). The fiber

\[F_t = \{x \in B_\varepsilon : f_t(x) = \delta\}\]

is a smooth manifold called the Milnor fiber. For surface singularities in \(\mathbb{C}^2\), it is a surface of genus \(g\) with boundary, where \(\mu\) is the Milnor number.

Vanishing Cycles

Each of the \(\mu\) non-degenerate critical points of \(f_t\) contributes a vanishing cycle \(\delta_i \in H_1(F_t, \mathbb{Z})\) – a loop that shrinks to a point as \(t \to 0\). The \(\mu\) vanishing cycles form a basis of \(H_1(F_t, \mathbb{Z}) \cong \mathbb{Z}^\mu\).

The Intersection Form

The first homology carries a natural intersection form:

\[\begin{split}\langle \delta_i, \delta_j \rangle = \begin{cases} -2 & \text{if } i = j \\ 0 \text{ or } 1 & \text{if } i \neq j \end{cases}\end{split}\]

Defining \(C_{ij} = -\langle \delta_i, \delta_j \rangle\) gives:

  • \(C_{ii} = 2\) for all \(i\)

  • \(C_{ij} = 0\) or \(-1\) for \(i \neq j\)

This is exactly a Cartan matrix of ADE type. The vanishing cycles that intersect are connected by an edge in the Dynkin diagram.

The Result

Singularity

Normal form

Vanishing cycles

Intersection form

\(A_n\)

\(x^{n+1} + y^2\)

\(n\) cycles in a chain

\(A_n\) Dynkin diagram

\(D_n\)

\(x^2 y + y^{n-1}\)

\(n\) cycles, chain + fork

\(D_n\) Dynkin diagram

\(E_6\)

\(x^3 + y^4\)

6 cycles

\(E_6\) Dynkin diagram

\(E_7\)

\(x^3 + xy^3\)

7 cycles

\(E_7\) Dynkin diagram

\(E_8\)

\(x^3 + y^5\)

8 cycles

\(E_8\) Dynkin diagram

The monodromy acts as a Coxeter element of the Weyl group – a product of all simple reflections \(s_1 s_2 \cdots s_n\). These are exactly the mutation matrices from the mutation game.

5. Worked Example: A3 in Detail

We walk through the full analysis of \(f(x, y) = x^4 + y^2\) step by step.

Step 1: Verify isolated singularity.

>>> from sympy import symbols, diff, solve
>>> x, y = symbols('x y')  # define symbolic variables

>>> f = x**4 + y**2  # the A3 singularity (in two variables)

>>> # compute partial derivatives
>>> diff(f, x), diff(f, y)
(4*x**3, 2*y)

>>> # find critical points: where both partials vanish
>>> solve([diff(f, x), diff(f, y)], [x, y])
[(0, 0)]

The only critical point is the origin.

Step 2: Compute invariants.

>>> from sympy import symbols
>>> from mutation_game import milnor_number, corank, classify
>>> x, y = symbols('x y')
>>> f = x**4 + y**2

>>> milnor_number(f, (x, y))  # dimension of the local algebra O/J(f)
3
>>> corank(f, (x, y))         # number of zero eigenvalues of the Hessian
1
>>> classify(f, (x, y))       # determine ADE type from mu and corank
'A3'

The Milnor number is 3 (the singularity splits into 3 Morse points), the corank is 1 (one degenerate direction), and the type is \(A_3\).

Step 3: Inspect the local algebra.

>>> # The Jacobian ideal J(f) = <4x^3, 2y>
>>> # Monomials not in the leading term ideal: {1, x, x^2}
>>> # Therefore dim O/(J(f)) = 3 = mu

The local algebra \(\mathcal{O}_2 / \langle x^3, y \rangle\) has basis \(\{1, x, x^2\}\), confirming \(\mu = 3\).

Step 4: Morsification – perturb to split critical points.

>>> from sympy import symbols, diff, Rational, nsolve
>>> x, y = symbols('x y')

>>> # perturb f = x^4 + y^2 by adding lower-order terms
>>> ft = x**4 - Rational(3, 4)*x**2 - Rational(1, 10)*x + y**2
>>> dft_dx = diff(ft, x)  # partial derivative of the perturbed function
>>> dft_dx
4*x**3 - 3*x/2 - 1/10

>>> # find the 3 critical points numerically (setting y = 0)
>>> for guess in [-1.0, 0.1, 0.8]:
...     xc = nsolve(dft_dx, x, guess)          # numerical root near guess
...     vc = ft.subs([(x, xc), (y, 0)])         # critical value f(xc, 0)
...     print(f"  x = {float(xc):.4f}, f = {float(vc):.4f}")
  x = -0.5758, f = -0.0812
  x = -0.0675, f = 0.0034
  x = 0.6433, f = -0.2034

The singularity has split into 3 non-degenerate critical points, arranged along the \(x\)-axis. Each contributes a vanishing cycle.

Step 5: The intersection pattern.

The 3 critical points are ordered on the real line: \(x_1 < x_2 < x_3\). Adjacent vanishing cycles intersect once, non-adjacent ones are disjoint:

  • \(\langle \delta_1, \delta_2 \rangle = 1\)

  • \(\langle \delta_2, \delta_3 \rangle = 1\)

  • \(\langle \delta_1, \delta_3 \rangle = 0\)

The intersection graph is a path \(\delta_1 - \delta_2 - \delta_3\), which is the \(A_3\) Dynkin diagram.

Step 6: Connect to the mutation game.

>>> from mutation_game import MutationGame

>>> # create the A3 mutation game from the Dynkin diagram
>>> game = MutationGame.from_dynkin("A3")

>>> # the Cartan matrix = negative of the intersection form
>>> print(game.cartan_matrix())
[[ 2 -1  0]
 [-1  2 -1]
 [ 0 -1  2]]

This is exactly the negative of the intersection matrix: \(C_{ij} = -\langle \delta_i, \delta_j \rangle\).

>>> # enumerate all roots by BFS over mutation sequences
>>> roots = game.calculate_roots()
>>> pos = [r for r in roots if all(v >= 0 for v in r)]  # positive roots only
>>> print(f"{len(pos)} positive roots, {len(roots)} total")
6 positive roots, 12 total

The monodromy of the singularity is the Coxeter element \(M_0 \cdot M_1 \cdot M_2\):

>>> import numpy as np

>>> # the Coxeter element = product of all mutation matrices
>>> M = game.mutation_matrix(0) @ game.mutation_matrix(1) @ game.mutation_matrix(2)
>>> print(M)
[[ 0  0 -1]
 [ 1  0 -1]
 [ 0  1 -1]]

The general case

For \(A_n\), take \(f(x, y) = x^{n+1} + y^2\). The Morsification

\[f_t(x, y) = x^{n+1} - t_1 x^{n-1} - t_2 x^{n-2} - \cdots - t_n x + y^2\]

splits the singularity into \(n\) Morse points on the \(x\)-axis. The vanishing cycles form a chain, giving the \(A_n\) path graph.

6. Worked Example: D4 in Detail

Step 1: Verify singularity and compute invariants.

>>> from sympy import symbols, solve, diff, hessian
>>> from mutation_game import milnor_number, corank, classify
>>> x, y = symbols('x y')  # define symbolic variables

>>> f = x**2 * y + y**3  # the D4 singularity

>>> # find critical points
>>> solve([diff(f, x), diff(f, y)], [x, y])
[(0, 0)]

>>> milnor_number(f, (x, y))  # dimension of local algebra
4
>>> corank(f, (x, y))         # both Hessian eigenvalues are zero
2
>>> classify(f, (x, y))       # mu=4, corank=2 -> D4
'D4'

Step 2: Inspect the Hessian.

>>> # compute the Hessian matrix of second derivatives
>>> H = hessian(f, [x, y])

>>> # evaluate at the origin: all entries are zero (corank = 2)
>>> H.subs([(x, 0), (y, 0)])
Matrix([[0, 0], [0, 0]])

The \(D_4\) singularity is distinguished from \(A_3\) by its corank. Its 3-jet \(x^2 y + y^3 = y(x^2 + y^2)\) has a repeated factor in \(y\), unlike the \(E\)-types whose 3-jet is a perfect cube.

Step 3: Morsification.

>>> from sympy import symbols, diff, solve, Rational, im
>>> x, y = symbols('x y')

>>> # perturb to split the singularity into 4 Morse points
>>> ft = x**2*y + y**3 - Rational(1, 5)*y

>>> # solve for critical points: set both partials to zero
>>> sys = [diff(ft, x), diff(ft, y)]
>>> crits = solve(sys, [x, y])

>>> # filter real solutions and print their positions and critical values
>>> for c in crits:
...     if im(c[0]) == 0 and im(c[1]) == 0:
...         v = ft.subs([(x, c[0]), (y, c[1])])  # critical value
...         print(f"  ({float(c[0]):.4f}, {float(c[1]):.4f}), f = {float(v):.4f}")
  (0.0000, -0.2582), f = 0.0344
  (0.0000, 0.2582), f = -0.0344
  (-0.3651, -0.1291), f = 0.0172
  (0.3651, -0.1291), f = 0.0172

Four critical points (matching \(\mu = 4\)). Note the fork structure: the critical points are no longer collinear. Two of them share the same critical value, reflecting the fork in the \(D_4\) diagram.

Step 4: Connect to the mutation game.

>>> from mutation_game import MutationGame

>>> # create D4 from its Dynkin diagram name
>>> game = MutationGame.from_dynkin("D4")

>>> # adjacency matrix: node 1 is the hub connecting to 0, 2, and 3
>>> print(game.adj)
[[0 1 0 0]
 [1 0 1 1]
 [0 1 0 0]
 [0 1 0 0]]

>>> # compute the full root system
>>> roots = game.calculate_roots()
>>> pos = [r for r in roots if all(v >= 0 for v in r)]  # positive roots
>>> print(f"{len(pos)} positive roots, {len(roots)} total")
12 positive roots, 24 total

The general case

For \(D_n\) (\(n \geq 4\)), \(f = x^2 y + y^{n-1}\) has \(\mu = n\). After Morsification, the \(n\) vanishing cycles arrange as a chain of \(n-2\) with two cycles forking off the end – the \(D_n\) Dynkin diagram.

7. The E-type Singularities

The exceptional cases:

>>> from sympy import symbols
>>> from mutation_game import milnor_number, corank, classify
>>> x, y = symbols('x y')

>>> # classify all three exceptional singularities
>>> for name, f_expr in [('E6', x**3 + y**4),
...                       ('E7', x**3 + x*y**3),
...                       ('E8', x**3 + y**5)]:
...     mu = milnor_number(f_expr, (x, y))   # local algebra dimension
...     cr = corank(f_expr, (x, y))           # Hessian corank
...     tp = classify(f_expr, (x, y))         # ADE type
...     print(f"  {name}: f = {f_expr}, mu = {mu}, corank = {cr}, type = {tp}")
  E6: f = x**3 + y**4, mu = 6, corank = 2, type = E6
  E7: f = x**3 + x*y**3, mu = 7, corank = 2, type = E7
  E8: f = x**3 + y**5, mu = 8, corank = 2, type = E8

All three have corank 2. They are distinguished from the \(D\)-types by their 3-jet: the \(E\)-types have a pure cube \(x^3\) in their lowest-order terms, while \(D\)-types have a reducible cubic \(x^2 y\).

  • E6: 6 vanishing cycles in the \(E_6\) pattern (chain of 5 with one branch off the third node)

  • E7: 7 vanishing cycles in the \(E_7\) pattern

  • E8: 8 vanishing cycles in the \(E_8\) pattern – the most complex simple singularity

8. Thom’s Seven Catastrophes

Thom’s catastrophe theory studies how families of functions depend on control parameters. The codimension is the number of parameters in the universal unfolding.

Type

Normal form

Codimension

Universal unfolding

\(A_2\)

\(x^3\)

1

\(x^3 + ax\)

\(A_3\)

\(x^4\)

2

\(x^4 + ax^2 + bx\)

\(A_4\)

\(x^5\)

3

\(x^5 + ax^3 + bx^2 + cx\)

\(A_5\)

\(x^6\)

4

\(x^6 + ax^4 + bx^3 + cx^2 + dx\)

\(D_4^-\)

\(x^3 - xy^2\)

3

\(x^3 - xy^2 + a(x^2+y^2) + bx + cy\)

\(D_4^+\)

\(x^3 + xy^2\)

3

\(x^3 + xy^2 + a(x^2+y^2) + bx + cy\)

\(D_5\)

\(x^2y + y^4\)

4

\(x^2y + y^4 + ay^2 + bx^2 + cy + dx\)

In any generic family with at most 4 control parameters, only these seven types appear. Their traditional names:

ADE type

Catastrophe name

Codimension

Geometry

\(A_2\)

Fold

1

Two sheets merging

\(A_3\)

Cusp

2

Cusp of a caustic

\(A_4\)

Swallowtail

3

Self-intersecting surface

\(A_5\)

Butterfly

4

Butterfly-shaped bifurcation set

\(D_4^-\)

Hyperbolic umbilic

3

Wave crests on shallow water

\(D_4^+\)

Elliptic umbilic

3

Light focusing patterns

\(D_5\)

Parabolic umbilic

4

Transition between hyperbolic and elliptic

The remaining ADE types have codimension \(\geq 5\) and require special tuning to produce. This is why Thom stopped at seven.

9. Summary: The Chain of Connections

\[\boxed{\text{Simple singularity}} \xrightarrow{\text{Morsification}} \boxed{\text{Vanishing cycles}} \xrightarrow{\text{Intersection form}} \boxed{\text{Cartan matrix}} \xleftarrow{\text{Mutation game}} \boxed{\text{Dynkin diagram}}\]
  1. Start with a degenerate critical point of a smooth function [AGV1985]

  2. Perturb it to split into \(\mu\) non-degenerate critical points [Milnor1968]

  3. Each critical point contributes a vanishing cycle in the Milnor fiber

  4. The intersection numbers of vanishing cycles give the Cartan matrix [Brieskorn1968]

  5. The Cartan matrix is the same one from the mutation game on the Dynkin diagram [Slodowy1980]

  6. The monodromy = product of Picard–Lefschetz reflections = Coxeter element

For the role of catastrophe theory in this picture, see [Thom1975].

References

[AGV1985]

V. I. Arnold, S. M. Gusein-Zade, A. N. Varchenko, Singularities of Differentiable Maps, Vol. I, Birkhauser, 1985.

[Thom1975]

R. Thom, Structural Stability and Morphogenesis, Benjamin, 1975.

[Milnor1968]

J. W. Milnor, Singular Points of Complex Hypersurfaces, Annals of Mathematics Studies 61, Princeton, 1968.

[Brieskorn1968]

E. Brieskorn, “Die Auflosung der rationalen Singularitaten holomorpher Abbildungen,” Math. Annalen 178 (1968), 255–270.

[Slodowy1980]

P. Slodowy, Simple Singularities and Simple Algebraic Groups, Lecture Notes in Mathematics 815, Springer, 1980.