{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "statistical-approach", "metadata": {}, "outputs": [], "source": [ "using Revise, Oscar" ] }, { "cell_type": "markdown", "id": "refined-reporter", "metadata": {}, "source": [ "# Worksheet 2: Dimension, multiplicities, etc.\n", "\n", "## Dimension: Local and global\n", "\n", "The definition of (Krull-) dimension in Noetherian rings is quite unaccessible in general. \n", "For local rings, the most practical method to compute dimensions is via the \n", "Hilbert-Samuel polynomial. " ] }, { "cell_type": "code", "execution_count": null, "id": "limiting-english", "metadata": {}, "outputs": [], "source": [ "R, (x,y,z,w) = QQ[\"x\", \"y\", \"z\", \"w\"]\n", "M = R[x y z*(z-1); y z w]\n", "I = ideal(R, minors(M, 2))\n", "I" ] }, { "cell_type": "markdown", "id": "potential-patio", "metadata": {}, "source": [ "With the following command we create a *local* ordering on the variables of $R$." ] }, { "cell_type": "code", "execution_count": null, "id": "compact-building", "metadata": {}, "outputs": [], "source": [ "o = negdegrevlex(gens(R))" ] }, { "cell_type": "markdown", "id": "departmental-month", "metadata": {}, "source": [ "We can use this to produce a *standard basis* of the ideal $I$ for this ordering. " ] }, { "cell_type": "code", "execution_count": null, "id": "animated-defensive", "metadata": {}, "outputs": [], "source": [ "std_I = std_basis(I, o)" ] }, { "cell_type": "markdown", "id": "previous-library", "metadata": {}, "source": [ "For a given ordering, we can also ask for the *leading ideal* of $I$ which is simply the ideal generated \n", "by the leading terms of the elements of the standard basis." ] }, { "cell_type": "code", "execution_count": null, "id": "imposed-print", "metadata": {}, "outputs": [], "source": [ "lead_I = leading_ideal(I, ordering=o)" ] }, { "cell_type": "markdown", "id": "allied-penny", "metadata": {}, "source": [ "By construction, the leading ideal is a *homogeneous* ideal for this ordering. We can pass to the graded quotient ring for this ideal and compute the Hilbert polynomial." ] }, { "cell_type": "code", "execution_count": null, "id": "brutal-violation", "metadata": {}, "outputs": [], "source": [ "Rgr, _ = grade(R) # The same ring as R, but with the standard grading \n", "lead_Igr = ideal(Rgr, Rgr.(gens(lead_I))) # The graded version of the ideal lead_I\n", "Q, _ = quo(Rgr, lead_Igr) # The (graded) quotient ring by that ideal\n", "h = hilbert_polynomial(Q)" ] }, { "cell_type": "markdown", "id": "documentary-series", "metadata": {}, "source": [ "We must remark that here we computed the Hilbert *polynomial* $P(t)$ of $A := R_{\\mathrm gr}/ I_{\\mathrm gr}$. It is the unique polynomial \n", "such that for $k \\gg 1$, $k \\in \\mathbb N$ one has $P(k) = \\dim_{\\mathbb C} A_k$ with $A_k$ the $k$-th \n", "graded piece of the algebra $A$. \n", "\n", "The Hilbert-Samuel polynomial, on the other hand, is the unique polynomial $Q(t)$ such that for $k \\gg 1$, $k \\in \\mathbb N$ one has $Q(k) = \\dim_{\\mathbb C} A / \\mathfrak m^k$. \n", "\n", "Since $A / \\mathfrak m^k \\cong A_0 \\oplus A_1 \\oplus \\dots \\oplus A_{k-2} \\oplus A_{k-1}$ as vector spaces, \n", "it is easy to see that $Q(k) = P(k-1) - P(k-2)$ and $P(k) = \\sum_{i=0}^{k-1} P(i)$." ] }, { "cell_type": "markdown", "id": "severe-following", "metadata": {}, "source": [ "Note that the Hilbert polynomial comes with a new variable in a new ring." ] }, { "cell_type": "code", "execution_count": null, "id": "reverse-receiver", "metadata": {}, "outputs": [], "source": [ "parent(h)" ] }, { "cell_type": "markdown", "id": "organic-assets", "metadata": {}, "source": [ "Also the Hilbert series is available " ] }, { "cell_type": "code", "execution_count": null, "id": "different-thong", "metadata": {}, "outputs": [], "source": [ "p, q = hilbert_series(Q)" ] }, { "cell_type": "markdown", "id": "grand-diamond", "metadata": {}, "source": [ "Accessing the coefficients of the Hilbert polynomial is rather tedious. The reason is that \n", "the `coefficients` command returns an `iterator`; a common behaviour in `julia`. " ] }, { "cell_type": "code", "execution_count": null, "id": "female-leeds", "metadata": {}, "outputs": [], "source": [ "coefficients(h)" ] }, { "cell_type": "markdown", "id": "incomplete-mumbai", "metadata": {}, "source": [ "How can we use such iterators? Well, we can, for example, iterate over them in `for`-loops." ] }, { "cell_type": "code", "execution_count": null, "id": "provincial-macintosh", "metadata": {}, "outputs": [], "source": [ "for c in coefficients(h)\n", " println(c)\n", "end" ] }, { "cell_type": "markdown", "id": "eleven-cowboy", "metadata": {}, "source": [ "If we do not care about such features and only want everything in a list, we can just feed the iterator to `collect`." ] }, { "cell_type": "code", "execution_count": null, "id": "laden-plumbing", "metadata": {}, "outputs": [], "source": [ "c = collect(coefficients(h))" ] }, { "cell_type": "markdown", "id": "grave-eleven", "metadata": {}, "source": [ "Or just take any other shortcut to the information of interest:" ] }, { "cell_type": "code", "execution_count": null, "id": "negative-quantity", "metadata": {}, "outputs": [], "source": [ "last(coefficients(h))" ] }, { "cell_type": "markdown", "id": "comparable-imaging", "metadata": {}, "source": [ "## The Milnor number\n", "\n", "Let us compute the Milnor number for one of the previous examples" ] }, { "cell_type": "code", "execution_count": null, "id": "challenging-burst", "metadata": {}, "outputs": [], "source": [ "R, (x,y) = QQ[\"x\", \"y\"]\n", "f = x*y*(x+y-1)" ] }, { "cell_type": "markdown", "id": "wicked-gibraltar", "metadata": {}, "source": [ "We can implement the functionality from the lecture in various functions." ] }, { "cell_type": "code", "execution_count": null, "id": "flush-emperor", "metadata": {}, "outputs": [], "source": [ "function milnor_number(f::MPolyElem)\n", " R = parent(f)\n", " J = ideal(R, minors(jacobi_matrix(f), 1))\n", " return singular_vdim(J)\n", "end" ] }, { "cell_type": "markdown", "id": "forbidden-induction", "metadata": {}, "source": [ "Note that we used a rather involved method as a black box: `singular_vdim`." ] }, { "cell_type": "code", "execution_count": null, "id": "printable-faculty", "metadata": {}, "outputs": [], "source": [ "milnor_number(f)" ] }, { "cell_type": "markdown", "id": "decreased-junior", "metadata": {}, "source": [ "So what is happening there under the hood? \n", "\n", "Basically, `singular_vdim` is counting the monomials 'below' the generators of the leading ideal.\n", "Let's see whether we can manually confirm the result." ] }, { "cell_type": "code", "execution_count": null, "id": "material-bench", "metadata": {}, "outputs": [], "source": [ "R = parent(f)\n", "J = ideal(R, minors(jacobi_matrix(f), 1))\n", "o = degrevlex(gens(R))\n", "leadJ = leading_ideal(J, ordering=o)\n", "singular_vdim(leadJ)" ] }, { "cell_type": "markdown", "id": "forced-linux", "metadata": {}, "source": [ "The above is the *global* milnor number. If we are only interested in singularities \n", "at the origin, then we can repeat the same process with a local ordering." ] }, { "cell_type": "code", "execution_count": null, "id": "ultimate-guarantee", "metadata": {}, "outputs": [], "source": [ "R = parent(f)\n", "J = ideal(R, minors(jacobi_matrix(f), 1))\n", "o = negdegrevlex(gens(R))\n", "leading_ideal(J, ordering=o)" ] }, { "cell_type": "markdown", "id": "express-worship", "metadata": {}, "source": [ "We see that the singularity at the origin has Milnor number $1$ instead of the global Milnor number $4$. \n", "We allowed ourselves to accomodate the above commands in the following function." ] }, { "cell_type": "code", "execution_count": null, "id": "romantic-enzyme", "metadata": {}, "outputs": [], "source": [ "singular_vdim_local(J)" ] }, { "cell_type": "markdown", "id": "sporting-vitamin", "metadata": {}, "source": [ "Similarly, we could do the same at any other rational point $p \\in \\mathbf A^2$, for instance at the other singular points $(0, 1)$ or $(1, 0)$ of $f$. To this end, we have to apply an isomorphism $\\varphi \\colon R \\to R$ that takes $p$ to the origin, translate the ideal along $\\varphi$ and repeat the computations for the local ordering. \n", "Luckily, this has already been wrapped up in the hidden (i.e. non-exported) function `Oscar.shifted_ideal` " ] }, { "cell_type": "code", "execution_count": null, "id": "coordinate-better", "metadata": {}, "outputs": [], "source": [ "L, _ = Localization(R, complement_of_ideal(R, [1, 0]))\n", "J_loc = L(J)" ] }, { "cell_type": "code", "execution_count": null, "id": "conceptual-budget", "metadata": {}, "outputs": [], "source": [ "Df = jacobi_matrix(L(f))\n", "J = ideal(L, minors(Df, 1))\n", "J_shift = Oscar.shifted_ideal(J)" ] }, { "cell_type": "markdown", "id": "anticipated-partition", "metadata": {}, "source": [ "Note that `shifted_ideal` returns an ideal in the original polynomial ring $R$ and not in the localization $L$!" ] }, { "cell_type": "code", "execution_count": null, "id": "three-designation", "metadata": {}, "outputs": [], "source": [ "singular_vdim_local(J_shift)" ] }, { "cell_type": "markdown", "id": "literary-explanation", "metadata": {}, "source": [ "## The Tjurina number\n", "\n", "For any singular germ $(X,0) \\subset (\\mathbf C,0)$ with an isolated singularity, there is the \n", "*Tjurina number* counting the minimal number of parameters in a versal deformation of $(X,0)$. \n", "For a hypersurface $X = f^{-1}(\\{0\\})$, $f \\colon (\\mathbf C^{n+1}, 0) \\to (\\mathbf C,0)$ it defined as the length of the *Tjurina algebra*. Let us see this in an example." ] }, { "cell_type": "code", "execution_count": null, "id": "rough-breast", "metadata": {}, "outputs": [], "source": [ "R, (x,y) = QQ[\"x\", \"y\"]\n", "f0 = x^5 + y^5\n", "J0 = ideal(R, f0) + ideal(R, minors(jacobi_matrix(f0), 1))\n", "singular_vdim_local(J0)\n", "# milnor_number(f0) # run for comparison" ] }, { "cell_type": "markdown", "id": "gothic-bulgarian", "metadata": {}, "source": [ "Note that this is equal to the Milnor number of $f$. This is due to the fact that $f$ is *quasi-homogeneous*. If the function does not have this property, the Milnor and Tjurina number can differ:" ] }, { "cell_type": "code", "execution_count": null, "id": "immediate-scheme", "metadata": {}, "outputs": [], "source": [ "f1 = x^5 + y^5 + x^3*y^3\n", "J1 = ideal(R, f1) + ideal(R, minors(jacobi_matrix(f1), 1))\n", "singular_vdim_local(J1)" ] }, { "cell_type": "code", "execution_count": null, "id": "surrounded-controversy", "metadata": {}, "outputs": [], "source": [ "milnor_number(f1)\n", "# J = ideal(R, minors(jacobi_matrix(f1), 1))\n", "# primary_decomposition(J) # Try this if you want to see what went wrong." ] }, { "cell_type": "markdown", "id": "transparent-triple", "metadata": {}, "source": [ "What a difference! But wait -- did the Milnor number really jump up that much compared to $f_0$? Somehow, it did, but *globally* and not at the origin!" ] }, { "cell_type": "code", "execution_count": null, "id": "exclusive-happening", "metadata": {}, "outputs": [], "source": [ "L, _ = Localization(R, complement_of_ideal(R, [0, 0]))\n", "Oscar.milnor_number(L(f))" ] }, { "cell_type": "markdown", "id": "shaped-reggae", "metadata": {}, "source": [ "Still, we see a difference of $1$ between these two numbers." ] }, { "cell_type": "markdown", "id": "possible-validity", "metadata": {}, "source": [ "## Milnor numbers of ICIS and the Le-Greuel formula\n", "\n", "Recall the discussion of the Le-Greuel formula for ICIS from the lecture. \n", "A necessary ingredient is the construction of *random* hyperplanes. \n", "This can, for example, be done as follows. " ] }, { "cell_type": "code", "execution_count": null, "id": "offensive-effectiveness", "metadata": {}, "outputs": [], "source": [ "[rand(QQ, -100:100) for i in 1:10]" ] }, { "cell_type": "markdown", "id": "unlimited-israeli", "metadata": {}, "source": [ "Let us compute the Milnor number(s) for the $S_5$ singularity." ] }, { "cell_type": "code", "execution_count": null, "id": "monthly-accountability", "metadata": {}, "outputs": [], "source": [ "R, (x,y,z) = QQ[\"x\", \"y\", \"z\"]\n", "I = ideal(R, [x*y, x^2 + y^2 + z^2])\n", "# Let us first check that this is really an ICIS\n", "@show dim(I)\n", "@show radical(singular_slocus(I))" ] }, { "cell_type": "markdown", "id": "heated-miami", "metadata": {}, "source": [ "Next, we need to construct the isolated hypersurface singularity\n", "given by a random linear combination of the two generators." ] }, { "cell_type": "code", "execution_count": null, "id": "lucky-lucas", "metadata": {}, "outputs": [], "source": [ "a = [rand(QQ, -100:100) for i in 1:2]\n", "h = sum([c*g for (c, g) in zip(a, gens(I))])\n", "I1 = ideal(R, h)\n", "issubset(I1, I)" ] }, { "cell_type": "markdown", "id": "protected-battle", "metadata": {}, "source": [ "Let us verify that this is indeed an isolated hypersurface singularity." ] }, { "cell_type": "code", "execution_count": null, "id": "genuine-waste", "metadata": {}, "outputs": [], "source": [ "@show dim(I1)\n", "@show radical(singular_slocus(I1))" ] }, { "cell_type": "markdown", "id": "parliamentary-ethnic", "metadata": {}, "source": [ "If we want to know more about the singularity at the origin, we may ask OSCAR (or more precisely SINGULAR in the background) to classify it." ] }, { "cell_type": "code", "execution_count": null, "id": "integrated-cricket", "metadata": {}, "outputs": [], "source": [ "classify(h)" ] }, { "cell_type": "code", "execution_count": null, "id": "historic-selling", "metadata": {}, "outputs": [], "source": [ "mu1 = milnor_number(h)" ] }, { "cell_type": "markdown", "id": "indian-nitrogen", "metadata": {}, "source": [ "Now comes the truely determinantal part of the formula." ] }, { "cell_type": "code", "execution_count": null, "id": "sorted-airplane", "metadata": {}, "outputs": [], "source": [ "M = jacobi_matrix(gens(I))\n", "J = ideal(R, minors(M, 2)) + I1\n", "mu0 = singular_vdim_local(J)" ] }, { "cell_type": "markdown", "id": "virgin-cambodia", "metadata": {}, "source": [ "Hence, the total Milnor number is $\\mu(S_5) = \\mu_0 - \\mu_1 = 6 - 1 = 5$." ] }, { "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": [ "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)" ] } ], "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 }