{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "rocky-semester", "metadata": { "scrolled": false }, "outputs": [], "source": [ "using Oscar" ] }, { "cell_type": "markdown", "id": "single-afghanistan", "metadata": {}, "source": [ "# Worksheet 4: Deformations of IHS, ICIS, and beyond" ] }, { "cell_type": "markdown", "id": "animal-sight", "metadata": {}, "source": [ "## Versal deformations of isolated hypersurface singularities\n", "\n", "We can consider an isolated hypersurface singularity $f \\colon (\\mathbb C^n, 0) \\to (\\mathbb C,0)$ \n", "up to right- ($\\mathcal R$) or contact-equivalence ($\\mathcal K)$. As already discussed \n", "in the previous lectures, we can write down a semi universal unfolding/deformation \n", "by choosing a monomial basis for the Milnor- or the Tjurina algebra, respectively. \n", "\n", "Let us do this for the $A_3$-hypersurface singularity $f = x^4 - y\\cdot z$. " ] }, { "cell_type": "code", "execution_count": null, "id": "infinite-animation", "metadata": {}, "outputs": [], "source": [ "R, (x,y,z) = QQ[\"x\", \"y\", \"z\"]\n", "f = x^4 - y*z\n", "J = ideal(R, minors(jacobi_matrix(f), 1))\n", "\n", "# Let us first manually gather a monomial basis for the Milnor algebra\n", "o = negdegrevlex(gens(R)) # a local ordering on the variables of R\n", "leadJ = leading_ideal(J, ordering=o)" ] }, { "cell_type": "markdown", "id": "younger-finding", "metadata": {}, "source": [ "Now we can manually gather those monomials in the monomial diagram which are not in the leading ideal \n", "of the jacobian ideal. In this case, we eventually find $1, x, x^2$.\n", "\n", "To automatize this, let us write a function." ] }, { "cell_type": "code", "execution_count": null, "id": "heated-rental", "metadata": {}, "outputs": [], "source": [ "function kbase_local(I::MPolyIdeal)\n", " R = base_ring(I)\n", " \n", " o = negdegrevlex(gens(R))\n", " leadI = leading_ideal(I, ordering=o)\n", " \n", " mon_base = elem_type(R)[]\n", " d = 0\n", " new_mons = [x for x in Oscar.all_monomials(R, d) if !(x in leadI)]\n", " while length(new_mons) > 0 \n", " mon_base = vcat(mon_base, new_mons)\n", " d = d+1\n", " new_mons = [x for x in Oscar.all_monomials(R, d) if !(x in leadI)]\n", " end\n", " return mon_base\n", "end" ] }, { "cell_type": "markdown", "id": "medieval-billion", "metadata": {}, "source": [ "Does it reproduce the same result? Let's check." ] }, { "cell_type": "code", "execution_count": null, "id": "equivalent-preparation", "metadata": {}, "outputs": [], "source": [ "kbase_local(J)" ] }, { "cell_type": "markdown", "id": "robust-richmond", "metadata": {}, "source": [ "We can now write down a semi-universal unfolding of $f$. First, we need to introduce new \n", "variables, the deformation parameters." ] }, { "cell_type": "code", "execution_count": null, "id": "honey-coating", "metadata": {}, "outputs": [], "source": [ "function semi_universal_unfolding_local(f::MPolyElem)\n", " R = parent(f)\n", " J = ideal(R, minors(jacobi_matrix(f), 1))\n", " mon_base = kbase_local(J)\n", " mu = length(mon_base) # The Milnor number\n", " n = ngens(R)\n", " \n", " # set up the new ring with the deformation parameters.\n", " Rext, v = PolynomialRing(coefficient_ring(R), vcat(symbols(R), [Symbol(\"u$i\") for i in 1:mu]))\n", " u = gens(Rext)[n+1:end]\n", " \n", " # set up the usual map R → Rext\n", " inc = hom(R, Rext, gens(Rext)[1:n])\n", " F = inc(f)\n", " F = F + sum([t*inc(x) for (t, x) in zip(u, mon_base)])\n", " return (F, gens(Rext)[1:n], gens(Rext)[n+1:end])\n", "end" ] }, { "cell_type": "markdown", "id": "multiple-being", "metadata": {}, "source": [ "Let us check that it gives the expected result." ] }, { "cell_type": "code", "execution_count": null, "id": "cardiac-pierce", "metadata": {}, "outputs": [], "source": [ "F, X, U = semi_universal_unfolding_local(f)" ] }, { "cell_type": "markdown", "id": "anonymous-kentucky", "metadata": {}, "source": [ "## The discriminant of an IHS" ] }, { "cell_type": "markdown", "id": "olive-helping", "metadata": {}, "source": [ "Let $F(x; t) \\colon (\\mathbb C^n, 0) \\times (\\mathbb C^\\mu,0) \\to (\\mathbb C,0)$ be the semi-universal unfolding \n", "of an isolated hypersurface singularity $f = f_0 = F(x; 0)$. The *discriminant* is defined as the set of \n", "parameters $t \\in \\mathbb C^\\mu$ for which the fiber $X_t = f^{-1}(\\{0\\})$ is singular. Can we compute that locus?\n", "\n", "First, we need a method to compute the \"relative Jacobian matrix\" where we take the derivatives only with respect to the variables, not the parameters. We can pass the variables as a second argument and simply add that method \n", "to the existing `jacobi_matrix` function. \n", "\n", "Note that julia distinguishes between different *methods* of the same \n", "*function* according to the *signature*, i.e. the number of arguments and their types." ] }, { "cell_type": "code", "execution_count": null, "id": "posted-skiing", "metadata": {}, "outputs": [], "source": [ "function Oscar.jacobi_matrix(f::MPolyElem, x::Vector{T}) where {T<:MPolyElem}\n", " R = parent(f)\n", " all(x->(x in gens(R)), x) || error(\"The given elements are not all ring variables\")\n", " n = length(x)\n", " A = zero(MatrixSpace(R, n, 1))\n", " for i in 1:n\n", " A[i, 1] = derivative(f, x[i])\n", " end\n", " return A\n", "end" ] }, { "cell_type": "markdown", "id": "professional-pacific", "metadata": {}, "source": [ "Having the above method, we can manually compute the equation for the discriminant $\\Delta$ using elimination \n", "orderings for the projection to the parameter space, cf. yesterday's lectures." ] }, { "cell_type": "code", "execution_count": null, "id": "narrow-windsor", "metadata": {}, "outputs": [], "source": [ "RU = parent(F) # The ring of the ambient space of the deformation \n", "JF = ideal(RU, minors(jacobi_matrix(F, X), 1)) + ideal(RU, F) # The relative Tjurina ideal \n", "\n", "# We need an elemination ordering for the projection to the deformation base\n", "o = degrevlex(gens(RU)[1:3])*negdegrevlex(gens(RU)[4:5])\n", "\n", "# compute a standard basis w.r.t this elimination ordering\n", "SB = std_basis(JF, o)\n", "\n", "# collect only those elements which are contained in the subring of the base\n", "disc_eqns = [f for f in SB if all(v->!(divides(leading_monomial(f, o), v))[1], gens(RU)[1:3])]" ] }, { "cell_type": "markdown", "id": "dirty-arabic", "metadata": {}, "source": [ "The specialists among us may recognize that this is indeed the formula for the discriminant \n", "of a (depressed) polynomial of degree $4$. It is also known as the \"swallow tail\", a famous \n", "singularity. \n", "\n", "We can investigate the singular locus of $\\Delta$. " ] }, { "cell_type": "code", "execution_count": null, "id": "amino-journal", "metadata": {}, "outputs": [], "source": [ "dec = primary_decomposition(singular_slocus(ideal(RU, disc_eqns)))" ] }, { "cell_type": "markdown", "id": "beautiful-paintball", "metadata": {}, "source": [ "What a mess! But luckily, we already picked out some special points on $\\Delta$ for you; \n", "namely \n", "\n", " * $t_1 = (0,0,-1)$ on the regular locus of $\\Delta$; \n", " * $t_2 = (-3, 8, -6)$ on the second component above; \n", " * $t_3 = (1, 0, -2)$ on the first component. \n", " \n", "Let's check that these points really lay on them. " ] }, { "cell_type": "code", "execution_count": null, "id": "secret-pocket", "metadata": {}, "outputs": [], "source": [ "delta = disc_eqns[1]\n", "t1 = [0,0,0, 0,0, -1]\n", "@show evaluate(delta, t1)\n", "@show all(g->iszero(evaluate(g, t1)), gens(dec[2][1]))\n", "@show all(g->iszero(evaluate(g, t1)), gens(dec[1][1]))" ] }, { "cell_type": "code", "execution_count": null, "id": "bronze-precipitation", "metadata": {}, "outputs": [], "source": [ "t2 = [0,0,0, -3, 8, -6]\n", "@show evaluate(delta, t2)\n", "@show all(g->iszero(evaluate(g, t2)), gens(dec[2][1]))\n", "@show all(g->iszero(evaluate(g, t2)), gens(dec[1][1]))" ] }, { "cell_type": "code", "execution_count": null, "id": "assumed-stroke", "metadata": {}, "outputs": [], "source": [ "t3 = [0,0,0, 1, 0, -2]\n", "@show evaluate(delta, t3)\n", "@show all(g->iszero(evaluate(g, t3)), gens(dec[2][1]))\n", "@show all(g->iszero(evaluate(g, t3)), gens(dec[1][1]))" ] }, { "cell_type": "markdown", "id": "wrapped-transsexual", "metadata": {}, "source": [ "Let us analyze the singularities in the fibers for either one of the three parameters. First $t_1$:" ] }, { "cell_type": "code", "execution_count": null, "id": "pressed-gates", "metadata": {}, "outputs": [], "source": [ "f1 = evaluate(F, R.([x,y,z, 0, 0, -1]))\n", "radical(singular_slocus(ideal(R, f1)))" ] }, { "cell_type": "markdown", "id": "chicken-profile", "metadata": {}, "source": [ "So $f_1 = F(x,y,z; t_1)$ has an isolated singularity at the origin. Which one is it?" ] }, { "cell_type": "code", "execution_count": null, "id": "equal-diagnosis", "metadata": {}, "outputs": [], "source": [ "classify(f1)" ] }, { "cell_type": "markdown", "id": "combined-gardening", "metadata": {}, "source": [ "In fact, the whole regular stratum of $\\Delta$ is characterized by the fibers having one single $A_1$-singularity.\n", "\n", "Let us repeat the same with $t_2$." ] }, { "cell_type": "code", "execution_count": null, "id": "strange-georgia", "metadata": {}, "outputs": [], "source": [ "f2 = evaluate(F, R.([x,y,z, -3, 8, -6]))\n", "radical(singular_slocus(ideal(R, f2)))" ] }, { "cell_type": "markdown", "id": "acceptable-maximum", "metadata": {}, "source": [ "The `classify` command can only analyze singularities at the origin (because it is using local orderings in the background) and, hence, we first have to shift our function." ] }, { "cell_type": "code", "execution_count": null, "id": "naval-attendance", "metadata": {}, "outputs": [], "source": [ "shift = hom(R, R, [x+1, y, z])\n", "classify(shift(f2))" ] }, { "cell_type": "markdown", "id": "criminal-tucson", "metadata": {}, "source": [ "So we see that on this stratum of $\\Delta$ the fibers have precisely one $A_2$ singularity." ] }, { "cell_type": "code", "execution_count": null, "id": "large-tracy", "metadata": {}, "outputs": [], "source": [ "f3 = evaluate(F, R.([x,y,z, 1, 0, -2]))\n", "radical(singular_slocus(ideal(R, f3)))" ] }, { "cell_type": "markdown", "id": "resistant-skill", "metadata": {}, "source": [ "Here, we have two singularities and therefore need two shifts." ] }, { "cell_type": "code", "execution_count": null, "id": "split-reynolds", "metadata": {}, "outputs": [], "source": [ "shift1 = hom(R, R, [x+1, y, z])\n", "classify(shift1(f3))" ] }, { "cell_type": "code", "execution_count": null, "id": "defensive-southeast", "metadata": {}, "outputs": [], "source": [ "shift2 = hom(R, R, [x-1, y, z])\n", "classify(shift2(f3))" ] }, { "cell_type": "markdown", "id": "handled-daisy", "metadata": {}, "source": [ "We conclude that on the third stratum, the fibers have two singularities of type $A_1$." ] }, { "cell_type": "markdown", "id": "minute-cookie", "metadata": {}, "source": [ "## Deformations of complete intersections\n", "\n", "We use the $S_5$-singularity from before." ] }, { "cell_type": "code", "execution_count": null, "id": "handed-headset", "metadata": {}, "outputs": [], "source": [ "R, (x,y,z) = QQ[\"x\", \"y\", \"z\"]\n", "I = ideal(R, [x*y, x^2 + y^2 + z^2])" ] }, { "cell_type": "markdown", "id": "every-lodge", "metadata": {}, "source": [ "The normal module is defined as $N = \\mathrm{Hom}_R(I, R/I)$. OSCAR knows how to handle `Hom`-modules, \n", "but we need to convert all the participants into modules, first." ] }, { "cell_type": "code", "execution_count": null, "id": "strategic-worth", "metadata": {}, "outputs": [], "source": [ "F1 = FreeMod(R, 1) # A free module over R of rank 1\n", "Imod, _ = sub(F1, [g*F1[1] for g in gens(I)]) # the submodule of F1 generated by I\n", "RmodI, _ = quo(F1, Imod) # the quotient by that submodule\n", "N, vec_to_hom = hom(Imod, RmodI)" ] }, { "cell_type": "markdown", "id": "threaded-brook", "metadata": {}, "source": [ "The last command is a bit subtle. It returns a pair `(N, vec_to_hom)` consisting of the actual \n", "module `N` and a map `vec_to_hom`. While often we can discard any extra maps returned by \n", "constructors, we will need this one in the following. \n", "\n", "What does `vec_to_hom` do? Well, `N` is constructed as a `Hom`-module. A priori, this is just another \n", "finitely generated module, but also every element $v \\in N$ can be interpreted as a module homomorphism \n", "$ I \\to R/I$. The returned map `vec_to_hom` allows us to do that interpretation on the computer side \n", "so that we really get an honest morphism of modules." ] }, { "cell_type": "code", "execution_count": null, "id": "residential-symphony", "metadata": {}, "outputs": [], "source": [ "v = N[1] # the first generator of N\n", "typeof(v)" ] }, { "cell_type": "markdown", "id": "progressive-announcement", "metadata": {}, "source": [ "We can try to apply `v` to an element of `Imod` directly, but this will fail." ] }, { "cell_type": "code", "execution_count": null, "id": "assigned-intensity", "metadata": {}, "outputs": [], "source": [ "g = Imod[2] # the second generator of I\n", "v(g) # This will complain!" ] }, { "cell_type": "markdown", "id": "conditional-death", "metadata": {}, "source": [ "Converting `v` into a homomorphism makes everything work." ] }, { "cell_type": "code", "execution_count": null, "id": "authorized-momentum", "metadata": {}, "outputs": [], "source": [ "phi = vec_to_hom(v)\n", "@show phi\n", "phi(Imod[2])" ] }, { "cell_type": "markdown", "id": "hungry-survival", "metadata": {}, "source": [ "We proceed with the construction of the $T^1(S_5)$. We need to divide by the image of the jacobian matrix." ] }, { "cell_type": "code", "execution_count": null, "id": "involved-commissioner", "metadata": {}, "outputs": [], "source": [ "f = gens(I)\n", "Df = jacobi_matrix(f)" ] }, { "cell_type": "markdown", "id": "executed-victor", "metadata": {}, "source": [ "Now we need the complicated map \n", "$\\theta \\colon R^n \\to N, \\quad (v_1,\\dots,v_n) \\mapsto \\left( \\sum_j a_j \\cdot f_j \\mapsto \\sum_{i,j} v_i \\frac{\\partial f_j}{\\partial x_i} a_j \\right)$. \n", "For this we need the `vec_to_hom` map from before and take *preimages* of elements along this map. \n", "Eventually, we arrive at the following, complicated expression." ] }, { "cell_type": "code", "execution_count": null, "id": "professional-toddler", "metadata": {}, "outputs": [], "source": [ "Fn = FreeMod(R, ngens(R))\n", "theta = hom(Fn, N, [preimage(vec_to_hom, hom(Imod, RmodI, [Df[i,j]*RmodI[1] for j in 1:ngens(Imod)])) for i in 1:ngens(R)])\n", "S, _ = image(theta)\n", "T1, _ = quo(N, S)\n", "kbase(T1)" ] }, { "cell_type": "markdown", "id": "refined-kingston", "metadata": {}, "source": [ "## Versal deformations of arbitrary isolated singularities\n", "\n", "Singular (and hence Oscar) knows how to compute the semi-universal deformation of an arbitrary \n", "singularity according to Grauert's theorem. Let us demonstrate this in Pinkham's example (cf. the \n", "first lecture on determinantal singularities by Prof. Maria Ruas)." ] }, { "cell_type": "code", "execution_count": null, "id": "tough-banana", "metadata": {}, "outputs": [], "source": [ "R, x = PolynomialRing(QQ, \"x\" => 1:5)\n", "M1 = R[x[1] x[2] x[3] x[4]; x[2] x[3] x[4] x[5]]" ] }, { "cell_type": "code", "execution_count": null, "id": "referenced-bible", "metadata": {}, "outputs": [], "source": [ "I = ideal(R, minors(M1, 2))\n", "inc, p = versal_unfolding(I)" ] }, { "cell_type": "markdown", "id": "impossible-favorite", "metadata": {}, "source": [ "Apart from the verbose Singular output during the computation, this returns us a pair of two maps: the inclusion of \n", "the central fiber `inc` into the family and the projection `p` down to the parameter space. \n", "The domains and codomains of these maps are 'space germs'." ] }, { "cell_type": "code", "execution_count": null, "id": "hispanic-interstate", "metadata": {}, "outputs": [], "source": [ "domain(inc)" ] }, { "cell_type": "code", "execution_count": null, "id": "tired-controversy", "metadata": {}, "outputs": [], "source": [ "domain(p)" ] }, { "cell_type": "code", "execution_count": null, "id": "impressed-satisfaction", "metadata": {}, "outputs": [], "source": [ "codomain(p)" ] }, { "cell_type": "code", "execution_count": null, "id": "biblical-lease", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.7.3", "language": "julia", "name": "julia-1.7" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.7.3" } }, "nbformat": 4, "nbformat_minor": 5 }