1 | //////////////////////////////////////////////////////////////// |
---|
2 | version="version fpaprops.lib 4.1.2.0 Feb_2019 "; // $Id$ |
---|
3 | category="Noncommutative"; |
---|
4 | info=" |
---|
5 | LIBRARY: fpaprops.lib Algorithmic ring-theoretic properties of finitely presented algebras (Letterplace) |
---|
6 | AUTHORS: Karim Abou Zeid, karim.abou.zeid at rwth-aachen.de |
---|
7 | |
---|
8 | Support: Project II.6 in the transregional collaborative research centre |
---|
9 | SFB-TRR 195 'Symbolic Tools in Mathematics and their Application' of the German DFG |
---|
10 | |
---|
11 | OVERVIEW: |
---|
12 | In this library, algorithms for computing various ring-theoretic properties of |
---|
13 | finitely presented algebras are implemented. |
---|
14 | Applicability: Letterplace rings. |
---|
15 | |
---|
16 | REFERENCES: |
---|
17 | Huishi Li: Groebner bases in ring theory. World Scientific, 2010. |
---|
18 | |
---|
19 | KEYWORDS: finitely presented algebra; ring theory; Letterplace Groebner basis; |
---|
20 | growth of algebra; Gelfand-Kirillov dimension; global homological dimension; semi-prime ideal; Ufnarovski graph |
---|
21 | |
---|
22 | SEE ALSO: fpadim_lib, freegb_lib |
---|
23 | |
---|
24 | PROCEDURES: |
---|
25 | lpNoetherian(<GB>); check whether A/<LM(GB)> is (left/right) Noetherian |
---|
26 | lpIsSemiPrime(<GB>); check whether A/<LM(GB)> is semi-prime |
---|
27 | lpIsPrime(<GB>); check whether A/<LM(GB)> is prime |
---|
28 | lpGlDimBound(<GB>); compute an upper bound for the global dimension of A/<GB> |
---|
29 | lpSubstitute(); substitute a variable with polynomials (ring homomorphism) |
---|
30 | lpUfGraph(<GB>); constructs the Ufnarovskij graph for <LM(GB)> |
---|
31 | lpCalcSubstDegBound(); utility for lpSubstitute |
---|
32 | "; |
---|
33 | |
---|
34 | LIB "fpadim.lib"; |
---|
35 | |
---|
36 | /* very fast and cheap test of consistency and functionality |
---|
37 | DO NOT make it static ! |
---|
38 | after adding the new proc, add it here */ |
---|
39 | proc tstfpaprops() |
---|
40 | { |
---|
41 | example lpNoetherian; |
---|
42 | example lpIsSemiPrime; |
---|
43 | example lpIsPrime; |
---|
44 | example lpGlDimBound; |
---|
45 | example lpSubstitute; |
---|
46 | example lpUfGraph; |
---|
47 | example lpCalcSubstDegBound; |
---|
48 | }; |
---|
49 | |
---|
50 | |
---|
51 | //////////////////////////////////////////////////////////////////// |
---|
52 | proc lpNoetherian(ideal G) |
---|
53 | "USAGE: lpNoetherian(G); G an ideal in a Letterplace ring |
---|
54 | RETURN: int |
---|
55 | 0 not Noetherian |
---|
56 | 1 left Noetherian |
---|
57 | 2 right Noetherian |
---|
58 | 3 Noetherian |
---|
59 | 4 weak Noetherian |
---|
60 | PURPOSE: Check whether the monomial algebra A/<LM(G)> is (left/right) noetherian |
---|
61 | ASSUME: - basering is a Letterplace ring |
---|
62 | - G is a Groebner basis |
---|
63 | THEORY: lpNoetherian works with the monomial algebra A/<LM(G)>. |
---|
64 | If it gives an affirmative answer for one of the properties, then it |
---|
65 | holds for both A/<LM(G)> and A/<G>. However, a negative answer applies |
---|
66 | only to A/<LM(G)> and not necessarily to A/<G>. |
---|
67 | NOTE: Weak Noetherian means that two-sided ideals in A/<LM(G)> satisfy |
---|
68 | the acc (ascending chain condition). |
---|
69 | " |
---|
70 | { |
---|
71 | G = lead(G); |
---|
72 | G = simplify(G, 2+4+8); |
---|
73 | |
---|
74 | // check special case 1 |
---|
75 | int l = 0; |
---|
76 | for (int i = 1; i <= size(G); i++) { |
---|
77 | // find the max degree in G |
---|
78 | int d = deg(G[i]); |
---|
79 | if (d > l) { |
---|
80 | l = d; |
---|
81 | } |
---|
82 | |
---|
83 | // also if G is the whole ring |
---|
84 | if (leadmonom(G[i]) == 1) { |
---|
85 | ERROR("noetherianity not defined for 0-ring") |
---|
86 | } |
---|
87 | kill d; |
---|
88 | } kill i; |
---|
89 | // if longest word has length 1 we handle it as a special case |
---|
90 | if (l == 1) { |
---|
91 | int n = lpVarBlockSize(basering); // variable count |
---|
92 | int k = size(G); |
---|
93 | if (k == n) { // only the field left |
---|
94 | return(3); // every field is noetherian |
---|
95 | } |
---|
96 | if (k == n-1) { // V = {1} with loop |
---|
97 | return(3); |
---|
98 | } |
---|
99 | if (k <= n-2) { // V = {1} with more than one loop |
---|
100 | return(0); |
---|
101 | } |
---|
102 | } |
---|
103 | |
---|
104 | intmat UG = lpUfGraph(G); |
---|
105 | |
---|
106 | // check special case 2 |
---|
107 | intmat zero[nrows(UG)][ncols(UG)]; |
---|
108 | if (UG == zero) { |
---|
109 | return (3); |
---|
110 | } |
---|
111 | |
---|
112 | if (!imHasLoops(UG) && imIsUpRightTriangle(topologicalSort(UG))) { |
---|
113 | // UG is a DAG |
---|
114 | return (3); |
---|
115 | } |
---|
116 | |
---|
117 | // DFS from every vertex, if cycle is found, check every vertex for incomming/outcom |
---|
118 | intvec visited; |
---|
119 | visited[ncols(UG)] = 0; |
---|
120 | int inFlag, outFlag, inOutFlag; |
---|
121 | for (int v = 1; v <= ncols(UG) && (inFlag + outFlag) != 3; v++) { |
---|
122 | int inOutFlags = inOutCommingEdgesInCycles(UG, v, visited, 0); |
---|
123 | if (inOutFlags == 1) { |
---|
124 | inFlag = 1; |
---|
125 | } |
---|
126 | if (inOutFlags == 2) { |
---|
127 | outFlag = 1; |
---|
128 | } |
---|
129 | if (inOutFlags == 3) { |
---|
130 | inFlag = 1; |
---|
131 | outFlag = 1; |
---|
132 | } |
---|
133 | if (inOutFlags == 4) { |
---|
134 | inOutFlag = 1; |
---|
135 | } |
---|
136 | if (inOutFlags == 5) { |
---|
137 | inFlag = 1; |
---|
138 | inOutFlag = 1; |
---|
139 | } |
---|
140 | if (inOutFlags == 6) { |
---|
141 | outFlag = 1; |
---|
142 | inOutFlag = 1; |
---|
143 | } |
---|
144 | if (inOutFlags == 7) { |
---|
145 | inFlag = 1; |
---|
146 | outFlag = 1; |
---|
147 | inOutFlag = 1; |
---|
148 | } |
---|
149 | kill inOutFlags; |
---|
150 | } kill v; |
---|
151 | int noetherian = 3 - 1*inFlag - 2*outFlag; |
---|
152 | if (noetherian == 0) { |
---|
153 | return (4 - 4*inOutFlag); // weak noetherian |
---|
154 | } |
---|
155 | return (noetherian); |
---|
156 | } |
---|
157 | example |
---|
158 | { |
---|
159 | "EXAMPLE:"; echo = 2; |
---|
160 | ring r = 0,(x,y),dp; |
---|
161 | def R = freeAlgebra(r, 5); |
---|
162 | setring R; |
---|
163 | ideal G = x*x, y*x; // K<x,y>/<xx,yx> is right noetherian |
---|
164 | lpNoetherian(G); |
---|
165 | } |
---|
166 | |
---|
167 | static proc inOutCommingEdgesInCycles(intmat G, int v, intvec visited, intvec path) { |
---|
168 | // Mark the current vertex as visited |
---|
169 | visited[v] = 1; |
---|
170 | |
---|
171 | // Store the current vertex in path |
---|
172 | if (path[1] == 0) { |
---|
173 | path[1] = v; |
---|
174 | } else { |
---|
175 | path[size(path) + 1] = v; |
---|
176 | } |
---|
177 | |
---|
178 | int inFlag, outFlag, inOutFlag; |
---|
179 | |
---|
180 | for (int w = 1; w <= ncols(G) && (inFlag + outFlag) != 3; w++) { |
---|
181 | if (G[v,w] == 1) { |
---|
182 | if (visited[w] == 1) { // new cycle |
---|
183 | int tmpInFlag; |
---|
184 | int tmpOutFlag; |
---|
185 | if (v == w) { // cycle is a loop |
---|
186 | for (int u = 1; u <= ncols(G); u++) { |
---|
187 | if (G[v,u] && u != v) { |
---|
188 | outFlag = 1; |
---|
189 | tmpOutFlag = 1; |
---|
190 | } |
---|
191 | if (G[u,v] && u != v) { |
---|
192 | inFlag = 1; |
---|
193 | tmpInFlag = 1; |
---|
194 | } |
---|
195 | } kill u; |
---|
196 | } else { |
---|
197 | for (int i = size(path); i >= 1; i--) { // for each vertex in the path |
---|
198 | // check for neighbors not directly next or prev in cycle |
---|
199 | for (int u = 1; u <= ncols(G); u++) { |
---|
200 | if (G[path[i],u] == 1) { // there is an edge to u |
---|
201 | if (path[i] != v) { |
---|
202 | if (u != path[i+1]) { // and u is not the next element in the cycle |
---|
203 | outFlag = 1; |
---|
204 | tmpOutFlag = 1; |
---|
205 | } |
---|
206 | } else { |
---|
207 | if (u != w) { |
---|
208 | outFlag = 1; |
---|
209 | tmpOutFlag = 1; |
---|
210 | } |
---|
211 | } |
---|
212 | } |
---|
213 | if (G[u,path[i]] == 1) { // there is an edge from u |
---|
214 | if (path[i] != w) { |
---|
215 | if (u != path[i-1]) { // and u is not the previous element in the cylce |
---|
216 | inFlag = 1; |
---|
217 | tmpInFlag = 1; |
---|
218 | } |
---|
219 | } else { |
---|
220 | if (u != v) { |
---|
221 | inFlag = 1; |
---|
222 | tmpInFlag = 1; |
---|
223 | } |
---|
224 | } |
---|
225 | } |
---|
226 | } kill u; |
---|
227 | if (path[i] == w) { |
---|
228 | break; |
---|
229 | } |
---|
230 | } kill i; |
---|
231 | } |
---|
232 | if (tmpInFlag > 0 && tmpOutFlag > 0) { |
---|
233 | // there are both in and outcomming edges in this cycle |
---|
234 | inOutFlag = 1; |
---|
235 | } |
---|
236 | kill tmpInFlag; |
---|
237 | kill tmpOutFlag; |
---|
238 | } else { |
---|
239 | int inOutFlags = inOutCommingEdgesInCycles(G, w, visited, path); |
---|
240 | if (inOutFlags == 1) { |
---|
241 | inFlag = 1; |
---|
242 | } |
---|
243 | if (inOutFlags == 2) { |
---|
244 | outFlag = 1; |
---|
245 | } |
---|
246 | if (inOutFlags == 3) { |
---|
247 | inFlag = 1; |
---|
248 | outFlag = 1; |
---|
249 | } |
---|
250 | if (inOutFlags == 4) { |
---|
251 | inOutFlag = 1; |
---|
252 | } |
---|
253 | if (inOutFlags == 5) { |
---|
254 | inFlag = 1; |
---|
255 | inOutFlag = 1; |
---|
256 | } |
---|
257 | if (inOutFlags == 6) { |
---|
258 | outFlag = 1; |
---|
259 | inOutFlag = 1; |
---|
260 | } |
---|
261 | if (inOutFlags == 7) { |
---|
262 | inFlag = 1; |
---|
263 | outFlag = 1; |
---|
264 | inOutFlag = 1; |
---|
265 | } |
---|
266 | kill inOutFlags; |
---|
267 | } |
---|
268 | } |
---|
269 | } kill w; |
---|
270 | |
---|
271 | return (1*inFlag + 2*outFlag + 4*inOutFlag); |
---|
272 | } |
---|
273 | |
---|
274 | proc lpIsSemiPrime(ideal G) |
---|
275 | "USAGE: lpIsSemiPrime(G); G an ideal in a Letterplace ring |
---|
276 | RETURN: boolean |
---|
277 | PURPOSE: Check whether A/<LM(G)> is semi-prime ring, |
---|
278 | alternatively whether <LM(G)> is a semi-prime ideal in A. |
---|
279 | ASSUME: - basering is a Letterplace ring |
---|
280 | - G is a Groebner basis |
---|
281 | THEORY: A (two-sided) ideal I in the ring A is semi-prime, if for any a in A one has |
---|
282 | aAa subseteq I implies a in I. |
---|
283 | NOTE: lpIsSemiPrime works with the monomial algebra A/<LM(G)>. |
---|
284 | A positive answer holds for both A/<LM(G)> and A/<G>, while |
---|
285 | a negative answer applies only to A/<LM(G)> and not necessarily to |
---|
286 | A/<G>. |
---|
287 | " |
---|
288 | { |
---|
289 | // old theory part: that is when p * (A/<LM(G)>) * p != 0 for all p in (A/<LM(G)> - {0}). |
---|
290 | G = lead(G); |
---|
291 | G = simplify(G, 2+4+8); |
---|
292 | |
---|
293 | // check special case 1 |
---|
294 | int l = 0; |
---|
295 | for (int i = 1; i <= size(G); i++) { |
---|
296 | // find the max degree in G |
---|
297 | int d = deg(G[i]); |
---|
298 | if (d > l) { |
---|
299 | l = d; |
---|
300 | } |
---|
301 | |
---|
302 | // also if G is the whole ring |
---|
303 | if (leadmonom(G[i]) == 1) { |
---|
304 | ERROR("primeness not defined for 0-ring") |
---|
305 | } |
---|
306 | kill d; |
---|
307 | } kill i; |
---|
308 | // if longest word has length 1 we handle it as a special case |
---|
309 | if (l == 1) { |
---|
310 | return(1); |
---|
311 | } |
---|
312 | |
---|
313 | list VUG = lpUfGraph(G, 1); |
---|
314 | intmat UG = VUG[1]; // the Ufnarovskij graph |
---|
315 | ideal V = VUG[2]; // the vertices of UG (standard words with length = l-1) |
---|
316 | |
---|
317 | list LG = lpId2ivLi(G); |
---|
318 | list SW = ivStandardWordsUpToLength(LG, maxDeg(G)); |
---|
319 | list LV = lpId2ivLi(V); |
---|
320 | |
---|
321 | // delete the 0 in SW |
---|
322 | int indexofzero = ivIndexOf(SW, 0); |
---|
323 | if (indexofzero > 0) { // should be always true when |SW| > 0 |
---|
324 | SW = delete(SW, indexofzero); |
---|
325 | } |
---|
326 | |
---|
327 | // check if each monomial in SW is cyclic |
---|
328 | for (int i = 1; i <= size(SW); i++) { |
---|
329 | if (!isCyclicInUfGraph(UG, LV, SW[i])) { |
---|
330 | return (0); |
---|
331 | } |
---|
332 | } kill i; |
---|
333 | |
---|
334 | return (1); |
---|
335 | } |
---|
336 | example |
---|
337 | { |
---|
338 | "EXAMPLE:"; echo = 2; |
---|
339 | ring r = 0,(x1,x2),dp; |
---|
340 | def R = freeAlgebra(r, 5); |
---|
341 | setring R; |
---|
342 | ideal G = x1*x2, x2*x1; // K<x1,x2>/<x1*x2,x2*x1> is semi prime |
---|
343 | lpIsSemiPrime(G); |
---|
344 | } |
---|
345 | |
---|
346 | // checks whether a monomial is a cyclic monomial |
---|
347 | static proc isCyclicInUfGraph(intmat UG, list LV, intvec u) |
---|
348 | { |
---|
349 | if (ncols(UG) == 0) {return (0);} // UG is empty |
---|
350 | if (u == 0) {return (0);} // 0 is never cyclic |
---|
351 | |
---|
352 | int l = size(LV[1]) + 1; |
---|
353 | |
---|
354 | int s = size(u); |
---|
355 | if (s <= l - 1) { |
---|
356 | for (int i = 1; i <= size(LV); i++) { |
---|
357 | // for all vertices where u is a suffix |
---|
358 | if(isSF(u, LV[i])) { |
---|
359 | if (existsRoute(UG, i, i)) { |
---|
360 | return (1); |
---|
361 | } |
---|
362 | } |
---|
363 | } kill i; |
---|
364 | } else { // size(u) > l - 1 |
---|
365 | int m = s - l + 1; |
---|
366 | |
---|
367 | // there must be a route from v0 to vm |
---|
368 | intvec v0 = u[1..(l-1)]; // first in route of u |
---|
369 | intvec vm = u[m+1..m+(l-1)]; // last in route of u |
---|
370 | |
---|
371 | int iv0 = ivIndexOf(LV, v0); |
---|
372 | int ivm = ivIndexOf(LV, vm); |
---|
373 | if (iv0 <= 0 || ivm <= 0) { |
---|
374 | ERROR("u is not a standard word"); |
---|
375 | } |
---|
376 | |
---|
377 | return (existsRoute(UG, ivm, iv0)); |
---|
378 | } |
---|
379 | |
---|
380 | return (0); |
---|
381 | } |
---|
382 | |
---|
383 | proc lpIsPrime(ideal G) |
---|
384 | "USAGE: lpIsPrime(G); G an ideal in a Letterplace ring |
---|
385 | RETURN: boolean |
---|
386 | PURPOSE: Check whether A/<LM(G)> is prime ring, |
---|
387 | alternatively whether <LM(G)> is a prime ideal in A. |
---|
388 | ASSUME: - basering is a Letterplace ring |
---|
389 | - G is a Groebner basis |
---|
390 | THEORY: A (two-sided) ideal I in the ring A is prime, if for any a,b in A one has |
---|
391 | aAb subseteq I implies a in I or b in I. |
---|
392 | NOTE: lpIsPrime works with the monomial algebra A/<LM(G)>. |
---|
393 | A positive answer holds for both A/<LM(G)> and A/<G>, while |
---|
394 | a negative answer applies only to A/<LM(G)> and not necessarily to A/<G>. |
---|
395 | " |
---|
396 | { |
---|
397 | // old theory part: that is when p1 * (A/<LM(G)>) * p2 != 0 for all p1, p2 in (A/<LM(G)> - {0}). |
---|
398 | G = lead(G); |
---|
399 | G = simplify(G, 2+4+8); |
---|
400 | |
---|
401 | // check special case 1 |
---|
402 | int l = 0; |
---|
403 | for (int i = 1; i <= size(G); i++) { |
---|
404 | // find the max degree in G |
---|
405 | int d = deg(G[i]); |
---|
406 | if (d > l) { |
---|
407 | l = d; |
---|
408 | } |
---|
409 | |
---|
410 | // also if G is the whole ring |
---|
411 | if (leadmonom(G[i]) == 1) { |
---|
412 | ERROR("primeness not defined for 0-ring") |
---|
413 | } |
---|
414 | kill d; |
---|
415 | } kill i; |
---|
416 | // if longest word has length 1 we handle it as a special case |
---|
417 | if (l == 1) { |
---|
418 | return(1); |
---|
419 | } |
---|
420 | |
---|
421 | list VUG = lpUfGraph(G, 1); |
---|
422 | intmat UG = VUG[1]; // the Ufnarovskij graph |
---|
423 | ideal V = VUG[2]; // the vertices of UG (standard words with length = l-1) |
---|
424 | |
---|
425 | list LG = lpId2ivLi(G); |
---|
426 | list LV = lpId2ivLi(V); |
---|
427 | |
---|
428 | int n = ncols(UG); |
---|
429 | |
---|
430 | // 1) for each vi vj there exists a route from vi to vj (means UG is connected) |
---|
431 | for (int i = 1; i <= n; i++) { |
---|
432 | for (int j = 1; j <= n; j++) { |
---|
433 | if (!existsRoute(UG, i, j)) { |
---|
434 | return (0); |
---|
435 | } |
---|
436 | } kill j; |
---|
437 | } kill i; |
---|
438 | |
---|
439 | // 2) any standard word with length < l-1 is a suffix of a vertex |
---|
440 | list SW = ivStandardWordsUpToLength(LG, maxDeg(G) - 2); // < maxDeg - 1 |
---|
441 | if (size(SW) > 0 && size(LV) == 0) {return (0);} |
---|
442 | for (int i = 1; i <= size(SW); i++) { |
---|
443 | // check if SW[i] is a suffix of some LV |
---|
444 | for (int j = 1; j <= size(LV); j++) { |
---|
445 | if (!isSF(SW[i], LV[j])) { |
---|
446 | if (j == size(LV)) { |
---|
447 | return (0); |
---|
448 | } |
---|
449 | } else { |
---|
450 | break; |
---|
451 | } |
---|
452 | } |
---|
453 | } kill i; |
---|
454 | |
---|
455 | return (1); |
---|
456 | } |
---|
457 | example |
---|
458 | { |
---|
459 | "EXAMPLE:"; echo = 2; |
---|
460 | ring r = 0,(x,y),dp; |
---|
461 | def R = freeAlgebra(r, 5); |
---|
462 | setring R; |
---|
463 | ideal G = x*x, y*y; // K<x,y>/<xx,yy> is prime |
---|
464 | lpIsPrime(G); |
---|
465 | } |
---|
466 | |
---|
467 | static proc existsRoute(intmat G, int v, int u, list #) |
---|
468 | "USAGE: existsRoute(G,v,u); G a graph, v and u vertices |
---|
469 | NOTE: don't pass anything to # (internal use for recursion) |
---|
470 | routes always have at least one edge |
---|
471 | " |
---|
472 | { |
---|
473 | int n = ncols(G); |
---|
474 | |
---|
475 | // init visited |
---|
476 | intvec visited; |
---|
477 | if (size(#) > 0) { |
---|
478 | if (v == u) {return (1);} // don't check on first call so |route| >= 1 holds |
---|
479 | visited = #[1]; |
---|
480 | } else { // first call |
---|
481 | visited[n] = 0; |
---|
482 | } |
---|
483 | |
---|
484 | // mark current vertex as visited |
---|
485 | visited[v] = 1; |
---|
486 | |
---|
487 | // recursive DFS |
---|
488 | for (int i = 1; i <= n; i++) { |
---|
489 | if (G[v,i] && (!visited[i] || i == u)) { // i == u to allow routes from u to u |
---|
490 | if (existsRoute(G, i, u, visited)) { |
---|
491 | return (1); |
---|
492 | } |
---|
493 | } |
---|
494 | } kill i; |
---|
495 | |
---|
496 | return (0); |
---|
497 | } |
---|
498 | |
---|
499 | static proc UfGraphURTNZDGrowth(intmat UG) { |
---|
500 | // URTNZD = upper right triangle non zero diagonal |
---|
501 | for (int i = 1; i <= ncols(UG); i++) { |
---|
502 | UG[i,i] = 0; // remove all loops |
---|
503 | } kill i; |
---|
504 | intmat UGk = UG; |
---|
505 | intmat zero[nrows(UGk)][ncols(UGk)]; |
---|
506 | int k = 1; |
---|
507 | while (UGk != zero) { |
---|
508 | UGk = UGk * UG; |
---|
509 | k++; |
---|
510 | } |
---|
511 | return (k); |
---|
512 | } |
---|
513 | |
---|
514 | static proc imIsUpRightTriangle(intmat M) { |
---|
515 | for (int i = 1; i <= nrows(M); i++) { |
---|
516 | for (int j = 1; j < i; j++) { |
---|
517 | if(M[i,j] != 0) { return (0); } |
---|
518 | } kill j; |
---|
519 | } kill i; |
---|
520 | return (1); |
---|
521 | } |
---|
522 | |
---|
523 | static proc eliminateZerosUpTriangle(intmat G) { |
---|
524 | // G is expected to be an upper triangle matrix |
---|
525 | for (int i = ncols(G); i >= 1; i--) { // loop order is important because we delete entries |
---|
526 | if (G[i,i] == 0) { // i doesn't have a cycle |
---|
527 | for (int j = 1; j < i; j++) { |
---|
528 | if (G[j,i] == 1) { // j has an edge to i |
---|
529 | for (int k = i + 1; k <= nrows(G); k++) { |
---|
530 | if (G[i,k] == 1) { |
---|
531 | G[j,k] = G[i,k]; // give j all edges from i |
---|
532 | } |
---|
533 | } kill k; |
---|
534 | } |
---|
535 | } kill j; |
---|
536 | G = imDelRowCol(G,i,i); // remove vertex i |
---|
537 | } |
---|
538 | } kill i; |
---|
539 | return (G); |
---|
540 | } |
---|
541 | |
---|
542 | static proc imDelRowCol(intmat M, int row, int col) { |
---|
543 | // row and col are expected to be > 0 |
---|
544 | int nr = nrows(M); |
---|
545 | int nc = ncols(M); |
---|
546 | intmat Mdel[nr - 1][nc - 1]; |
---|
547 | for (int i = 1; i <= nr; i++) { |
---|
548 | for (int j = 1; j <= nc; j++) { |
---|
549 | if(i != row && j != col) { |
---|
550 | int newi = i; |
---|
551 | int newj = j; |
---|
552 | if (i > row) { newi = i - 1; } |
---|
553 | if (j > col) { newj = j - 1; } |
---|
554 | Mdel[newi,newj] = M[i,j]; |
---|
555 | kill newi; kill newj; |
---|
556 | } |
---|
557 | } kill j; |
---|
558 | } kill i; |
---|
559 | return (Mdel); |
---|
560 | } |
---|
561 | |
---|
562 | static proc topologicalSort(intmat G) { |
---|
563 | // NOTE: ignores loops |
---|
564 | // NOTE: this takes O(|V^3|), can be optimized |
---|
565 | int n = ncols(G); |
---|
566 | for (int i = 1; i <= n; i++) { // only use the submat at i |
---|
567 | // find a vertex v in the submat at i with no incoming edges |
---|
568 | int v; |
---|
569 | for (int j = i; j <= n; j++) { |
---|
570 | int incoming = 0; |
---|
571 | for (int k = i; k <= n; k++) { |
---|
572 | if (k != j && G[k,j] == 1) { |
---|
573 | incoming = 1; |
---|
574 | } |
---|
575 | } kill k; |
---|
576 | if (incoming == 0) { |
---|
577 | v = j; |
---|
578 | kill incoming; |
---|
579 | break; |
---|
580 | } else { |
---|
581 | if (j == n) { |
---|
582 | // G contains at least one cycle, abort |
---|
583 | return (G); |
---|
584 | } |
---|
585 | } |
---|
586 | kill incoming; |
---|
587 | } kill j; |
---|
588 | |
---|
589 | // swap v and i |
---|
590 | if (v != i) { |
---|
591 | G = imPermcol(G, v, i); |
---|
592 | G = imPermrow(G, v, i); |
---|
593 | } |
---|
594 | kill v; |
---|
595 | } kill i; |
---|
596 | return (G); |
---|
597 | } |
---|
598 | |
---|
599 | static proc imPermcol (intmat A, int c1, int c2) |
---|
600 | { |
---|
601 | intmat B = A; |
---|
602 | int k = nrows(B); |
---|
603 | B[1..k,c1] = A[1..k,c2]; |
---|
604 | B[1..k,c2] = A[1..k,c1]; |
---|
605 | return (B); |
---|
606 | } |
---|
607 | |
---|
608 | static proc imPermrow (intmat A, int r1, int r2) |
---|
609 | { |
---|
610 | intmat B = A; |
---|
611 | int k = ncols(B); |
---|
612 | B[r1,1..k] = A[r2,1..k]; |
---|
613 | B[r2,1..k] = A[r1,1..k]; |
---|
614 | return (B); |
---|
615 | } |
---|
616 | |
---|
617 | static proc UfGraphGrowth(intmat UG) |
---|
618 | { |
---|
619 | int n = ncols(UG); // number of vertices |
---|
620 | // iterate through all vertices |
---|
621 | |
---|
622 | intvec visited = 0:n; |
---|
623 | intvec cyclic = 0:n; |
---|
624 | intvec countedCycles = -2:n; |
---|
625 | |
---|
626 | int maxCycleCount = 0; |
---|
627 | for (int v = 1; v <= n; v++) { |
---|
628 | countedCycles = countCycles(UG, v, visited, cyclic, 0, countedCycles); |
---|
629 | dbprint("counted " + string(countedCycles[v]) + " cycles from vertex " + string(v) + "/" + string(n) + " (cache: " + string(countedCycles) + ")"); |
---|
630 | if (countedCycles[v] == -1) { |
---|
631 | return(-1); |
---|
632 | } |
---|
633 | if (countedCycles[v] > maxCycleCount) { |
---|
634 | maxCycleCount = countedCycles[v]; |
---|
635 | } |
---|
636 | } kill v; |
---|
637 | return (maxCycleCount); |
---|
638 | } |
---|
639 | |
---|
640 | static proc countCycles(intmat G, int v, intvec visited, intvec cyclic, intvec path, intvec countedCycles) |
---|
641 | "USAGE: countCycles(G, v, visited, cyclic, path); G a Graph, v the vertex to |
---|
642 | start. The parameter visited, cyclic and path should be 0. |
---|
643 | RETURN: int |
---|
644 | Maximal number of distinct cycles |
---|
645 | PURPOSE: Calculate the maximal number of distinct cycles in a single path starting at v |
---|
646 | ASSUME: Basering is a Letterplace ring |
---|
647 | " |
---|
648 | { |
---|
649 | if (countedCycles[v] > -2) { |
---|
650 | return (countedCycles); |
---|
651 | } |
---|
652 | // Mark the current vertex as visited |
---|
653 | visited[v] = 1; |
---|
654 | |
---|
655 | // Store the current vertex in path |
---|
656 | if (path[1] == 0) { |
---|
657 | path[1] = v; |
---|
658 | } else { |
---|
659 | path[size(path) + 1] = v; |
---|
660 | } |
---|
661 | |
---|
662 | int cycles = 0; |
---|
663 | for (int w = 1; w <= ncols(G); w++) { |
---|
664 | if (G[v,w] == 1) { |
---|
665 | if (visited[w] == 1) { // found new cycle |
---|
666 | // 1. for all vertices in path until w, check if they are cyclic |
---|
667 | for (int j = size(path); j >= 1; j--) { |
---|
668 | if(cyclic[path[j]] == 1) { |
---|
669 | // 1.1 if yes, return -1 |
---|
670 | countedCycles[v] = -1; |
---|
671 | return (countedCycles); |
---|
672 | } |
---|
673 | if (path[j] == w) { |
---|
674 | break; |
---|
675 | } |
---|
676 | } kill j; |
---|
677 | |
---|
678 | // 2. otherwise cycles++ |
---|
679 | for (int j = size(path); j >= 1; j--) { |
---|
680 | // 2.2 remove the edges from that cycle and mark the vertices as cyclic |
---|
681 | if (j == size(path)) { // special case in the first iteration |
---|
682 | cyclic[v] = 1; |
---|
683 | G[v, w] = 0; |
---|
684 | } else { |
---|
685 | cyclic[path[j]] = 1; |
---|
686 | G[path[j], path[j+1]] = 0; |
---|
687 | } |
---|
688 | if (path[j] == w) { |
---|
689 | break; |
---|
690 | } |
---|
691 | } kill j; |
---|
692 | |
---|
693 | // 3. countCycles() on all these vertices |
---|
694 | int maxCycleCount = 0; |
---|
695 | for (int j = size(path); j >= 1; j--) { |
---|
696 | countedCycles = countCycles(G, path[j], visited, cyclic, path, countedCycles); |
---|
697 | if(countedCycles[path[j]] == -1) { |
---|
698 | countedCycles[v] = -1; |
---|
699 | return (countedCycles); |
---|
700 | } |
---|
701 | if (countedCycles[path[j]] > maxCycleCount) { |
---|
702 | maxCycleCount = countedCycles[path[j]]; |
---|
703 | } |
---|
704 | if (path[j] == w) { |
---|
705 | break; |
---|
706 | } |
---|
707 | } kill j; |
---|
708 | if (maxCycleCount >= cycles) { |
---|
709 | cycles = maxCycleCount + 1; |
---|
710 | } |
---|
711 | kill maxCycleCount; |
---|
712 | } else { |
---|
713 | countedCycles = countCycles(G, w, visited, cyclic, path, countedCycles); |
---|
714 | if (countedCycles[w] == -1) { |
---|
715 | countedCycles[v] = -1; |
---|
716 | return (countedCycles); |
---|
717 | } |
---|
718 | if (countedCycles[w] > cycles) { |
---|
719 | cycles = countedCycles[w]; |
---|
720 | } |
---|
721 | } |
---|
722 | } |
---|
723 | } kill w; |
---|
724 | countedCycles[v] = cycles; |
---|
725 | return (countedCycles); |
---|
726 | } |
---|
727 | |
---|
728 | proc lpUfGraph(ideal G, list #) |
---|
729 | "USAGE: lpUfGraph(G); G a set of monomials in a letterplace ring. |
---|
730 | RETURN: intmat or list |
---|
731 | NOTE: lpUfGraph(G); returns intmat. lpUfGraph(G,1); returns list L with L[1] an intmat and L[2] an ideal. |
---|
732 | The intmat is the Ufnarovskij Graph and the ideal contains the vertices. |
---|
733 | PURPOSE: Constructs the Ufnarovskij graph induced by G |
---|
734 | the adjacency matrix of the Ufnarovskij graph induced by G |
---|
735 | ASSUME: - basering is a Letterplace ring |
---|
736 | - G are the leading monomials of a Groebner basis |
---|
737 | " |
---|
738 | { |
---|
739 | dbprint("computing maxDeg"); |
---|
740 | int l = maxDeg(G); |
---|
741 | if (l - 1 == 0) { |
---|
742 | // TODO: how should the graph look like when l - 1 = 0 ? |
---|
743 | ERROR("Ufnarovskij graph not implemented for l = 1"); |
---|
744 | } |
---|
745 | int lV = lpVarBlockSize(basering); |
---|
746 | // TODO: what if l <= 0? |
---|
747 | dbprint("computing standard words"); |
---|
748 | ideal SW = lpStandardWords(G, l - 1); // vertices |
---|
749 | int n = ncols(SW); |
---|
750 | dbprint("n = " + string(n)); |
---|
751 | intmat UG[n][n]; // Ufnarovskij graph |
---|
752 | for (int i = 1; i <= n; i++) { |
---|
753 | for (int j = 1; j <= n; j++) { |
---|
754 | dbprint("Ufnarovskii graph: " + string((i-1)*n + j) + "/" + string(n*n) + " entries"); |
---|
755 | // [Studzinski page 76] |
---|
756 | poly v = SW[i]; |
---|
757 | poly w = SW[j]; |
---|
758 | intvec v_overlap; |
---|
759 | intvec w_overlap; |
---|
760 | if (l - 1 > 1) { |
---|
761 | v_overlap = leadexp(v); |
---|
762 | w_overlap = leadexp(w); |
---|
763 | v_overlap = v_overlap[(lV+1) .. (l-1)*lV]; |
---|
764 | w_overlap = w_overlap[1 .. (l-2)*lV]; |
---|
765 | } |
---|
766 | if (v_overlap == w_overlap && !lpLmDivides(G, v * lpVarAt(w, l - 1))) { |
---|
767 | UG[i,j] = 1; |
---|
768 | } |
---|
769 | kill v; kill w; kill v_overlap; kill w_overlap; |
---|
770 | } kill j; |
---|
771 | } kill i; |
---|
772 | if (size(#) > 0) { |
---|
773 | if (typeof(#[1]) == "int") { |
---|
774 | if (#[1] != 0) { |
---|
775 | list ret = UG; |
---|
776 | ret[2] = SW; // the vertices |
---|
777 | return (ret); |
---|
778 | } |
---|
779 | } |
---|
780 | } |
---|
781 | return (UG); |
---|
782 | } |
---|
783 | example |
---|
784 | { |
---|
785 | "EXAMPLE:"; echo = 2; |
---|
786 | ring r = 0,(x,y,z),dp; |
---|
787 | def R = freeAlgebra(r, 5); // constructs a Letterplace ring |
---|
788 | setring R; // sets basering to Letterplace ring |
---|
789 | ideal I = x*y, x*z, z*y, z*z; |
---|
790 | lpUfGraph(I); |
---|
791 | lpUfGraph(I,1); |
---|
792 | } |
---|
793 | |
---|
794 | static proc maxDeg(ideal G) |
---|
795 | { |
---|
796 | int l = 0; |
---|
797 | for (int i = 1; i <= size(G); i++) { // find the max degree in G |
---|
798 | int d = deg(G[i]); |
---|
799 | if (d > l) { |
---|
800 | l = d; |
---|
801 | } |
---|
802 | kill d; |
---|
803 | } kill i; |
---|
804 | return (l); |
---|
805 | } |
---|
806 | |
---|
807 | static proc lpStandardWords(ideal G, int length) |
---|
808 | "ASSUME: G is simplified |
---|
809 | " |
---|
810 | { |
---|
811 | if (length < 0) { |
---|
812 | return (delete(ideal(0), 1)); // no standard words |
---|
813 | } |
---|
814 | |
---|
815 | ideal words = maxideal(length); |
---|
816 | for (int i = ncols(words); i >= 1; i--) { |
---|
817 | if (lpLmDivides(G, words[i])) { |
---|
818 | words = delete(words, i); |
---|
819 | } |
---|
820 | } kill i; |
---|
821 | return (words); |
---|
822 | } |
---|
823 | |
---|
824 | static proc ivStandardWords(list G, int length) |
---|
825 | "ASSUME: G is simplified |
---|
826 | " |
---|
827 | { |
---|
828 | if (length <= 0) { |
---|
829 | list words; |
---|
830 | if (length == 0 && !ivdivides(G,0)) { |
---|
831 | words[1] = 0; // iv = 0 means monom = 1 |
---|
832 | } |
---|
833 | return (words); // no standard words |
---|
834 | } |
---|
835 | int lV = lpVarBlockSize(basering); // variable count |
---|
836 | list prevWords = ivStandardWords(G, length - 1); |
---|
837 | list words; |
---|
838 | for (int i = 1; i <= lV; i++) { |
---|
839 | for (int j = 1; j <= size(prevWords); j++) { |
---|
840 | intvec word = prevWords[j]; |
---|
841 | word[length] = i; |
---|
842 | // assumes that G is simplified! |
---|
843 | if (!ivdivides(G, word)) { |
---|
844 | words = insert(words, word); |
---|
845 | } |
---|
846 | kill word; |
---|
847 | } kill j; |
---|
848 | } kill i; |
---|
849 | return (words); |
---|
850 | } |
---|
851 | |
---|
852 | static proc ivStandardWordsUpToLength(list G, int length) |
---|
853 | "ASSUME: G is simplified |
---|
854 | " |
---|
855 | { |
---|
856 | list words = ivStandardWords(G,0); |
---|
857 | if (size(words) == 0) {return (words)} |
---|
858 | for (int i = 1; i <= length; i++) { |
---|
859 | words = words + ivStandardWords(G, i); |
---|
860 | } kill i; |
---|
861 | return (words); |
---|
862 | } |
---|
863 | |
---|
864 | static proc ivdivides(list G, intvec iv) { |
---|
865 | for (int k = 1; k <= size(G); k++) { |
---|
866 | if (isIF(G[k], iv)) { |
---|
867 | return (1); |
---|
868 | } else { |
---|
869 | if (k == size(G)) { |
---|
870 | return (0); |
---|
871 | } |
---|
872 | } |
---|
873 | } kill k; |
---|
874 | return (0); |
---|
875 | } |
---|
876 | |
---|
877 | // lpGkDim now implemented in dynamic module |
---|
878 | /* proc lpGkDim(ideal G) */ |
---|
879 | /* "USAGE: lpGkDim(G); G an ideal in a letterplace ring */ |
---|
880 | /* RETURN: int */ |
---|
881 | /* PURPOSE: Determines the Gelfand Kirillov dimension of A/<G> */ |
---|
882 | /* -1 means positive infinite */ |
---|
883 | /* ASSUME: - basering is a Letterplace ring */ |
---|
884 | /* - G is a Groebner basis */ |
---|
885 | /* " */ |
---|
886 | /* { */ |
---|
887 | /* G = lead(G); */ |
---|
888 | /* G = simplify(G, 2+4+8); */ |
---|
889 | |
---|
890 | /* // check special case 1 */ |
---|
891 | /* int l = 0; */ |
---|
892 | /* for (int i = 1; i <= size(G); i++) { */ |
---|
893 | /* // find the max degree in G */ |
---|
894 | /* int d = deg(G[i]); */ |
---|
895 | /* if (d > l) { */ |
---|
896 | /* l = d; */ |
---|
897 | /* } */ |
---|
898 | |
---|
899 | /* // also if G is the whole ring return minus infinity */ |
---|
900 | /* if (leadmonom(G[i]) == 1) { */ |
---|
901 | /* ERROR("GK-Dim not defined for 0-ring") */ |
---|
902 | /* } */ |
---|
903 | /* kill d; */ |
---|
904 | /* } kill i; */ |
---|
905 | /* // if longest word has length 1, or G is the zero ideal, we handle it as a special case */ |
---|
906 | /* if (l == 1 || size(G) == 0) { */ |
---|
907 | /* int n = lpVarBlockSize(basering); // variable count */ |
---|
908 | /* int k = size(G); */ |
---|
909 | /* if (k == n) { // V = {1} no edges */ |
---|
910 | /* return(0); */ |
---|
911 | /* } */ |
---|
912 | /* if (k == n-1) { // V = {1} with loop */ |
---|
913 | /* return(1); */ |
---|
914 | /* } */ |
---|
915 | /* if (k <= n-2) { // V = {1} with more than one loop */ |
---|
916 | /* return(-1); */ |
---|
917 | /* } */ |
---|
918 | /* } */ |
---|
919 | |
---|
920 | /* dbprint("computing Ufnarovskii graph"); */ |
---|
921 | /* intmat UG = lpUfGraph(G); */ |
---|
922 | /* if (printlevel >= voice + 1) { */ |
---|
923 | /* UG; */ |
---|
924 | /* } */ |
---|
925 | |
---|
926 | /* // check special case 2 */ |
---|
927 | /* intmat zero[nrows(UG)][ncols(UG)]; */ |
---|
928 | /* if (UG == zero) { */ |
---|
929 | /* return (0); */ |
---|
930 | /* } */ |
---|
931 | |
---|
932 | /* // check special case 3 */ |
---|
933 | /* dbprint("topological sorting of Ufnarovskii graph"); */ |
---|
934 | /* UG = topologicalSort(UG); */ |
---|
935 | /* if (printlevel >= voice + 1) { */ |
---|
936 | /* UG; */ |
---|
937 | /* } */ |
---|
938 | |
---|
939 | /* dbprint("check if Ufnarovskii graph is DAG"); */ |
---|
940 | /* if (imIsUpRightTriangle(UG)) { */ |
---|
941 | /* UG = eliminateZerosUpTriangle(UG); */ |
---|
942 | /* if (ncols(UG) == 0 || nrows(UG) == 0) { // when the diagonal was zero */ |
---|
943 | /* return (0) */ |
---|
944 | /* } */ |
---|
945 | /* dbprint("DAG detected, using URTNZD growth alg"); */ |
---|
946 | /* return(UfGraphURTNZDGrowth(UG)); */ |
---|
947 | /* } */ |
---|
948 | |
---|
949 | /* // otherwise count cycles in the Ufnarovskij Graph */ |
---|
950 | /* dbprint("not a DAG, using regular growth alg"); */ |
---|
951 | /* return(UfGraphGrowth(UG)); */ |
---|
952 | /* } */ |
---|
953 | /* example */ |
---|
954 | /* { */ |
---|
955 | /* "EXAMPLE:"; echo = 2; */ |
---|
956 | /* ring r = 0,(x,y,z),dp; */ |
---|
957 | /* def R = freeAlgebra(r, 5); // constructs a Letterplace ring */ |
---|
958 | /* R; */ |
---|
959 | /* setring R; // sets basering to Letterplace ring */ |
---|
960 | /* ideal I = z;//an example of infinite GK dimension */ |
---|
961 | /* lpGkDim(I); */ |
---|
962 | /* I = x,y,z; // gkDim = 0 */ |
---|
963 | /* lpGkDim(I); */ |
---|
964 | /* I = x*y, x*z, z*y, z*z;//gkDim = 2 */ |
---|
965 | /* lpGkDim(I); */ |
---|
966 | /* ideal G = y*x - x*y, z*x - x*z, z*y - y*z; G = std(G); */ |
---|
967 | /* G; */ |
---|
968 | /* lpGkDim(G); // 3, as expected for K[x,y,z] */ |
---|
969 | /* } */ |
---|
970 | |
---|
971 | proc lpGlDimBound(ideal G) |
---|
972 | "USAGE: lpGlDimBound(I); I an ideal |
---|
973 | RETURN: int, an upper bound for the global dimension, -1 means infinity |
---|
974 | PURPOSE: computing an upper bound for the global dimension |
---|
975 | ASSUME: - basering is a Letterplace ring, G is a reduced Groebner Basis |
---|
976 | EXAMPLE: example lpGlDimBound; shows example |
---|
977 | NOTE: if I = LM(I), then the global dimension is equal the Gelfand |
---|
978 | Kirillov dimension if it is finite |
---|
979 | Global dimension should be 0 for A/G = K and 1 for A/G = K<x1...xn> |
---|
980 | " |
---|
981 | { |
---|
982 | G = simplify(G,2); // remove zero generators |
---|
983 | // NOTE: Gl should be 0 for A/G = K and 1 for A/G = K<x1...xn> |
---|
984 | // G1 contains generators with single variable in LM |
---|
985 | ideal G1; |
---|
986 | for (int i = 1; i <= size(G); i++) { |
---|
987 | if (ord(G[i]) < 2) { // single variable in LM |
---|
988 | G1 = insertGenerator(G1,G[i]); |
---|
989 | } |
---|
990 | } kill i; |
---|
991 | G1 = simplify(G1,2); // remove zero generators |
---|
992 | |
---|
993 | // G = NF(G,G1) |
---|
994 | for (int i = 1; i <= ncols(G); i++) { // do not use size() here |
---|
995 | G[i] = lpNF(G[i],G1); |
---|
996 | } kill i; |
---|
997 | G = simplify(G,2); // remove zero generators |
---|
998 | |
---|
999 | // delete variables in LM(G1) from the ring |
---|
1000 | def save = basering; |
---|
1001 | def R = basering; |
---|
1002 | if (size(G1) > 0) { |
---|
1003 | while (size(G1) > 0) { |
---|
1004 | if (lpVarBlockSize(R) > 1) { |
---|
1005 | def @R = R - string(G1[1]); |
---|
1006 | R = @R; |
---|
1007 | kill @R; |
---|
1008 | setring R; |
---|
1009 | /* ring R = lpDelVar(lp2iv(G1[1])[1]); // TODO replace with proper method */ |
---|
1010 | ideal G1 = imap(save,G1); |
---|
1011 | G1 = simplify(G1, 2); // remove zero generators |
---|
1012 | } else { |
---|
1013 | // only the field is left (no variables) |
---|
1014 | return(0); |
---|
1015 | } |
---|
1016 | } |
---|
1017 | ideal G = imap(save, G); // put this here, because when save == R this call would make G = 0 |
---|
1018 | } |
---|
1019 | |
---|
1020 | // Li p. 184 if G = LM(G), then I = LM(I) and thus glDim = gkDim if it's finite |
---|
1021 | for (int i = 1; i <= size(G); i++) { |
---|
1022 | if (G[i] != lead(G[i])) { |
---|
1023 | break; |
---|
1024 | } else { |
---|
1025 | if (i == size(G)) { // if last iteration |
---|
1026 | int gkDim = lpGkDim(G); |
---|
1027 | if (gkDim >= 0) { |
---|
1028 | return (gkDim); |
---|
1029 | } |
---|
1030 | kill gkDim; |
---|
1031 | } |
---|
1032 | } |
---|
1033 | } kill i; |
---|
1034 | |
---|
1035 | intmat GNC = lpGraphOfNChains(G); |
---|
1036 | |
---|
1037 | // assuming GNC is connected |
---|
1038 | |
---|
1039 | // TODO: maybe loop+cycle checking could be done more efficiently? |
---|
1040 | if (!imHasLoops(GNC) && imIsUpRightTriangle(topologicalSort(GNC))) { |
---|
1041 | // GNC is a DAG |
---|
1042 | intmat GNCk = GNC; |
---|
1043 | intmat zero[1][ncols(GNCk)]; |
---|
1044 | int k = 1; |
---|
1045 | // while first row isn't empty |
---|
1046 | while (GNCk[1,1..(ncols(GNCk))] != zero[1,1..(ncols(zero))]) { |
---|
1047 | GNCk = GNCk * GNC; |
---|
1048 | k++; |
---|
1049 | } |
---|
1050 | // k-1 = number of edges in longest path starting from 1 |
---|
1051 | return (k-1); |
---|
1052 | } else { |
---|
1053 | // GNC contains loops/cycles => there is always an n-chain |
---|
1054 | return (-1); // infinity |
---|
1055 | } |
---|
1056 | } |
---|
1057 | example |
---|
1058 | { |
---|
1059 | "EXAMPLE:"; echo = 2; |
---|
1060 | ring r = 0,(x,y),dp; |
---|
1061 | def R = freeAlgebra(r, 5); // constructs a Letterplace ring |
---|
1062 | setring R; // sets basering to Letterplace ring |
---|
1063 | ideal G = x*x, y*y,x*y*x; // it is a monomial Groebner basis |
---|
1064 | lpGlDimBound(G); |
---|
1065 | ideal H = y*x - x*y; H = std(H); // H is a Groebner basis |
---|
1066 | lpGlDimBound(H); // gl dim of K[x,y] is 2, as expected |
---|
1067 | } |
---|
1068 | |
---|
1069 | static proc imHasLoops(intmat A) { |
---|
1070 | int n = ncols(A); |
---|
1071 | for (int i = 1; i <= n; i++) { |
---|
1072 | if (A[i,i] == 1) { |
---|
1073 | return (1); |
---|
1074 | } |
---|
1075 | } kill i; |
---|
1076 | return (0); |
---|
1077 | } |
---|
1078 | |
---|
1079 | static proc lpGraphOfNChains(ideal G) // G must be reduced |
---|
1080 | { |
---|
1081 | list LG = lpId2ivLi(lead(G)); |
---|
1082 | int n = lpVarBlockSize(basering); |
---|
1083 | int degbound = lpDegBound(basering); |
---|
1084 | |
---|
1085 | list V; |
---|
1086 | for (int i = 0; i <= n; i++) { |
---|
1087 | V[i+1] = i; // add 1 and all variables |
---|
1088 | } kill i; |
---|
1089 | for (int i = 1; i <= size(LG); i++) { |
---|
1090 | intvec u = LG[i]; |
---|
1091 | for (int j = 2; j <= size(u); j++) { |
---|
1092 | intvec v = u[j..size(u)]; |
---|
1093 | if (!contains(V, v)) { |
---|
1094 | V = insert(V, v, size(V)); // add subword j..size |
---|
1095 | } |
---|
1096 | kill v; |
---|
1097 | } kill j; |
---|
1098 | kill u; |
---|
1099 | } kill i; |
---|
1100 | int nV = size(V); |
---|
1101 | intmat GNC[nV][nV]; // graph of n-chains |
---|
1102 | |
---|
1103 | // for vertex 1 |
---|
1104 | for (int i = 2; i <= n + 1; i++) { |
---|
1105 | GNC[1,i] = 1; // 1 has an edge to all variables |
---|
1106 | } kill i; |
---|
1107 | |
---|
1108 | // for the other vertices |
---|
1109 | for (int i = 2; i <= nV; i++) { |
---|
1110 | for (int j = 2; j <= nV; j++) { |
---|
1111 | intvec uv = V[i],V[j]; |
---|
1112 | |
---|
1113 | if (contains(LG, uv)) { |
---|
1114 | GNC[i,j] = 1; |
---|
1115 | } else { |
---|
1116 | // Li p. 177 |
---|
1117 | // search for a right divisor 'w' of uv in G |
---|
1118 | // then check if G doesn't divide the subword uv-1 |
---|
1119 | |
---|
1120 | // look for a right divisor in LG |
---|
1121 | for (int k = 1; k <= size(LG); k++) { |
---|
1122 | if (isSF(LG[k], uv)) { |
---|
1123 | // w = LG[k] |
---|
1124 | if(!ivdivides(LG, uv[1..(size(uv)-1)])) { |
---|
1125 | // G doesn't divide uv-1 |
---|
1126 | GNC[i,j] = 1; |
---|
1127 | break; |
---|
1128 | } |
---|
1129 | } |
---|
1130 | } kill k; |
---|
1131 | } |
---|
1132 | kill uv; |
---|
1133 | } kill j; |
---|
1134 | } kill i; |
---|
1135 | |
---|
1136 | return(GNC); |
---|
1137 | } |
---|
1138 | |
---|
1139 | static proc contains(list L, def item) |
---|
1140 | { |
---|
1141 | for (int i = 1; i <= size(L); i++) { |
---|
1142 | if (L[i] == item) { |
---|
1143 | return (1); |
---|
1144 | } |
---|
1145 | } kill i; |
---|
1146 | return (0); |
---|
1147 | } |
---|
1148 | |
---|
1149 | proc lpSubstitute(poly f, ideal s1, ideal s2, list #) |
---|
1150 | "USAGE: lpSubstitute(f,s1,s2[,G]); f poly, s1 list (ideal) of variables |
---|
1151 | to replace, s2 list (ideal) of polynomials to replace with, G optional ideal to |
---|
1152 | reduce with. |
---|
1153 | RETURN: poly, the substituted polynomial |
---|
1154 | ASSUME: - basering is a Letterplace ring |
---|
1155 | - s1 contains a subset of the set of variables |
---|
1156 | - s2 and s1 are of the same size |
---|
1157 | - G is a Groebner basis, |
---|
1158 | - the current ring has a sufficient degbound (which also can be calculated with lpCalcSubstDegBound()) |
---|
1159 | NOTE: the procedure implements the image of a polynomial f |
---|
1160 | under an endomorphism of a free algebra, defined by s1 and s2: |
---|
1161 | variables, not present in s1, are left unchanged; |
---|
1162 | variable s1[k] is mapped to a polynomial s2[k]. |
---|
1163 | - An optional ideal G extends the endomorphism as above to the morphism into the factor algebra K<X>/G. |
---|
1164 | EXAMPLE: example lpSubstitute; shows examples |
---|
1165 | " |
---|
1166 | { |
---|
1167 | ideal G; |
---|
1168 | if (size(#) > 0) { |
---|
1169 | if (typeof(#[1])=="ideal") { |
---|
1170 | G = #[1]; |
---|
1171 | } |
---|
1172 | } |
---|
1173 | |
---|
1174 | poly fs; |
---|
1175 | for (int i = 1; i <= size(f); i++) { |
---|
1176 | poly fis = leadcoef(f[i]); |
---|
1177 | intvec ivfi = lp2iv(f[i]); |
---|
1178 | for (int j = 1; j <= size(ivfi); j++) { |
---|
1179 | int varindex = ivfi[j]; |
---|
1180 | if (varindex > 0) { |
---|
1181 | int subindex = lpIndexOf(s1, var(varindex)); |
---|
1182 | if (subindex > 0) { |
---|
1183 | s2[subindex] = lpNF(s2[subindex],G); |
---|
1184 | fis = fis * s2[subindex]; |
---|
1185 | } else { |
---|
1186 | fis = fis * lpNF(iv2lp(varindex),G); |
---|
1187 | } |
---|
1188 | /*fis = lpNF(fis,G);*/ |
---|
1189 | kill subindex; |
---|
1190 | } |
---|
1191 | kill varindex; |
---|
1192 | } kill j; |
---|
1193 | kill ivfi; |
---|
1194 | fs = fs + fis; |
---|
1195 | kill fis; |
---|
1196 | } |
---|
1197 | kill i; |
---|
1198 | fs = lpNF(fs, G); |
---|
1199 | return (fs); |
---|
1200 | } |
---|
1201 | example |
---|
1202 | { |
---|
1203 | "EXAMPLE:"; echo = 2; |
---|
1204 | ring r = 0,(x,y,z),dp; |
---|
1205 | def R = freeAlgebra(r, 4); |
---|
1206 | setring R; |
---|
1207 | ideal G = x*y; // optional |
---|
1208 | poly f = 3*x*x+y*x; |
---|
1209 | ideal s1 = x, y; |
---|
1210 | ideal s2 = y*z*z, x; // i.e. x --> yzz and y --> x |
---|
1211 | // the substitution probably needs a higher degbound |
---|
1212 | int minDegBound = lpCalcSubstDegBound(f,s1,s2); |
---|
1213 | minDegBound; // thus the bound needs to be increased |
---|
1214 | setring r; // back to original r |
---|
1215 | def R1 = freeAlgebra(r, minDegBound); |
---|
1216 | setring R1; |
---|
1217 | lpSubstitute(imap(R,f), imap(R,s1), imap(R,s2)); |
---|
1218 | // the last parameter is optional; above it was G=<xy> |
---|
1219 | // the output will be reduced with respect to G |
---|
1220 | lpSubstitute(imap(R,f), imap(R,s1), imap(R,s2), imap(R,G)); |
---|
1221 | } |
---|
1222 | |
---|
1223 | // another example: |
---|
1224 | /* |
---|
1225 | //////// EXAMPLE B //////// |
---|
1226 | ring r = 0,(x,y,z),dp; |
---|
1227 | def R = freeAlgebra(r, 4); |
---|
1228 | setring R; |
---|
1229 | |
---|
1230 | poly f = 3*x*x+y*x; |
---|
1231 | poly g = z*x+y; |
---|
1232 | poly h = 7*x*z+x; |
---|
1233 | ideal I = f,g,h; |
---|
1234 | ideal s1 = x, y; |
---|
1235 | ideal s2 = y*z*z, x; |
---|
1236 | |
---|
1237 | int minDegBound = lpCalcSubstDegBound(I,s1,s2); |
---|
1238 | setring r; |
---|
1239 | def R1 = freeAlgebra(r, minDegBound); |
---|
1240 | setring R1; |
---|
1241 | |
---|
1242 | ideal I = imap(R,I); |
---|
1243 | ideal s1 = imap(R,s1); |
---|
1244 | ideal s2 = imap(R,s2); |
---|
1245 | for (int i = 1; i <= size(I); i++) { |
---|
1246 | lpSubstitute(I[i], s1, s2); |
---|
1247 | } |
---|
1248 | */ |
---|
1249 | |
---|
1250 | static proc lpIndexOf(ideal I, poly p) { |
---|
1251 | for (int i = 1; i <= size(I); i++) { |
---|
1252 | if (I[i] == p) { |
---|
1253 | return (i); |
---|
1254 | } |
---|
1255 | } kill i; |
---|
1256 | return (-1); |
---|
1257 | } |
---|
1258 | |
---|
1259 | static proc ivIndexOf(list L, intvec iv) { |
---|
1260 | for (int i = 1; i <= size(L); i++) { |
---|
1261 | if (L[i] == iv) { |
---|
1262 | return (i); |
---|
1263 | } |
---|
1264 | } kill i; |
---|
1265 | return (-1); |
---|
1266 | } |
---|
1267 | |
---|
1268 | |
---|
1269 | static proc lpCalcSubstDegBoundSingle(poly f, ideal s1, ideal s2) |
---|
1270 | "USAGE: lpCalcSubstDegBoundSingle(f,s1,s2); f letterplace polynomial, s1 list (ideal) of variables |
---|
1271 | to replace, s2 list (ideal) of polynomials to replace with |
---|
1272 | RETURN: int, the min degbound required to perform the substitution |
---|
1273 | ASSUME: - basering is a Letterplace ring |
---|
1274 | EXAMPLE: example lpCalcSubstDegBoundSingle; shows examples |
---|
1275 | " |
---|
1276 | { |
---|
1277 | int maxDegBound = 0; |
---|
1278 | for (int i = 1; i <= size(f); i++) { |
---|
1279 | intvec ivfi = lp2iv(f[i]); |
---|
1280 | int tmpDegBound; |
---|
1281 | for (int j = 1; j <= size(ivfi); j++) { |
---|
1282 | int varindex = ivfi[j]; |
---|
1283 | if (varindex > 0) { |
---|
1284 | int subindex = lpIndexOf(s1, var(varindex)); |
---|
1285 | if (subindex > 0) { |
---|
1286 | tmpDegBound = tmpDegBound + deg(s2[subindex]); |
---|
1287 | } else { |
---|
1288 | tmpDegBound = tmpDegBound + 1; |
---|
1289 | } |
---|
1290 | kill subindex; |
---|
1291 | } |
---|
1292 | kill varindex; |
---|
1293 | } kill j; |
---|
1294 | if (tmpDegBound > maxDegBound) { |
---|
1295 | maxDegBound = tmpDegBound; |
---|
1296 | } |
---|
1297 | kill ivfi; kill tmpDegBound; |
---|
1298 | } kill i; |
---|
1299 | |
---|
1300 | // increase degbound by 50% when ideal is provided |
---|
1301 | // needed for lpNF |
---|
1302 | maxDegBound = maxDegBound + (maxDegBound div 2); |
---|
1303 | |
---|
1304 | return (maxDegBound); |
---|
1305 | } |
---|
1306 | example |
---|
1307 | { |
---|
1308 | // see lpCalcSubstDegBound() |
---|
1309 | } |
---|
1310 | |
---|
1311 | proc lpCalcSubstDegBound(ideal I, ideal s1, ideal s2) |
---|
1312 | "USAGE: lpCalcSubstDegBound(I,s1,s2); I ideal of polynomials, s1 ideal of variables to replace, s2 ideal of polynomials to replace with |
---|
1313 | RETURN: int, the min degbound required to perform all of the substitutions |
---|
1314 | ASSUME: - basering is a Letterplace ring |
---|
1315 | EXAMPLE: example lpCalcSubstDegBound; shows examples |
---|
1316 | NOTE: convenience method |
---|
1317 | " |
---|
1318 | { |
---|
1319 | int maxDegBound = 0; |
---|
1320 | for (int i = 1; i <= size(I); i++) { |
---|
1321 | int tmpDegBound = lpCalcSubstDegBoundSingle(I[i], s1, s2, #); |
---|
1322 | if (tmpDegBound > maxDegBound) { |
---|
1323 | maxDegBound = tmpDegBound; |
---|
1324 | } |
---|
1325 | kill tmpDegBound; |
---|
1326 | } kill i; |
---|
1327 | return (maxDegBound); |
---|
1328 | } |
---|
1329 | example |
---|
1330 | { |
---|
1331 | "EXAMPLE:"; echo = 2; |
---|
1332 | ring r = 0,(x,y,z),dp; |
---|
1333 | def R = freeAlgebra(r, 4); |
---|
1334 | setring R; |
---|
1335 | ideal I = 3*x*x+y*x, x*y*x - z; |
---|
1336 | ideal s1 = x, y; // z --> z |
---|
1337 | ideal s2 = y*z*z, x; // i.e. x --> yzz and y --> x |
---|
1338 | // the substitution probably needs a higher degbound |
---|
1339 | lpCalcSubstDegBound(I,s1,s2); |
---|
1340 | lpCalcSubstDegBound(I[1],s1,s2); |
---|
1341 | } |
---|
1342 | |
---|
1343 | static proc isSF(intvec S, intvec I) |
---|
1344 | " |
---|
1345 | PURPOSE: |
---|
1346 | checks, if a word S is a suffix of another word I |
---|
1347 | " |
---|
1348 | { |
---|
1349 | int n = size(S); |
---|
1350 | if (n <= 0 || S == 0) {return(1);} |
---|
1351 | int m = size(I); |
---|
1352 | if (m < n) {return(0);} |
---|
1353 | intvec IS = I[(m-n+1)..m]; |
---|
1354 | if (IS == S) {return(1);} |
---|
1355 | else {return(0);} |
---|
1356 | } |
---|
1357 | |
---|
1358 | static proc isIF(intvec IF, intvec I) |
---|
1359 | " |
---|
1360 | PURPOSE: |
---|
1361 | checks, if a word IF is an infix of another word I |
---|
1362 | " |
---|
1363 | { |
---|
1364 | int n = size(IF); |
---|
1365 | int m = size(I); |
---|
1366 | |
---|
1367 | if (n <= 0 || IF == 0) {return(1);} |
---|
1368 | if (m < n) {return(0);} |
---|
1369 | |
---|
1370 | for (int i = 0; (n + i) <= m; i++){ |
---|
1371 | intvec IIF = I[(1 + i)..(n + i)]; |
---|
1372 | if (IIF == IF) { |
---|
1373 | return(1); |
---|
1374 | } |
---|
1375 | kill IIF; |
---|
1376 | } kill i; |
---|
1377 | return(0); |
---|
1378 | } |
---|
1379 | |
---|
1380 | // no longer working with new interface and new orderings |
---|
1381 | /* // TODO: use original ring attrib to create a new letterplace ring */ |
---|
1382 | /* // removes a variable from a letterplace ring (a bit of a hack) */ |
---|
1383 | /* static proc lpDelVar(int index) { */ |
---|
1384 | /* int lV = lpVarBlockSize(basering); // number of variables in the main block */ |
---|
1385 | /* int d = lpDegBound(basering); // degree bround */ |
---|
1386 | /* list LR = ringlist(basering); */ |
---|
1387 | |
---|
1388 | /* if (!(index >= 1 && index <= lV)) { return (basering); } // invalid index */ |
---|
1389 | |
---|
1390 | /* // remove frome the variable list */ |
---|
1391 | /* for (int i = (d-1)*lV + index; i >= 1; i = i - lV) { */ |
---|
1392 | /* LR[2] = delete(LR[2], i); */ |
---|
1393 | /* } kill i; */ |
---|
1394 | |
---|
1395 | /* // remove from a ordering */ |
---|
1396 | /* intvec aiv = LR[3][1][2]; */ |
---|
1397 | /* aiv = aiv[1..(d*lV-d)]; */ |
---|
1398 | /* LR[3][1][2] = aiv; */ |
---|
1399 | |
---|
1400 | /* // remove block orderings */ |
---|
1401 | /* int del = (lV - index); */ |
---|
1402 | /* int cnt = -1; */ |
---|
1403 | /* for (int i = size(LR[3]); i >= 2; i--) { */ |
---|
1404 | /* if (LR[3][i][2] != 0) { */ |
---|
1405 | /* for (int j = size(LR[3][i][2]); j >= 1; j--) { */ |
---|
1406 | /* cnt++; // next 1 */ |
---|
1407 | /* if (cnt%lV == del) { */ |
---|
1408 | /* // delete */ |
---|
1409 | /* if (size(LR[3][i][2]) > 1) { // if we have more than one element left, delete one */ |
---|
1410 | /* LR[3][i][2] = delete(LR[3][i][2],j); */ |
---|
1411 | /* } else { // otherwise delete the whole block */ |
---|
1412 | /* LR[3] = delete(LR[3], i); */ |
---|
1413 | /* break; */ |
---|
1414 | /* } */ |
---|
1415 | /* } */ |
---|
1416 | /* } kill j; */ |
---|
1417 | /* } */ |
---|
1418 | /* } kill i; */ |
---|
1419 | |
---|
1420 | /* def R = setLetterplaceAttributes(ring(LR),d,lV-1); */ |
---|
1421 | /* return (R); */ |
---|
1422 | /* } */ |
---|
1423 | /* example */ |
---|
1424 | /* { */ |
---|
1425 | /* "EXAMPLE:"; echo = 2; */ |
---|
1426 | /* ring r = 0,(x,y,z),dp; */ |
---|
1427 | /* def A = freeAlgebra(r, 3); */ |
---|
1428 | /* setring A; A; */ |
---|
1429 | /* def R = lpDelVar(2); setring R; R; */ |
---|
1430 | /* } */ |
---|