-
Notifications
You must be signed in to change notification settings - Fork 1
/
Binomials.m2
2457 lines (2225 loc) · 88.7 KB
/
Binomials.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
-- -*- coding: utf-8 -*-
-- Binomials.m2
--
-- Copyright (C) 2009-2018 Thomas Kahle <[email protected]>
--
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or (at
-- your option) any later version.
--
-- This program is distributed in the hope that it will be useful, but
-- WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
--
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
newPackage(
"Binomials",
Version => "1.3",
Date => "September 2018",
Authors => {{
Name => "Thomas Kahle",
Email => "[email protected]",
HomePage => "http://www.thomas-kahle.de"},
{
Name => "Ruilong Zhuang",
Email => "[email protected]"}, --joined in 2018
{
Name => "Alexandru Iosif",
Email => "[email protected]",
HomePage => "https://alexandru-iosif.github.io"}}, --joined in 2018
Headline => "Specialized routines for binomial ideals",
PackageImports => {"FourTiTwo", "Cyclotomic"},
Reload => true,
Certification => {
"journal name" => "The Journal of Software for Algebra and Geometry: Macaulay2",
"journal URI" => "http://j-sag.org/",
"article title" => "Decompositions of binomial ideals",
"acceptance date" => "2012-02-06",
"published article URI" => "http://j-sag.org/Volume4/jsag-1-2012.pdf",
"published code URI" => "http://j-sag.org/Volume4/Binomials.m2",
"repository code URI" => "svn://svn.macaulay2.com/Macaulay2/trunk/M2/Macaulay2/packages/Binomials.m2",
"release at publication" => 14467,
"version at publication" => "1.0",
"volume number" => "4",
"volume URI" => "http://j-sag.org/Volume4/"
}
)
export {
-- 'Official' functions
"binomialPrimaryDecomposition",
"binomialCellularDecomposition",
"binomialUnmixedDecomposition",
"binomialRadical",
"binomialMinimalPrimes",
"binomialAssociatedPrimes",
"binomialSolve",
"binomialBasis",
"monomialParameterization",
-- tests
"binomialIsPrime",
"binomialIsPrimary",
"cellularBinomialIsPrimary",
"isCellular",
"isBinomial",
"isUnital",
-- input related
"makeBinomial",
"latticeBasisIdeal",
-- cellular stuff:
"cellularBinomialAssociatedPrimes",
"cellularBinomialUnmixedDecomposition",
-- "cellularAssociatedLattices",
"cellularBinomialPrimaryDecomposition",
"cellularBinomialRadical",
-- simple wrappers:
"BPD",
"BCD",
"BUD",
-- auxillary functions:
"partialCharacter",
"idealFromCharacter", -- should be renamed to ideal once M2 supports this
"randomBinomialIdeal",
"extractInclusionMinimalIdeals",
-- Not in the interface:
-- "axisSaturate",
-- "cellVars",
-- "cellularEmbeddedLatticeWitnesses",
-- "Lsat",
-- "saturatePChar",
-- "satIdeals",
-- "nonCellstdm",
-- "maxNonCellstdm",
-- "minimalPrimaryComponent",
-- "binomialFrobeniusPower",
-- -- Helpers for Gröbner-free isBinomial
-- "reducedRowEchelon",
-- "gensMinimalDegree",
-- "isSupportOfRowsAtMostTwo",
-- "CKbasisRecursion"
-- "isBinomialGroebnerFree",
-- "isStandardGraded",
-- Options
"CellVariables", -- for partialCharacter
"ReturnPrimes", -- for cellularBinomialIsPrimary
"ReturnPChars", -- for cellularBinomialIsPrimary
"ReturnCellVars", -- for binomialCellularDecomposition
"GroebnerFree", -- for isBinomial
--Types
"PartialCharacter"--HashTable
}
axisSaturate = (I,i) -> (
-- By Ignacio Ojeda and Mike Stillman
-- For computing saturations w.r.t. a single variable:
R := ring I;
I1 := ideal(1_R);
s := 0;
f := R_i;
while not(I1 == I) do (
s = s + 1;
I1 = I;
I = ideal syz gb(matrix{{f}}|gens I,
SyzygyRows=>1,Syzygies=>true););
{s-1, I}
)
-- Cellular decomposition of binomial ideals:
binomialCellularDecomposition = method (Options => {ReturnCellVars => false, Verbose=>false})
binomialCellularDecomposition Ideal := Ideal => o -> I -> (
-- based on code by Ignacio Ojeda and Mike Stillman
R := ring I;
if I == ideal (1_R) then return {};
n := numgens R;
Answer := {};
L := null;
IntersectAnswer := ideal(1_R);
ToDo := {{{1_R},toList(0..n-1),I}};
-- Each entry of the ToDoList is a triple:
-- #0 contains list of variables with respect to which is already saturated
-- #1 contains variables to be considered for cell variables
-- #2 is the ideal to decompose
compo := 0;
next := () -> (
if #ToDo === 0 then false
else (
L = ToDo#0;
ToDo = drop(ToDo,1);
if gens IntersectAnswer % L#2 == 0 then (
if o#Verbose then (
<< "redundant component" << endl;
)
)
-- if its not redundant:
else if #(L#1) === 0 then ( -- #(L#1) counts 'remaining variables to check'
-- no variables remain to check :
-- We have an answer
compo = compo + 1;
newone := trim L#2;
if o#Verbose then (
<< "cellular components found: " << compo << endl;);
if o#ReturnCellVars then Answer = append(Answer,{newone, delete(1_R,L#0)})
else Answer = append (Answer,newone);
IntersectAnswer = intersect(IntersectAnswer,newone);
-- if we have enough, stop after this iteration
if IntersectAnswer == I then ToDo = {})
else ( -- So, there are remaining variables #(L#1) is not zero
i := L#1#0; -- i is a variable under consideration
newL1 := drop(L#1,1); -- gets removed from the list
result := axisSaturate(L#2, i); -- compute saturation wrt i
J := result#1; -- Ideal
k := result#0; -- Saturation Exponent
if k > 0 then ( -- If a division was needed:
-- We add the monomial i^k to ideal under consideration
J2 := L#2 + ideal(R_i^k);
-- And remove L#0 components from variables that we already
-- considered before
J2 = saturate(J2, product L#0);
if J2 != ideal(1_R) then
-- If something is left after removing we have more to decompose J2
ToDo = prepend({L#0, newL1, J2},ToDo));
-- Continue with the next variable and add i to L#0
if J != ideal(1_R) then ToDo = prepend({L#0|{R_i}, newL1, J},ToDo);
);
true));
while next() do ();
Answer
)
-- This function saturates an integer lattice. It expects
-- the matrix A, whose image is the lattice.
Lsat = A -> LLL syz transpose LLL syz transpose A;
isCellular = method (Options => {ReturnCellVars => false})
isCellular Ideal := Ideal => o -> I -> (
-- This function checks if a binomial ideal is cellular
-- In the affirmative case it can return the cellular (regular) variables.
R := ring I;
cv := cellVars I;
if cv == {} then prod := 1_R else prod = product cv;
if I == saturate (I, prod) then (
-- I is cellular
if o#ReturnCellVars then return cv
else return true;
)
else false)
cellVars = method (Options => {CellVariables => null})
cellVars Ideal := Ideal => o -> I -> (
-- This function compotes the cell variables for a cellular ideal if necessary.
if o#CellVariables === null then (
cv := {};
for i in gens ring I do if saturate (I,i) != substitute(ideal(1), ring I) then cv=cv|{i};
return cv;
)
else o#CellVariables)
-- Setting up the PartialCharacter Type
PartialCharacter = new Type of HashTable;
partialCharacter = method (Options => {CellVariables => null})
partialCharacter Ideal := Ideal => o -> I -> (
-- Will compute the partial character associated to a cellular binomial ideal.
-- If the cell variables are known they can be given as an optional argument.
vs := {}; -- This will hold the lattice generators
vsmat := matrix "0"; -- Holds the matrix whose image is L
cl := {}; -- This will hold the coefficients
R := ring I;
CoeffR := coefficientRing R; -- needed to form terms below
II := ideal;
-- The input should be a cellular ideal
cv := cellVars(I, CellVariables=>o#CellVariables);
ncv := toList (set gens R - cv);
-- If there are no cellular variables,
-- the ideal is monomial and the partial character is zero:
if cv == {} then (
return new PartialCharacter from {"J"=>{}, "L"=>matrix "0", "c"=>{1}};
);
-- We need to construct this ring to properly extract coefficients below
S := CoeffR(monoid [cv]);
-- intersect I with the ring k[cv] and map to S
if #ncv != 0 then (
II = sub(eliminate (ncv, I),S);
)
else (
-- S = R, stick with original def!
II = I;
);
-- The partial character of the zero ideal is on the zero lattice.
if ( II == 0 ) then (
for i in cv do vs = vs | { 0_ZZ };
cl = {1_ZZ};
return new PartialCharacter from {"J"=>cv, "L"=>transpose matrix {vs}, "c"=>cl};
);
-- So, II is not zero:
-- Let ts be the list of minimal generators, this uses that II\subset S !
ts := entries mingens II;
-- for each term, find the exponent vector
oldmat := matrix "0";
oldvs := {};
for t in ts#0 do (
-- Want to check if we already have this generator included
-- Save the old values
oldmat = vsmat;
oldvs = vs;
-- compute new ones
vs = vs | {((exponents t)#0 - (exponents t)#1)};
vsmat = transpose matrix vs;
-- Do we need the new generator ?
if image oldmat == image vsmat then (
-- Undo changes:
vsmat = oldmat;
vs = oldvs;
)
else (
-- So we have a new generator : update coefficient list
coeffs := entries ((coefficients(t))#1);
F := coefficientRing ring coeffs#1#0;
coe := for c in coeffs list lift(c#0,F);
cl = cl | { sub ( -coe#1 / coe#0, CoeffR) };
);
);
return (new PartialCharacter from {"J"=>cv, "L"=> transpose matrix vs , "c"=>cl});
)
randomBinomialIdeal = (R,numge,maxdeg, maxwidth, homog) -> (
-- Generate 'random' ideals for testing purposes. The
-- distribution is completely heuristic and designed to serve
-- internal purposes
-- Inputs: a ring R, the number of generators numgen, the maximal
-- degree of each variable maxded, the maximal number of
-- variables appearing in binomial, wether the output should be
-- homogeneous
-- Caveat: The result might simply be not homogeneous or of the
-- given degree due to deps between the random generators
-- Output: a 'random' binomial ideal.
Rge := gens R;
ng := #Rge;
ge := {};
ra := 0; split := 0;
va := {}; m := {};
z := for i in 0..ng-maxwidth-1 list 0;
if homog then (
if odd maxwidth then maxwidth = maxwidth + 1;
for i in 0..numge do (
-- m will be a list of nonzero random exponents
m = for j in 0..(maxwidth//2)-1 list (
ra = (random (2*maxdeg)) +1 ;
if ra > maxdeg then ra = -ra // 2;
ra
);
m = m | for j in 0..(maxwidth//2)-1 list (
ra = (random (2*maxdeg)) +1 ;
if ra > maxdeg then ra = -ra // 2;
-ra
);
-- filling with zeros
m = random (m |z);
ge = ge | {makeBinomial (R,m,1)};
);
)
else (
for i in 0..numge do (
-- m will be a list of nonzero random exponents
m = for j in 0..maxwidth-1 list (
ra = (random (2*maxdeg)) +1 ;
if ra > maxdeg then ra = -ra // 2;
ra
);
-- filling with zeros
m = random (m |z);
ge = ge | {makeBinomial (R,m,1)};
);
);
ideal (mingens ideal(ge)))
isStandardGraded = R -> (
-- check if a ring is standard graded
if unique degrees R != {{1}} then return false;
true)
isBinomial = method (Options => {GroebnerFree => true})
isBinomial Ideal := Ideal => o -> I -> (
--Checks binomiality of an ideal
--Input: an ideal I
--Output: Boolean: whether the ideal is a binomial ideal
--Option: GroebnerFree: By default, it sets to be true, then use
--Groebner free method to detect binomiality. If it sets to be
--false, then compute redeced gb.
-- The GroebnerFree Method only works with a standard grading.
if not isStandardGraded ring I then return isBinomialGroebner I;
if not o#GroebnerFree then return isBinomialGroebner I;
isB := (isBinomialGroebnerFree I)#0;
if isHomogeneous I then return isB;
if not isB then
print "Gröbner free method failed.\nStarting computation of Gröbner basis...\n";
isBinomialGroebner I)
isBinomialGroebner = I -> (
ge := flatten entries gens gb I;
for g in ge do (
if #(terms g) > 2 then return false;
);
true)
binomialBasis = I -> (
--this function generates a binomial basis of an ideal if exists
--Input: an ideal I
--Output: a binomial basis of the input if it exists. Otherwise, error.
--The output is a matrix
-- The GroebnerFree Method only works with a standard grading.
if not isStandardGraded ring I then (
if isBinomialGroebner I then return gens gb I
else (
print "Ideal is not binomial";
return null);
);
(isB, bins) := isBinomialGroebnerFree I;
if isB then return bins;
if isHomogeneous I then (
print "Ideal is not binomial";
return null)
else (
-- inhomogeneous case:
print "Gröbner free method failed.\nStarting computation of Gröbner basis...\n";
if isBinomialGroebner I then return gens gb I
else (
print "Ideal is not binomial";
return null);
)
)
reducedRowEchelon = C ->(
--this function returns the row echelon form of a matrix
if C == 0 then return C;
c := numColumns C;
variable := symbol variable;
R:=ring (C_(0,0)); S := R[variable_1..variable_c,MonomialOrder => Lex];
C=sub(transpose(coefficients(matrix {flatten entries gens gb ideal flatten entries (sub(C,S)*transpose(vars S))},
Monomials=> flatten entries vars S))_1,R);
matrix reverse entries C)
minDegreeElements = L -> (
-- The input is a list of polynomials in a standard graded ring
-- and the output is the sublist of polynomials of minimal degree.
-- If the zero polynomnial appears in the list, it is discarded
L = select (L, l -> l!=0);
mindegree := min(apply (L, l -> first degree l));
select (L, l -> first degree l == mindegree)
)
isSupportOfRowsAtMostTwo = C ->(
--decides if a matrix has at most two non zero entries in each row
lead := 0;
for r to numRows C - 1 do(
lead = 0;
for c to numColumns C - 1 do(
if C_(r,c) !=0 then lead = lead+1;
if lead > 2 then return false;
);
);
true)
isBinomialGroebnerFree = I ->(
-- detecting Binomiality using Groebner Free method.
-- Implements Algorithm 3.3 in [CK15] and part of Recipe 4.5 in [CK15]
-- Input: an ideal I
-- Output: a list consisting of a boolean value indicating the binomality
-- of the given ideal and its binomial basis
-- CAVEAT: The calling function must respect that the Boolean return value
-- false does not mean that the ideal is not binomial. The calling function
-- must deal with the case of an inhomogeneous input and the return value
-- false separately.
R:=ring I;
F:= set I_*;
needToDehom := false;
if not isHomogeneous I then(
--homogenize the generators of the ideal as in Recipe 4.5 in [CK15]
ttt := symbol ttt;
K := coefficientRing R;
ge := baseName \ gens R;
-- The last variable is the homogenization variable
Rh := K(monoid [ge|{ttt}]);
IinRh := sub(I,Rh);
homogenizedGensI := homogenize (gens IinRh, last gens Rh);
needToDehom = true;
F = set flatten entries homogenizedGensI;
);
(isB, bins) := CKbasisRecursion F;
if not isB then return (false, {});
if needToDehom then (
bins = sub (bins, {last gens ring bins=>1});
bins = sub (bins, R);
);
(true,bins))
CKbasisRecursion = F -> (
-- Input: A set of standard-graded polynomials,
-- Output: (Boolean, a matrix of binomials)
-- Algorithm 3.3. in [CK15]
if F === set {} then return (true, matrix{{0}});
R:=ring first toList F;
Rorig:=ring first toList F;
A:=matrix{{}};
M:=matrix{{}};
B:={};
Fmin:={};
while F=!=set{} do(
Fmin = minDegreeElements toList F;
(M,A) = coefficients (matrix{Fmin}, Monomials =>
toList set flatten entries monomials matrix{toList F});
-- We think of this as coefficients times monomials -> transpose.
A = reducedRowEchelon transpose A;
M = transpose M;
if not isSupportOfRowsAtMostTwo A then return (false, {});
B = B | flatten entries sub(A*M, Rorig);
F = F-(set Fmin);
if F === set{} then return (true, matrix{B}); --this avoids the next step in case F is empty
R = R/(ideal (A*M));
F = set flatten entries gens sub(ideal toList F,R);
F = F-set{0_R};
-- Check again if the reduction made things zero
if F === set{} then return (true, matrix{B}); --this avoids the next step in case F is empty
);
-- We should never reach this:
print "You have found a bug in the binomials package. Please report.";
(false, matrix {{}})
)
monomialParameterization = I ->(
-- Write a prime binomial ideal as a kernel of a monomial homomorphism
-- Input: a prime binomial ideal containing no monomial The set of
-- generators should be in binomial form
-- Output: a map whose kernel is the original ideal
R := ring I;
-- basic checks:
eligible := true;
if not char R == 0 then eligible = false;
if not isBinomial I then eligible = false;
if not binomialIsPrime I then eligible = false;
if not eligible then (
error "Sorry, only implemented for prime binomial ideals in characteristic zero";
);
n := numgens R;
K := coefficientRing R;
exponentList := {};
coeff := null;
-- extract exponents into the rows of a matrix & put constant term
-- at in last column
for e in I_* do(
coeff = (coefficients e)#1;
coeff = flatten entries sub (coeff, K);
if (length coeff > 2) then (
error "One of the generators is not binomial. Please specify binomial generating set.";
);
if (length coeff == 1) then (
error "Input ideal cannot contain monomials.";
);
exponentList = exponentList|{(exponents e)#0 - (exponents e)#1|{-coeff#1/coeff#0}};
);
exponentMatrix := matrix exponentList;
--characters are enconded as variables v_i in a ring S
--storeVarMap maps v_i to the i-th character
imageDimension := rank exponentMatrix_{0..(numColumns exponentMatrix-2)};
i := 0;
while (imageDimension =!= numRows exponentMatrix) do (
if rank submatrix'(exponentMatrix,{i},) == imageDimension then (
exponentMatrix = submatrix'(exponentMatrix,{i},);
i = i-1;
);
i = i+1;
);
r := numRows exponentMatrix;
v := symbol v;
S := R[v_1..v_r,MonomialOrder => Lex];
exponentList = entries transpose exponentMatrix;
i = 0;
storeVarMap := for i to r-1 list v_(i+1)_S => (exponentList#-1#i);
--compute the kernel of exponentMatrix to find parameterization and
--perform the substitution of v_i by the i-th character
exponentList = exponentList_{0..(length exponentList-2)};
exponentMatrix = sub(matrix( transpose (exponentList|entries (id_(ZZ^r)*-1))),ZZ);
solution := gens gb ker exponentMatrix;
tt := symbol tt;
ti := symbol ti;
-- As the image ring we would like to use a Laurent polynomial
-- ring but kernels of maps from polynomial rings to Laurent
-- polynomial rings are not implemented yet. Therefore we
-- construct the Laurent polynomial ring by hand.
numv := numColumns solution-r;
G := K[tt_1..tt_numv, ti_1..ti_numv];
posvars := unique (gens G)_{0, numv-1}; -- unique solves the case L_{0,0};
invvars := unique (gens G)_{numv, 2*numv-1};
laurentRels := ideal for i from 1 to (numColumns solution-r) list tt_i*ti_i - 1;
H := G/laurentRels;
charValues := flatten entries sub(sub(vars S, storeVarMap),K);
L := entries solution;
--get parameterization from exponents in the list
T := for l in L list (
monomial := product for i to numv-1 list (
if l#i > 0 then posvars#i^(l#i) else (invvars#i)^(-l#i)
);
charMon := product for i to #charValues-1 list(
(charValues#i)^(l#(i+numv))
);
monomial*charMon);
map(H,R,T_{0..n-1}) --return the map
)
isUnital = I -> (
-- A unital ideal is generated by unital binomials and monomials.
ge := flatten entries gens gb I;
for g in ge do (
coeffs := sort flatten entries (coefficients g)#1;
if coeffs == {1} then continue;
if coeffs == { -1} then continue;
if coeffs == { -1 , 1} then continue;
return false;
);
true)
nonCellstdm = {CellVariables=>null} >> o -> I -> (
-- extracts the (finite) set of nilpotent monomials
-- modulo a cellular binomial ideal.
R := ring I;
cv := set cellVars(I, CellVariables=>o#CellVariables);
-- Extracting the monomials in the non-Cell variables.
-- Problem: They may not live in R because R was extended on the way.
-- This use of baseName is intended to fix a problem where the variables in cv
-- are actual variables of a ring over a field extension.
ncv := value \ toList (set (baseName \ (gens R)) - baseName \ cv);
-- We map I to the subring: kk[ncv]
CoeffR := coefficientRing R;
S := CoeffR(monoid [ncv]);
J := kernel map (R/I,S);
basis (S/J))
maxNonCellstdm = {CellVariables=>null} >> o -> I -> (
-- Computes the maximal monomials in the nilpotent variables
cv := cellVars(I, CellVariables=>o#CellVariables);
nm := flatten entries nonCellstdm (I,CellVariables=>cv);
-- The following code extracts the maximal elements in a list of monomials
result := {};
maxel := 0;
while nm != {} do (
maxel = max nm;
-- Add maxel to the result
result = result | {maxel};
-- Delete everyone who is dividing maxel
nm = for m in nm list (if maxel // m != 0 then continue; m);
);
result)
makeBinomial = (R,m,c) -> (
-- constructs the binomial associated to
-- exponent vector m and coefficient c in R
var := gens R;
posmon :=1_R;
negmon :=1_R;
for i in 0..#m-1 do (
if m#i > 0 then (
posmon = posmon * var#i^(m#i)
)
else (
negmon = negmon * var#i^(-m#i)
);
);
posmon - c*negmon)
idealFromCharacter = method();
idealFromCharacter (Ring, PartialCharacter) := Ideal => (R, pc) -> (
-- Constructs the lattice Ideal I_+(c) in R
-- R is a ring in which the ideal is returned
-- The columns of A should contain exponent vectors of generators
-- The vector c contains the corresponding coefficients which must lie
-- in the coefficient ring of R.
var := gens R;
if pc#"L" == 0 then return ideal 0_R;
cols := null;
binomials := null;
c := null;
k := ring pc#"c"#0;
idmat := matrix mutableIdentity(ZZ,#var);
if pc#"L" == idmat then (
-- If A is the unit matrix we are lucky,
-- no saturation is needed.
-- We coerce the coefficients to R:
c = apply (pc#"c", a -> (sub (a,R)));
cols = entries transpose pc#"L";
binomials = for i in 0..numcols(pc#"L")-1 list makeBinomial (R,cols#i, c#i);
return ideal binomials
)
else if set pc#"c" === set {1_k} then (
-- all coefficients are one, we can use 4ti2.
return toricMarkov (transpose pc#"L", R, InputType => "lattice");
)
else (
-- The general case, fall back to saturation in M2:
c = apply (pc#"c", a -> (sub (a,R)));
cols = entries transpose pc#"L";
binomials = for i in 0..numcols(pc#"L")-1 list makeBinomial (R,cols#i, c#i);
return saturate (ideal binomials, product var);
);
)
latticeBasisIdeal = (R,L) -> (
-- Constructs the lattice basis ideal (whose saturation is the lattice ideal)
-- Convention is that L's columns generate the lattice.
var := gens R;
if L == 0 then return ideal 0_R;
cols := null;
binomials :=null;
cols = entries transpose L;
binomials = for i in 0..numcols L-1 list makeBinomial (R,cols#i, 1);
ideal binomials)
saturatePChar = (pc) -> (
-- This function saturates a partial character and returns the result
-- as a list, even if the input was saturated.
-- If the lattice is saturated, the character is saturated
-- Note that this shortcircuits all problems with c being non-constant.
if image Lsat pc#"L" == image pc#"L" then (
return {pc};
);
-- The saturated lattice
S := Lsat(pc#"L");
-- The coefficient matrix :
K := pc#"L" // S;
-- print K;
-- Now we find the (binomial) equations for the saturated character:
numvars := numrows K;
varlist := for i in 0..numvars-1 list value ("m"|i);
Q := QQ(monoid [varlist]);
eqs := idealFromCharacter (Q, (new PartialCharacter from {"J"=>gens Q, "L"=>K, "c"=>pc#"c"}));
result := binomialSolve eqs;
r := #result;
i := 0;
return(for i from 0 to r-1 list(
new PartialCharacter from {"J" => pc#"J", "L" => S, "c" => result#i}));
)
satIdeals = (pc) -> (
-- Computes all the ideals belonging to saturations of
-- a given partial character.
satpc := saturatePChar(pc);
-- The following is the smallest ring containing all new
-- coefficients but not smaller than QQ
F := ring satpc#0#"c"#0;
if F === ZZ then F = QQ;
Q := F(monoid [satpc#0#"J"]);
for s in satpc list idealFromCharacter (Q, s))
binomialRadical = I -> (
cv := isCellular (I, ReturnCellVars=>true);
if not cv === false then (
return cellularBinomialRadical (I,CellVariables=>cv)
);
-- In the general case
print "Input not cellular, computing minimial primes ...";
mp := binomialMinimalPrimes I;
ideal mingens intersect mp)
cellularBinomialRadical = method (Options => {CellVariables => null})
cellularBinomialRadical Ideal := Ideal => o -> I -> (
-- Computes the radical of a cellular binomial ideal
R := ring I;
cv := cellVars(I, CellVariables=>o#CellVariables);
-- Get the non-cellular variables
noncellvars := toList(set (gens R) - cv);
M := sub (ideal (noncellvars),R);
I + M)
binomialIsPrimary = I -> (
-- Check if an arbitrary binomial ideal is primary
-- first check for cellularity, then run the specialized check if the ideal is cellular.
cv := isCellular (I, ReturnCellVars=>true);
-- Can't check with logical comparison because the return value could be a list
if cv === false then return false
else cellularBinomialIsPrimary (I, CellVariables=>cv))
cellularBinomialIsPrimary = method (Options => {ReturnPrimes => false , ReturnPChars => false, CellVariables=> null})
cellularBinomialIsPrimary Ideal := Ideal => o -> I -> (
-- Implements Alg. 9.4 in [ES96]
-- I must be a cellular ideal, CellVariables can be given for speedup
-- Returns the radical of I and whether I is primary
-- if the option ReturnPrimes is true, then it will return
-- the radical in the affirmative case and two distinct associated primes
-- otherwise
-- if the option ReturnPChars is true then it will return partial Characters
-- of the primes instead.
-- If both are true then it will return characters.
R := ring I;
-- Only proper ideals are considered primary
if I == ideal(1_R) then return false;
-- Handling of cell variables
cv := cellVars(I, CellVariables=>o#CellVariables);
-- Get the partial character of I
pc := partialCharacter(I, CellVariables=>cv);
noncellvars := toList(set gens R - cv);
M := sub (ideal (noncellvars),R);
-- the radical:
rad := I + M;
-- If the partial character is not saturated, the radical is not prime
if image Lsat pc#"L" != image pc#"L" then (
print "The radical is not prime, as the character is not saturated";
satpc := saturatePChar pc;
if o#ReturnPChars then (
-- This one is the fastest, so check it first
return {{satpc#0#"J",satpc#0#"L",satpc#0#"c"}, {satpc#0#"J",satpc#0#"L",satpc#1#"c"}}
);
if o#ReturnPrimes then (
F := ring satpc#0#"c"#0;
S := F(monoid [satpc#0#"J"]);
M = sub(M,S);
ap1 := idealFromCharacter (S,satpc#0) + M;
ap2 := idealFromCharacter (S,satpc#1) + M;
-- Return two distinct associated primes:
return {ap1,ap2};
)
else return false;
);
-- If the radical is prime, then there still might be embedded
-- primes that properly contain the radical. The remaining part
-- finds such primes by examining quotients w.r.t (maximal)
-- standard monomials.
-- The list of maximally standard monomials:
maxlist := maxNonCellstdm (I,CellVariables=>cv);
-- need to map to R to do colons with I
f := map (R, ring maxlist#0);
maxstdmon := maxlist / f;
for m in maxstdmon do (
q := I:m;
-- Mapping down to k[E]:
-- q2 := eliminate (noncellvars,q);
q2 := q + M;
-- I_+(sigma) was called prerad above:
if not isSubset(q2, rad) then (
-- creating some local names:
satqchar := saturatePChar partialCharacter (q,CellVariables=>cv);
if o#ReturnPChars then(
return {pc, {satqchar#0#"J",satqchar#0#"L",satqchar#0#"c"}}
);
if o#ReturnPrimes then (
F := ring satqchar#0#"c"#0;
S := F(monoid [satqchar#0#"J"]);
M = sub(M,S);
ap2 := idealFromCharacter (S, satqchar);
return {rad, ap2 + M};
)
else return false;
);
);
if o#ReturnPChars then return {pc};
if o#ReturnPrimes then return {rad};
true)
binomialIsPrime = method (Options => {CellVariables=>null})
binomialIsPrime Ideal := Ideal => o -> I -> (
-- A binomial ideal is prime if it is cellular and all its
-- monomial generators have power one and the associated partial
-- character is saturated. (Corollary 2.6 in ES96 )
-- Input: A Binomial Ideal and the cell variables if the ideal is cellular.
-- Output: true if the ideal is a prime ideal, false otherwise
-- test for cellularity:
-- if cellular variables are given then we belive that I is cellular
cv := null;
if o#CellVariables === null then (
cv = isCellular (I, ReturnCellVars=>true);
if cv === false then return false)
else cv = o#CellVariables;
-- Check if non-cellular variables are all contained:
R := ring I;
ncv := toList(set (gens R) - cv); -- nilpotent variables x \notin E
if not isSubset(promote(ideal ncv, R), I) then return false;
-- Test if the partial character saturated:
pc := partialCharacter (I, CellVariables=>cv);
if image Lsat pc#"L" != image pc#"L" then return false;
true)
binomialMinimalPrimes = method (Options => {Verbose=>false})
binomialMinimalPrimes Ideal := Ideal => o -> I -> (
-- Algorithm from "Decompositions of Binomial Ideals" (AISM),
-- based on computing a cellular decomposition of the radical of I.
if not isBinomial I then error "Input was not binomial";
R := ring I;
if I == ideal (1_R) then return {};
ge := gens R;
n := numgens R;
Answer := {};
L := null;
IntersectAnswer := ideal(1_R);
ToDo := {{{1_R},toList(0..n-1),I}};
compo := 0;
next := () -> (
if #ToDo === 0 then false
else (
L = ToDo#0;
ToDo = drop(ToDo,1);
if gens IntersectAnswer % L#2 == 0 then (
if o#Verbose then (
<< "redundant component" << endl;
);
)
else if #(L#1) === 0 then ( -- #(L#1) counts 'remaining variables to check'
compo = compo + 1;
newone := trim L#2;
if o#Verbose then (
<< "components found so far: " << compo << endl;);
Answer = append(Answer,{newone, delete(1_R,L#0)});
IntersectAnswer = intersect(IntersectAnswer,newone);
if IntersectAnswer == I then ToDo = {})
else ( -- So, there are remaining variables #(L#1) is not zero
i := L#1#0; -- i is a variable under consideration
newL1 := drop(L#1,1); -- gets removed from the list
result := axisSaturate(L#2, i); -- compute saturation wrt i
J := result#1; -- Ideal
k := result#0; -- Saturation Exponent
if k > 0 then ( -- If a division was needed:
J2 := L#2 + ideal(R_i);
J2 = saturate(J2, product L#0);
if J2 != ideal(1_R) then
ToDo = prepend({L#0, newL1, J2},ToDo));
if J != ideal(1_R) then (
ToDo = prepend({L#0|{R_i}, newL1, J},ToDo);
);
);
true));
while next() do ();
-- print Answer;
if o#Verbose then print "Decomposition done.";
ncv := {};
i := 0;
j := #Answer;
ME :=ideal; si := ideal; mp := {}; F := null; S:= null;
for a in Answer do (
i = i+1;
if o#Verbose then (
print ("Finding minimal primes of cellular component: " | toString i | " of " | toString j));
ME := ideal(toList(set (gens R) - a#1));
pc := partialCharacter (a#0, CellVariables=>a#1);
-- Check whether we have a radical ideal already:
if image Lsat pc#"L" == image pc#"L" then (
si = {a#0};
)
else (
si = satIdeals pc
);
F = coefficientRing ring si#0;
if F === QQ then S = R else S = F(monoid [ge]);
ME = sub (ME, S);
si = for i in si list sub(i,S);
si = si / (i -> trim (i + ME)); -- Adding monomials;
mp = mp | si;
);
extractInclusionMinimalIdeals (joinCyclotomic mp, Verbose=>o#Verbose))
isBetween = (a,b,c) -> (
-- Checks if a lies between b and c in divisibility order.
-- b and c need not be comparable, or sorted.
if (b%c == 0) then (
-- c divides b
if ( (a%c==0) and (b%a==0)) then return true;
)
else if (c%b == 0) then (
if ( (a%b==0) and (c%a==0)) then return true;
);
-- b and c are not comparable
false)
cellularBinomialAssociatedPrimes = method (Options => {CellVariables => null, Verbose=>false})
cellularBinomialAssociatedPrimes Ideal := Ideal => o -> I -> (
-- Computes the associated primes of cellular binomial ideal
-- It returns them in a polynomial ring with the same variables as ring I,
-- but potentially extended coefficient ring.
R := ring I;
cv := cellVars(I, CellVariables=>o#CellVariables);