forked from Macaulay2/Workshop-2015-Boise
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Loci.m2
1919 lines (1698 loc) · 74.2 KB
/
Loci.m2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- Copyright 2014-2015: James Parson
-- You may redistribute this file under the terms of the GNU General Public
-- License as published by the Free Software Foundation, either version 2
-- of the License, or any later version.
------------------------------------------
------------------------------------------
-- Header
------------------------------------------
------------------------------------------
if version#"VERSION" <= "1.4" then (
)
newPackage select((
"Loci",
Version => "0.0.1",
Date => "27. January 2015",
Authors => {
{Name => "James Parson", Email => "[email protected]"},
{Name => "Gwyn Whieldon", Email => "[email protected]", HomePage => "http://www.hood.edu/Academics/Departments/Mathematics/Faculty/Gwyneth-Whieldon.html"}
},
Headline => "Package for computing open Loci for properties or rings and homomorphisms",
Configuration => {},
DebuggingMode => true,
if version#"VERSION" > "1.4" then PackageExports => {}
), x -> x =!= null)
if version#"VERSION" <= "1.4" then (
)
export {
--
-- methods in package
-- "presentGraph",
-- "extendScalars",
-- "genericFreeness",
-- "absFrob",
-- "inRad",
-- "isOpenSubLocus",
-- "equalOpenLoci",
-- "Omega1",
-- "naiveCotComplex",
-- "LSCotComplex",
-- "relativeFlatLocusINT",
-- "semiContinuity",
-- "isCM",
-- "kunzRegularLocus",
-- "equidimensionalINT",
-- "equidimensional",
-- "genericSaturate",
-- "primeFactors",
-- "regularLocusZZ",
-- "regularLocusFiber",
"flatLocus",
"relativeFlatLocus",
"relativeDimension",
"unramifiedLocus",
"smoothLocus",
"etaleLocus",
"lciLocus",
"CMLocus",
"GorensteinLocus",
"CILocus",
"regularLocus",
"kunzRegularLocus",
"trueCodimension",
"isNormalFixed",
"codimPiece",
"dimPiece",
"isOpenSubLocus",
"equalOpenLoci",
"normalLocus",
"isReduced",
"reducedLocus",
"isS",
"isR"
}
------------------------------------------
------------------------------------------
-- Methods
------------------------------------------
------------------------------------------
-- to do:
-- * catch errors in "presentGraph" : I know what I would like to do, but I am still having trouble
-- because of rings without coefficient rings.
-- * think about types for various methods. Perhaps "Ring" is not the right thing.
-- in the M2 source code, one often find Ring, PolynomialRing, QuotientRing, etc.
-- for a simple example, search source code for "isAffineRing" (in "M2/galois.m2" for some reason)
-- * add something to find reduced locus, normal locus.
-- * could we do something with constructible loci? image---or locus where dim has particular values
-- is constructible, as follows directly from generic freeness.
-- perhaps this should wait for another time, since the other functions are basically done.
------
-- General methods for manipulating rings
-----
presentGraph = method()
-- the error-catching business below does not work when "A" does not have a coefficientRing.
-- I don't know the proper way to handle this situation. Perhaps I should ask about it on the M2
-- mailing list. (But to do that, I will have to overcome my paralyzing fear of setting up a Google
-- account.)
presentGraph(RingMap) := f -> (
-- The function takes a homomorphism f:A-->B as input. The rings A and B must have the same coefficientRing R,
-- and the homomorphism f must be an R-algebra homomorphism.
-- The function returns an A-algebra D, which is B viewed as A-algebra via f, along with isomorphisms ii:B-->D and phi:D-->B.
-- To do: add code checking to make certain that the hypotheses hold.
-- See tensor product code for how to make missing checks on R and f.
-- if B =!= ring M
-- then error "expected target of RingMap to match ring of module";
-- R := coefficientRing B; -- assume that this is common coefficient ring of A and B and that f is an R-algebra homomorphism.
-- if R =!= coefficientRing A
-- then error "expected source and target of RingMap to have same coefficientRing";
A := source f;
B := target f;
R := coefficientRing B;
-- if not ( R === coefficientRing A ) then error "expected source and target of ring map to have same coefficient ring";
-- gensR := generators(R, CoefficientRing => ZZ);
-- if not all(gensR, x -> promote(x,B) == f promote(x,A)) then error "expected ring map to be identity on coefficient ring";
(B1,s) := flattenRing(B, CoefficientRing => R);
C := A monoid ambient B1;
i := map(C, ambient B1, gens C); -- important here to add the "gens C"; to see why try a homomorphism f from A to A such as frobenius
j := map(C,A);
I := i(ideal B1);
II := ideal(apply(gens A, r->j(r) - i(lift(s(f(r)),ambient B1))));
D := C/(I + II);
ii := map(D,B1); -- this homomorphism is an isomorphism, but the coefficientRing of D is A; ** does this work? correct to be like "i" above?
phi := map(B1,D,apply(gens B, r->s(r))|apply(gens A, r->s(f(r)))); -- this homomorphism is the inverse of ii
(D,ii*s,(s^-1)*phi)
)
extendScalars = method()
extendScalars(Ring, RingMap) := (B,phi) -> (
-- input: an A-algebra B and a ring map A->C. Returns C-algebra obtained by extension of scalars and
-- extension of scalars homomorphism j:B-->this C-algebra
A := source phi; -- we assume that A is also the coefficientRing of B; we should check this!
C := target phi;
(B1,r) := flattenRing(B, CoefficientRing => A);
P := ambient B1;
I := ideal B1;
Q := C (monoid P);
i := map(Q, P, gens Q | apply(gens A, t->(phi(t))));
F := Q/i(I);
j := map(F, B1, gens Q | apply (gens A, t->(phi(t))));
(F,j*r)
)
---The next two methods are for flattening rings constructed with fraction fields
---fRing gives back a ring C over ZZ, QQ, or ZZ/p along with a map C->A that is a localization.
---To compute things about A, we may compute things about C and push forward along the localization map.
---This procedure is necessary for computing the regular locus when the coefficientRing of A
---is a field that is not perfect, since then regularity and smoothness over the coefficientRing do
---not coincide.
fRing = method()
fRing(Ring) := A -> (
(B,f) := flattenRing A;
if not instance(coefficientRing B, FractionField) then (B,f^-1)
else (
K := coefficientRing B;
R := last K.baseRings;
P := ambient B;
I := ideal B;
Q := R monoid P;
bad := map(Q,P);
J := ideal(apply(flatten entries gens I, x->bad(clearDenom x))); -- I read somewhere that I_* does the same as "fl ent gens I"
g := map(B, Q/J);
(C,i) := fRing(Q/J);
(C, (f^-1)*g*i)
)
)
clearDenom = method()
clearDenom(RingElement) := x -> (
P := ring x;
K := coefficientRing P;
(M,D) := coefficients x;
d := product apply(flatten entries D, a->denominator lift(a,K));
d*x
)
genericFreeness = method()
genericFreeness(Ideal) := K -> ( -- We no longer use this method, but it reminds us how the module method should look
G := flatten entries gens gb K;
apply(G,leadCoefficient)
)
genericFreeness(Module) := M -> (
-- module/polynomial ring B with coefficient ring A: returns (possibly empty) list {f_1,...,f_r} of nonzero elements of B with M_f free over A_f
-- if the list is empty, then M is free over A.
-- we should just be able to use "leadCoefficient", but the method does not seem to work properly
m := gens gb presentation M;
n := numgens source m - 1;
is := apply(toList(0..n),i->leadCoefficient m_((leadComponent m)_i,i));
hs := toList set is; -- remove duplicates to cut down on recursive calls
select(hs, f -> f!=1) -- remove 1's from the list to save silly recursive calls
)
genericFreenessFP = method()
genericFreenessFP(Module) := M -> (
-- module over B that is finitely presented over A: returns list as above
-- something bad is going on here, since I copied this code from the relativeFlatLocus code below
B := ring(M);
A := coefficientRing(B);
C := ambient(B); -- C is a polynomial ring over A
J := ideal B; -- We have C/J = B, presenting B as A-algebra
m := presentation M; -- Next we lift M to a module over C
N := (cokernel lift(m,C))/J; -- This C-module N is M restricted to C along C-->B
genericFreeness N
)
absFrob = method()
-- returns the absolute Frobenius endomorphism of A, if A has characteristic p for some p)
absFrob(Ring) := A -> (
p := char A;
if not isPrime p then
error "expected a ring with prime characteristic"
else map(A,A,apply(gens(A,CoefficientRing=>ZZ),x->x^p))
)
---
-- partially ordered set of open loci
---
inRad = method()
inRad(RingElement, Ideal) := (a,I) -> (
--check whether the ring element a is in the radical of I without computing the radical.
--one should check to see that a and I have the same ring.
--t := symbol t; --This worked! Hooray! This is not the way I've used this in the past...
A := ring a;
B := A (monoid [Variables=>1]);
promote(I,B)+ideal(promote(a,B)*B_0-1) == B
)
isOpenSubLocus = method()
isOpenSubLocus(Ideal,Ideal) := (I,J) -> (
-- assume I and J are ideals in the same ring A---one should test for this!
-- check whether the open locus defined by I is a subset of the open locus defined by J
if (ring I) =!= (ring J) then error "expected ideals in the same ring";
all(flatten entries gens I, x->inRad(x,J)) -- perhaps "I_*" in place of "flatten entries ... "
)
equalOpenLoci = method()
equalOpenLoci(Ideal,Ideal) := (I,J) -> isOpenSubLocus(I,J) and isOpenSubLocus(J,I)
---
-- Flat locus methods
----
-- perhaps add a "verbose" flag to activate printing of evidence for flatness.
-- Almost everything breaks if we try to apply it to a module over a fraction field B, since fraction fields have no ambient rings.
-- One should catch this possibility and give an error message instead of crashing into the debugger.
flatLocus = method()
flatLocus(Module) := M -> trim ann Ext^1(trim M, trim image presentation M)
-- I put the "trim" in, since I was getting an error "wrong number of rows or columns"
-- when I called this function while trying to compute the regular locus for ZZ[x]/(x^3 - 2)
relativeFlatLocusINT = method()
relativeFlatLocusINT(Module, Ideal) := (M,I) ->(
-- this function can be horribly slow!
-- The recursive calls often seem to calculate the same things over and over.
-- Is there a way to cache values to speed up the recursion? Surely there must be a way to do this...
-- print I; -- remove this comment for an amusing look at the horrors of recursion
B := ring(M); -- we are assuming B is a polynomial ring in what follows.
A := coefficientRing(B); -- This line does not work, if B is ZZ,QQ,ZZ/p, GF(p,n),RR, CC, etc.
if I == A then (ideal(1_B))
else (
C := (A/I) monoid B; -- is this the right way to do it?
fs := apply(genericFreeness(C**M), f -> lift(f,A));
xs := apply(fs, f -> trim relativeFlatLocusINT(M, trim(I+ideal(f)))); -- probably too much "trim"ming!
L1 := if xs=={} then (ideal(1_B)) else trim (intersect(xs)); -- empty intersection needs to be handled separately
L2 := trim ann(HH_1((chainComplex(map(A^1, module I, gens I))**B)**M)); -- The homology is Tor_1^A(A/I,M)
trim intersect(L1,L2)
)
)
relativeFlatLocusZZ = method()
--because Macaulay 2 only handles ZZ/n for n prime, we need a special
--method to handle flatness over ZZ.
relativeFlatLocusZZ(Module) := M ->(
--internal method: we assume that M is a module over a polynomial ring over ZZ
B := ring(M);
if 0_B == 1_B then ideal(1_B);
f := product(genericFreeness(M)); --nonzero element of ZZ such that M is free over ZZ[1/f]
ann(ker(map(M,M,{{f}}))) -- M is flat over ZZ if and only if it is torsion free.
-- The only torsion is f-power torsion. As long as the f-torsion
-- submodule vanishes, M is flat
)
relativeFlatLocus = method()
relativeFlatLocus(Module) := M ->(
B := ring(M);
A := coefficientRing(B);
(D,f) := flattenRing(B,CoefficientRing => A); --we do this for safety
C := ambient(D); -- C is a polynomial ring over A
J := ideal D; -- We have C/J = D, presenting D as A-algebra
m := presentation (f**M); -- Next we lift M to a module over C
N := (cokernel lift(m,C))/J; -- This C-module N is M restricted to C along C-->B
if A === ZZ then
trim ((f^-1)(promote(relativeFlatLocusZZ(N),D)))
else
trim ((f^-1)(promote(relativeFlatLocusINT(N,ideal(0_A)),D)))
-- The internal flatLocusINT routine works with modules over polynomial rings. We promote back to B
)
relativeFlatLocus(Ring) := B -> relativeFlatLocus(B^1)
relativeFlatLocus(Ideal) := J -> lift(relativeFlatLocus(ring(J)^1/J), ring(J))
relativeFlatLocus(RingMap, Module) := (f,M) -> (
-- We expect here that the target of f is the ring of M. (We should check that and print and error, if it is not the case.)
-- The check on these hypotheses can occur in "presentGraph", which does the grunt work of setting up the proper rings.
-- The function returns the flat locus for M considered as a module over the source A of f.
-- See tensor product code for how to make missing checks on R and f.
if target f =!= ring M then error "expected target of ring map to be ring of module";
(D,i,j) := presentGraph(f);
trim j(relativeFlatLocus(i**M))
)
relativeFlatLocus(RingMap) := f -> relativeFlatLocus(f,(target f)^1)
-- Returns flat locus for target of ring homomorphism f over its source.
-- We assume that source and target have the same coefficient ring and that f is a homomorphism over this ring.
-----
-- Semi-continuity of fiber dimension
-----
relativeDimensionINT = method()
relativeDimension = method()
relativeDimension(ZZ, RingMap) := (n,phi) -> (
-- work in progress: This function implements "semicontinuity of fiber dimension."
-- it yields the open locus (in Spec(B)) where the map f:Spec(B)-->Spec(A) has relative dimension < n.
-- For example, if we take n = 1, we get the quasi-finite locus of the morphism.
-- test example: A = QQ[x,y], B = QQ[u,v], phi = map(B,A,{u,u*v}). The when n = 0, we get ideal(0_B);
-- when n = 1, we get ideal(u); when n > 1, we get ideal(1_B).
A := source phi;
B := target phi;
relativeDimensionINT(n, phi, ideal(0_A), ideal(0_B))
)
relativeDimensionINT(ZZ,RingMap,Ideal,Ideal) := (n,phi,I,J) -> (
--The internal function to be called recursively.
--The function returns the locus in Spec(B/J) over which the relative dimension of Spec(B/J)-->Spec(A/I) is less than n.
--I think if one looks carefully, then one can see Noetherian induction just on B.
A := source phi; -- I should be an ideal of A
B := target phi; -- J should be an ideal of B
if J==B then J
else(
if not isPrime(J) then
trim (intersect apply(minimalPrimes(J),P->relativeDimensionINT(n, phi, I, P)))
else (
if preimage(phi, J) != I then -- this check does not work if A does not have a coefficientRing, e.g. if A is QQ
trim relativeDimensionINT(n, phi, preimage(phi, J), J)
else (
(D,i,j) := presentGraph(phi);
r := map(frac (A/I),A);
(F,k) := extendScalars(D/(i(J)), r); -- fiber of Spec(B/J) over generic point of Spec(A/I)
e := dim F; -- dimension of generic fiber of Spec(B/J)->Spec(A/I); is this just the difference of the dimensions?
if n <= e then J
else (
(R,s) := extendScalars(D,map(A/I,A));
M := s**(i**(B^1/J));
ls := apply(genericFreenessFP(M), r->lift(r,A));
f := product ls;
trim relativeDimensionINT(n, phi, I + ideal(f), J + ideal(phi(f)))
)
)
)
)
)
----------------------------
-- Cotangent complex methods
-- The methods below only work when the ambient ring of the input ring is a polynomial ring.
-- The functions that call them always call (I hope) with input of this form.
----------------------------
Omega1 = method()
-- This module is the truncation of the full cotangent complex at degree 0.
-- The version with "Ring" input only works when the ambient ring of the input
-- is a polynomial ring.
Omega1(Ring) := B -> trim (B**(coker jacobian ideal B))
Omega1(RingMap) := f -> (
(D,i,j) := presentGraph(f);
trim (j**Omega1(D))
)
naiveCotComplex = method()
naiveCotComplex(Ring) := B -> (
-- naive cotangent complex for B/A, where A = coefficientRing B.
-- If B = P/J, where P is the ambient polynomial ring over A, then the complex is
-- J/J^2 --> Omega^1_{P/A}\otimes_P B,
-- where the differential comes from the derivative d.
-- This complex is the truncation of the full cotangent complex at degree -1.
-- This function assumes that the ambient ring P is a polynomial ring.
J := ideal B;
P := ambient B;
n := numgens P;
d := map(P^n, module J, jacobian J);
C := chainComplex(d)**B
)
{*naiveCotComplex(RingMap) := f -> (
-- This does not work. Is it possible to extend scalars for a chain complex along a ring homomorphism?
(D,i,j) := presentGraph(f);
j(naiveCotComplex(D)) -- I don't know what to do here: j(chain complex) doesn't work and neither does any tensor product that I tried
-- perhaps one could extract the components of the complex and extends scalars manually.
)
*}
LSCotComplex = method()
LSCotComplex(Ring) := B -> (
-- Lichtenbaum-Schlessinger cotangent complex, which is the truncation of the full cotangent complex
-- at degree -2. One writes it as
-- Rel/TrivRel --> F\otimes B --> Omega^1_{P/A}\otimes B
-- doesn't quite work yet ** but maybe it does now
-- in any case, the function assumes that the ambient ring P is a polynomial ring.
J := ideal B;
P := ambient B;
n := numgens J;
d1 := jacobian J;
m := presentation module J;
Rel := image m;
TrivRel := image koszul(2, gens module J);
d2 := map(P^n, Rel/TrivRel, m);
C := chainComplex(P);
C.dd_1 = d1; C.dd_2 = d2; -- I tried to do this with the "chainComplex" command, but it did not work
C**B
)
----
-- Smooth, etale, unramified, relative complete intersection: Loci computed using cotangent complex; all relative properties
-- other things to add: syntomic (flat plus lci) locus.
---
unramifiedLocus = method()
unramifiedLocus(Ring) := B -> trim fittingIdeal(0, Omega1(B))
-- one could also use annihilator; unramified means vanishing of relative Omega^1
unramifiedLocus(RingMap) := f -> trim fittingIdeal(0, Omega1(f))
-- one could also use annihilator; unramified means vanishing of relative Omega^1
smoothLocus = method()
smoothLocus(Ideal) := J -> trim lift(smoothLocus ((ring J)/J), ring J)
smoothLocus(Ring) := B -> (
-- smooth locus for B over its coefficientRing
-- doesn't quite work yet ** in fact, I think it it working!
C := naiveCotComplex(B);
trim intersect(ann HH_1(C), flatLocus HH_0(C))
)
smoothLocus(RingMap) := f -> (
-- same hypotheses on f as in flatLocus(RingMap). Returns smooth locus of f.
(D,i,j) := presentGraph(f);
trim j(smoothLocus(D))
)
etaleLocus = method()
etaleLocus(Ring) := B -> trim (smoothLocus(B)*unramifiedLocus(B)) -- etale means smooth and unramified
etaleLocus(RingMap) := f -> trim (smoothLocus(f)*unramifiedLocus(f))
lciLocus = method()
lciLocus(Ring) := B-> (
-- relative complex intersection locus for B over its coefficientRing: we need T_2(B,A,B) = 0 (HH_2 of LS-cot. complex) and I/I^2 locally free,
-- where B = P/I with P a polynomial ring over A.
I := module ideal B;
C := LSCotComplex(B);
L1 := HH_2(C);
L2 := Ext^1(B**I, trim image presentation (B**I)); -- obstruction to I/I^2 = B**I locally free
trim intersect(ann L1, ann L2)
)
lciLocus(RingMap) := f -> (
-- same hypotheses on f as in flatLocus(RingMap). Returns the relative CI locus of f.
(D,i,j) := presentGraph(f);
trim j(lciLocus(D))
)
----
-- Methods for absolute properties: CM, Gorenstein, regular, etc
-- possibly add: reduced locus, normal locus, S_n locus, R_n locus
-- expose and document R_n, S_n, reduced checks
-- extend methods using primary decomposition to affine rings by finding
-- models over QQ or ZZ/p and extending scalars as in regularLocus function
---
codimPiece = method()
--Given a closed subset cut out by an ideal, returns the open locus where the closed subset has codimension > n
--only works for affine rings because of dimensions and primary decomposition.
--This function realizes the lower semicontinuity of codim_x(Y,X) in the variable x for a closed subset Y of X.
--Note that for x in X-Y, the codimension is +infty. In general, the larger the requested codimension, the smaller
--the open set. (Only high codimension irreducible components of Y are included.)
codimPiece(ZZ,Ideal) := (n, I) -> (
ps := select(minimalPrimes(I), P -> ( trueCodimension(P) <= n ));
if ps == {} then
ideal(1_(ring I))
else
trim intersect ps
)
dimPiece = method()
-- realizes upper semicontinuity of dim_x(X) in x: returns open locus of x where dim_x(X)<n.
dimPiece(ZZ, Ring) := (n, A) -> (
ps := select(minimalPrimes(ideal(0_A)), P -> (dim(A/P) >= n));
if ps == {} then
ideal(1_A)
else
trim intersect ps
)
trueCodimension2 = method()
trueCodimension2(Ideal) := I -> (
-- This function gives the codimension of Z(I) in Spec(A), where A is the ring of I
-- the built-in function may give the wrong result, if Spec(A) is not biequidimensional
-- Since this function uses primary decomposition, it only works when
-- the base ring of A is QQ or ZZ/p
A := ring I;
if not isAffineRing(A) then
error "expected affine ring"
else (
min(apply(minimalPrimes(ideal(0_A)), P->dim(A/P)-dim(A/(I+P))))
)
)
trueCodimension = method()
trueCodimension(Ideal) := I -> (
-- This function does the same thing as above but without using the full
-- computation of minimal primes containing I. The same function would work
-- for finitely presented ZZ-algebras, if one could get "dim" to work for
-- such rings.
-- The key here is that equidimensional affine rings are biequidimensional, which lets us
-- compute codimension as below.
A := ring I;
if not isAffineRing(A) then
error "expected affine ring"
else (
min(apply(equidimensional(ideal(0_A)), P->dim(A/P)-dim(A/(I+P))))
)
)
equidimensional = method()
equidimensional(Ideal) := I -> (
-- Returns a list of equidimensional ideals whose product has the same radical as I,
-- i.e. that cuts out the same subset of Spec(A) as I.
-- The function is handy, since it does not use primary decomposition.
-- In place of primary decomposition, it uses homological methods from
-- Eisenbud-Huneke-Vasconcelos. It will only work for an ideal in a ring
-- finitely presented over a regular ring.
-- despite my hopes, this function does not yet work over ZZ
-- ** In fact, the E-H-V approach is already in "topComponents"
A := ring I;
(B,i) := flattenRing A;
P := ambient B;
-- We will assume that the coefficient ring here is ZZ or a field -- but ZZ does not yet work
J := lift (i^-1 I, P);
apply(equidimensionalINT J, K -> i(promote(K,B)))
)
equidimensionalINT = method()
equidimensionalINT(Ideal) := I -> (
-- This function assumes that the given ideal is in an ideal in a regular domain.
P := ring I;
if I == P then {}
else
(
e := codim I; -- this does not work over ZZ!
J := ann Ext^e(P^1/I,P);
-- alternative would be to use
-- J := topComponents(I);
prepend(J,equidimensionalINT(saturate(I,J)))
)
)
regularLocus = method()
regularLocus(Ring) := A -> (
-- this function seems to work now for rings finitely presented over a field.
-- the tricky thing is to handle char = p, since to use the smooth locus to detect regularity
-- we need to work over a perfect field. To reduce to the case of a perfect base field,
-- we find a ring B that is finitely presented over ZZ/p such that A is a localization of B.
-- (The fRing function does the necessary calculation.)
-- a useful check to separate easier cases from harder cases is "isAffineRing", which checks whether
-- a ring is a quotient of a polynomial ring over a field.
if isAffineRing(A) then (
if char A == 0 then smoothLocus(A)
else (
(B,j) := fRing(A); j(smoothLocus(B))
)
)
else if coefficientRing(A) === ZZ then regularLocusZZ(A)
else error "only affine rings and rings finitely presented over ZZ implemented."
)
kunzRegularLocus = method()
-- An alternative way to calculate the regular locus for a ring of char. p
kunzRegularLocus(Ring) := A -> (
(B,i) := fRing(A); i(relativeFlatLocus absFrob B)
-- we first extract a finitely presented ring over ZZ, QQ, or ZZ/p to work with;
-- this step is necessary, since the flatness function for a homomorphism requires that homomorphism
-- be linear over the coefficientRings of the source and target.
-- a theorem of Kunz shows that for a ring of char p, flatness of absolute Frobenius is equivalent to regularity
-- note that if B does not have characteristic p, then "absFrob" returns an error
)
primeFactors = method()
primeFactors(ZZ) := n -> (
-- returns a list of the prime factors of n
apply(toList factor n, p -> p # 0)
)
regularLocusZZ = method()
regularLocusZZ(Ring) := A -> (
-- We assume without checking that the coefficientRing of A is ZZ for this internal function.
-- The goal is to define a function that returns the regular locus
-- of a finitely presented ZZ algebra
-- The function is just a toy, since M2 cannot handle ZZ/p when we have p > 32767 = 2^15 - 1
-- Such primes turn up incidentally in innocent-looking computations.
-- Consider, for example, regularLocusZZ (ZZ[x]/(x^7-5*x+5)), where the prime 590263 appears as a factor
-- of the discriminant and thus stops the computation.
-- Singular looks a bit better, since it works to 2^31 - 1.
I := smoothLocus(A); -- calculate the smooth locus over ZZ: I cuts out the singular locus
J := genericSaturate(I); -- calculate the closure of the singular locus of the fiber over QQ
K := saturate(I,J); -- remove the components of J from I
n := char (A/K); -- this characteristic cannot be 0, since we removed the part of the non-smooth locus over (0_ZZ)
ps := primeFactors(n);
if ps == {} then J -- the list "ps" is empty precisely when n = 1, i.e. when I and J have the same components.
-- in this case, both cut out the singular locus; we can return either one.
else (
trim intersect(J, intersect apply(ps, p->regularLocusFiber(p, A, K)))
-- the singular locus will be the union of J and the singular loci in the fibers that meet K
)
)
regularLocusFiber = method()
regularLocusFiber(ZZ, Ring, Ideal) := (p,A,I) ->(
-- take a prime p, a finitely presented ZZ-algebra A, and an ideal I in A
-- Returns the locus in Spec(A) of primes containing I and p where A is regular
-- This function is quite awkward, since M2 has weak support for finitely presented ZZ-algebras
-- We compute some things in the fiber over p and then lift back to A
-- We use the local criterion for regularity: if A/I is regular then A is regular along I
-- precisely where A-->A/I is an lci morphism, i.e. where we have a regular immersion.
Ap := A/ideal(p_A);
Ip := promote(I,Ap);
(F,phi,psi) := presentGraph(map(Ap,ZZ/p)); -- phi:Ap-->F and psi:F-->Ap isomorphism.
if Ip == ideal(1_Ap) then ideal(1_A)
else
(
ps := minimalPrimes(phi(Ip));
if phi(Ip) != ps_0 then trim intersect(apply(ps, P->regularLocusFiber(p,A,lift(psi(P),A))))
-- if phi(Ip) (I + <p>) is not prime, may call the function recursively on the minimal primes.
-- in the second part of the function, we may thus assume that phi(Ip) is prime.
-- that recursion is probably not the cleverest thing to do, but it works.
else
(
R := A/(I+ideal(p_A));
J := lciLocus(map(R,A)); -- this tells us where Spec(A/(I+<p>))->Spec(A) is a regular immersion
if J == ideal(0_R) then -- if it fails to be a regular immersion everywhere, along I, then
-- A is not regular at the generic point of I+<p> and hence is not regular along all of I+<p>
-- since the regular locus is open.
(I + ideal(p_A))
else
regularLocusFiber(p,A,lift(psi(smoothLocus(phi(Ip))),A)*lift(J,A))
-- A is regular at the places where A/(I + <p>) is regular and A->A/(I+<p>) is lci.
-- we continue recursively, which is safe to do, since the regular locus of A/(I+<p>)
-- (same as smooth locus over ZZ/p) is non-empty open, as A/(I+<p>) is a finitely
-- presented domain over ZZ/p.
)
)
)
genericSaturate = method()
genericSaturate(Ideal) := I -> (
-- Hidden method: assume I is an ideal in a ring A finitely presented over ZZ
-- find a new ideal cutting out the closure of the generic fiber of V(I) in Spec(A)
A := ring I; -- assume here that coefficientRing is ZZ
d := product apply(flatten entries gens gb I, leadCoefficient);
-- P := ambient A;
-- J := lift(I,P);
-- d := product apply(flatten entries gens gb J, leadCoefficient);
-- It seems that Groebner bases over quotient rings
-- are set up so that we do not need to lift to polynomial rings
-- I am a bit nervous, but it seems to work.
saturate(I, ideal(d_A))
)
CMLocus = method()
CMLocus(Ring) := A -> (
-- examples of non-CM rings: QQ[x,y]/(x^2,x*y) and QQ[x,y,z]/(x*y,x*z)
-- if A is an affine ring or a finitely presented ZZ algebra, the function returns its CM locus
(R, f) := flattenRing A;
k := coefficientRing R;
if (isField k) or (k===ZZ) then (
C := ambient R;
I := ideal R;
g := map(R,C);
n := dim C;
J := sum(0..n, i->product(select(0..n,j->j!=i),j->ann(Ext^j(C^1/I,C^1))));
trim f^-1(g(J))
)
else error "expected affine ring or finitely presented ZZ-algebra"
)
GorensteinLocus = method()
GorensteinLocus(Ring) := A -> (
-- nice test case: take coimage of QQ[u,v,w]->Q[t] by u->t^3, v->t^4, w->t^5. CM but not Gorenstein.
-- Looks at Bruns-Herzog for more examples. Find something nice to show Gorenstein but not CI
(R, f) := flattenRing A;
k := coefficientRing R;
if (isField k) or (k===ZZ) then (
C := ambient R;
I := ideal R;
g := map(R,C);
n := dim C;
L := f^-1(g(product(0..n,j->fittingIdeal(1,Ext^j(C^1/I,C^1)))));
trim intersect(L,CMLocus(A))
)
else error "expected affine ring or finitely presented ZZ-algebra"
)
CILocus = method()
-- calculates the absolute CI locus for affine rings and finitely presented ZZ algebras
-- using the fact that the CI locus is the same as the lci locus over the base field
-- or over ZZ.
CILocus(Ring) := A -> (
(R, f) := flattenRing A;
k := coefficientRing R;
if (isField k) or (k===ZZ) then
trim f^-1(lciLocus(map(R,k)))
else
error "expected affine ring or finitely presented ZZ-algebra"
)
isCM = method()
isCM(Ring) := A -> (
-- tests whether the ring A is CM.
CMLocus(A) == A
)
isS = method()
isS(ZZ, Ring) := (n,A) -> (
-- tests whether the ring A satisfies the S_n condition.
(trueCodimension(CMLocus(A))) > n -- since we use "true codim", this function works w/o equidimensional hypothesis
)
isR = method()
isR(ZZ, Ring) := (n,A) -> (
--tests whether the ring satisfies the R_n condition
--currently everything works for affine rings
(trueCodimension(regularLocus(A))) > n -- since we use "true codim", this function works w/o equidimensional hypothesis
)
isNormalFixed = method()
isNormalFixed(Ring) := A -> isS(2,A) and isR(1,A)
normalLocus = method()
normalLocus(Ring) := A -> codimPiece(1,regularLocus A)*codimPiece(2,CMLocus A)
isReduced = method()
isReduced(Ring) := A -> isS(1,A) and isR(0,A)
reducedLocus = method()
reducedLocus(Ring) := A -> intersect minimalPrimes ideal(0_A)
beginDocumentation()
-- Front Page
doc ///
Key
Loci
Headline
A package for computing open loci corresponding to various properties of rings and ring maps
Description
Text
Let $A$ be a commutative ring that is finitely presented over $\mathbf{Z}$ or over a field.
For many properties $\Pi$ of local rings, the set $U$ of primes $P$ in ${\rm Spec}(A)$ such that $A_P$
has the property $\Pi$ is open in ${\rm Spec}(A)$. This holds, for example, if $\Pi$ is the Gorenstein property,
the Cohen-Macaulay property, or the regularity property.
Let $\phi:A\to B$ be a ring homomorphism, and let $f:{\rm Spec}(B)\to {\rm Spec}(B)$ be the corresponding
morphism of spectra. For some properties of scheme morphisms, the set $U$ of points $P$ in ${\rm Spec}(B)$ such that $f$ has the
given property at $P$ is open in ${\rm Spec}(B)$. This openness holds, for example, for the properties of being flat,
unramified, {\'e}tale, and smooth.
This package provides functions that compute such open loci where various properties hold.
Each function constructing such a locus returns an ideal. The elements of the desired locus
are those primes that do not contain the ideal. (Two ideals thus describe the same locus when
they are contained in the same prime ideals, i.e. when they have the same radical.)
///
doc ///
Key
isOpenSubLocus
(isOpenSubLocus, Ideal, Ideal)
Headline
calculates order relation on open subsets of spectra
Usage
i=isOpenSubLocus(I,J)
Inputs
I:Ideal
J:Ideal
assumed to be in the same ring as I
Outputs
i:Boolean
indicating whether the open locus defined by I is contained in the open locus defined by J
Description
Text
Let $I$ and $J$ be finitely generated ideals in a ring $A$. These ideals correspond
to open subspaces $U$ and $V$ of ${\rm Spec}(A)$. We have $U\subset V$ precisely when
for each generator $f$ of $I$, the monoid generated by $f$ meets $J$. In other words
we have $U\subset V$ precisely when some power of $f$ is in $J$ or, equivalently,
when $f$ is in the radical of $J$. We check this condition by checking whether
$1-ft$ and $J$ generate the unit ideal in $A[t]$.
SeeAlso
equalOpenLoci
///
doc ///
Key
equalOpenLoci
(equalOpenLoci, Ideal, Ideal)
Headline
determines if the open loci defined by ideals are equal
Usage
I = equalOpenLoci(I,J)
Inputs
I:Ideal
J:Ideal
assumed to be in the same ring as I
Outputs
i:Boolean
indicating whether the open loci defined by I and J are equal
Description
Text
Let $I$ and $J$ be finitely generated ideals in a ring $A$. These ideals correspond
to open subspaces $U$ and $V$ of ${\rm Spec}(A)$. Using the function isOpenSubLocus,
we determine if these subspaces are equal.
SeeAlso
isOpenSubLocus
///
-- flatLocus
doc ///
Key
flatLocus
(flatLocus, Module)
Headline
calculates the flat locus of a finitely presented module over a ring
Usage
I = flatLocus(M)
Inputs
M:Module
Outputs
I:Ideal
defining the (open) flat locus of $M$
Description
Text
Let $A$ be the ring of $M$. For a prime ideal $P$ of $A$, the localization
$M_P$ is flat over $A$ if and only if $P$ does not contain $I$, i.e. the flat
locus of $M$ in ${\rm Spec}(A)$ is complement of the closed set defined by $I$.
Example
A = QQ[x,y];
M = module ideal(x,y);
flatLocus M
Text
The method works as follows: let $0\to K\to F\to M\to 0$ be an exact sequence with
$F$ projective. Then $M$ is locally free precisely when ${\rm Ext}^1(M,K)$ vanishes.
Since the formation of this ${\rm Ext}$ module is compatible with localization, the
open locally free locus is the complement of the support of the ${\rm Ext}$ module.
The ideal returned is an ideal cutting out this support.
SeeAlso
relativeFlatLocus
///
-- relativeFlatLocus
doc ///
Key
relativeFlatLocus
(relativeFlatLocus, Module)
(relativeFlatLocus, Ring)
(relativeFlatLocus, Ideal)
(relativeFlatLocus, RingMap)
(relativeFlatLocus, RingMap, Module)
Headline
calculates the relative flat locus of a finitely presented module over an algebra
Usage
I = relativeFlatLocus M
I = relativeFlatLocus B
I = relativeFlatLocus J
I = relativeFlatLocus(phi, M)
Inputs
M:Module
B:Ring
J:Ideal
phi:RingMap
Outputs
I:Ideal
defining the (open) relative flat locus of M
over the coefficient ring of B or over the source of phi
Description
Text
Let $M$ be a finitely presented module over a commutative ring $B$. Suppose
that $B$ is a finitely presented $A$-algebra, either given as a quotient of
a polynomial ring over $A$ or provided with its $A$-module structure via a
ring homomorphism $\phi:A\to B$. Under these hypotheses, there is an open set
$U$ in ${\rm Spec}(B)$ such that for all primes $P$ of $B$, the localization
$M_P$ is flat over $A$ if and only if $P$ is in $U$.
This method returns an ideal $I$ of $B$ with the property that for all primes
$P$ of $B$, the localization $M_P$ is flat over $A$ if and only if $P$ does not
contain $I$, i.e. the relative flat locus for $M$ over $A$ is the complement in
${\rm Spec}(B)$ of the closed set defined by $I$.