1 // emacs edit mode for this file is -*- C++ -*-
2
3 /****************************************
4 * Computer Algebra System SINGULAR *
5 ****************************************/
6 /*
7 * ABSTRACT - The FGLM-Algorithm
8 * Implementation of the fglm algorithm for 0-dimensional ideals,
9 * based on an idea by Faugere/Gianni/Lazard and Mora.
10 * The procedure CalculateFunctionals calculates the functionals
11 * which define the given ideal in the source ring. They build the
12 * input for GroebnerViaFunctionals, which defines the reduced
13 * groebner basis for the ideal in the destination ring.
14 */
15
16 /* Changes:
17 * o FindUnivariatePolys added
18 */
19
20
21
22
23 #include "kernel/mod2.h"
24
25
26 // assumes, that NOSTREAMIO is set in factoryconf.h, which is included
27 // by templates/list.h.
28
29 #include "factory/factory.h"
30
31 #include "factory/templates/ftmpl_list.h"
32 #include "factory/templates/ftmpl_list.cc"
33
34 #include "misc/options.h"
35 #include "misc/intvec.h"
36
37 #include "polys/monomials/maps.h"
38 #include "polys/monomials/ring.h"
39
40 #include "kernel/polys.h"
41 #include "kernel/ideals.h"
42 #include "kernel/GBEngine/kstd1.h"
43
44 #include "fglm.h"
45 #include "fglmvec.h"
46 #include "fglmgauss.h"
47
48 #define PROT(msg)
49 #define STICKYPROT(msg) if (BTEST1(OPT_PROT)) Print(msg)
50 #define PROT2(msg,arg)
51 #define STICKYPROT2(msg,arg) if (BTEST1(OPT_PROT)) Print(msg,arg)
52 #define fglmASSERT(ignore1,ignore2)
53
54
55
56 // internal Version: 1.3.1.12
57 // ============================================================
58 //! The idealFunctionals
59 // ============================================================
60
61 struct matElem
62 {
63 int row;
64 number elem;
65 };
66
67 struct matHeader
68 {
69 int size;
70 BOOLEAN owner;
71 matElem * elems;
72 };
73
74 class idealFunctionals
75 {
76 private:
77 int _block;
78 int _max;
79 int _size;
80 int _nfunc;
81 int * currentSize;
82 matHeader ** func;
83 matHeader * grow( int var );
84 public:
85 idealFunctionals( int blockSize, int numFuncs );
86 ~idealFunctionals();
87
dimen() const88 int dimen() const { fglmASSERT( _size>0, "called too early"); return _size; }
89 void endofConstruction();
90 void map( ring source );
91 void insertCols( int * divisors, int to );
92 void insertCols( int * divisors, const fglmVector to );
93 fglmVector addCols( const int var, int basisSize, const fglmVector v ) const;
94 fglmVector multiply( const fglmVector v, int var ) const;
95 };
96
97
idealFunctionals(int blockSize,int numFuncs)98 idealFunctionals::idealFunctionals( int blockSize, int numFuncs )
99 {
100 int k;
101 _block= blockSize;
102 _max= _block;
103 _size= 0;
104 _nfunc= numFuncs;
105
106 currentSize= (int *)omAlloc0( _nfunc*sizeof( int ) );
107 //for ( k= _nfunc-1; k >= 0; k-- )
108 // currentSize[k]= 0;
109
110 func= (matHeader **)omAlloc( _nfunc*sizeof( matHeader * ) );
111 for ( k= _nfunc-1; k >= 0; k-- )
112 func[k]= (matHeader *)omAlloc( _max*sizeof( matHeader ) );
113 }
114
~idealFunctionals()115 idealFunctionals::~idealFunctionals()
116 {
117 int k;
118 int l;
119 int row;
120 matHeader * colp;
121 matElem * elemp;
122 for ( k= _nfunc-1; k >= 0; k-- ) {
123 for ( l= _size-1, colp= func[k]; l >= 0; l--, colp++ ) {
124 if ( ( colp->owner == TRUE ) && ( colp->size > 0 ) ) {
125 for ( row= colp->size-1, elemp= colp->elems; row >= 0; row--, elemp++ )
126 nDelete( & elemp->elem );
127 omFreeSize( (ADDRESS)colp->elems, colp->size*sizeof( matElem ) );
128 }
129 }
130 omFreeSize( (ADDRESS)func[k], _max*sizeof( matHeader ) );
131 }
132 omFreeSize( (ADDRESS)func, _nfunc*sizeof( matHeader * ) );
133 omFreeSize( (ADDRESS)currentSize, _nfunc*sizeof( int ) );
134 }
135
136 void
endofConstruction()137 idealFunctionals::endofConstruction()
138 {
139 _size= currentSize[0];
140 }
141
142 void
map(ring source)143 idealFunctionals::map( ring source )
144 {
145 // maps from ring source to currentRing.
146 int var, col, row;
147 matHeader * colp;
148 matElem * elemp;
149 number newelem;
150
151 int * perm = (int *)omAlloc0( (_nfunc+1)*sizeof( int ) );
152 maFindPerm( source->names, source->N, NULL, 0, currRing->names,
153 currRing->N, NULL, 0, perm, NULL , currRing->cf->type);
154 nMapFunc nMap=n_SetMap( source->cf, currRing->cf);
155
156 matHeader ** temp = (matHeader **)omAlloc( _nfunc*sizeof( matHeader * ));
157 for ( var= 0; var < _nfunc; var ++ ) {
158 for ( col= 0, colp= func[var]; col < _size; col++, colp++ ) {
159 if ( colp->owner == TRUE ) {
160 for ( row= colp->size-1, elemp= colp->elems; row >= 0;
161 row--, elemp++ )
162 {
163 newelem= nMap( elemp->elem, source->cf, currRing->cf );
164 nDelete( & elemp->elem );
165 elemp->elem= newelem;
166 }
167 }
168 }
169 temp[ perm[var+1]-1 ]= func[var];
170 }
171 omFreeSize( (ADDRESS)func, _nfunc*sizeof( matHeader * ) );
172 omFreeSize( (ADDRESS)perm, (_nfunc+1)*sizeof( int ) );
173 func= temp;
174 }
175
176 matHeader *
grow(int var)177 idealFunctionals::grow( int var )
178 {
179 if ( currentSize[var-1] == _max ) {
180 int k;
181 for ( k= _nfunc; k > 0; k-- )
182 func[k-1]= (matHeader *)omReallocSize( func[k-1], _max*sizeof( matHeader ), (_max + _block)*sizeof( matHeader ) );
183 _max+= _block;
184 }
185 currentSize[var-1]++;
186 return func[var-1] + currentSize[var-1] - 1;
187 }
188
189 void
insertCols(int * divisors,int to)190 idealFunctionals::insertCols( int * divisors, int to )
191 {
192 fglmASSERT( 0 < divisors[0] && divisors[0] <= _nfunc, "wrong number of divisors" );
193 int k;
194 BOOLEAN owner = TRUE;
195 matElem * elems = (matElem *)omAlloc( sizeof( matElem ) );
196 elems->row= to;
197 elems->elem= nInit( 1 );
198 for ( k= divisors[0]; k > 0; k-- ) {
199 fglmASSERT( 0 < divisors[k] && divisors[k] <= _nfunc, "wrong divisor" );
200 matHeader * colp = grow( divisors[k] );
201 colp->size= 1;
202 colp->elems= elems;
203 colp->owner= owner;
204 owner= FALSE;
205 }
206 }
207
208
209 void
insertCols(int * divisors,const fglmVector to)210 idealFunctionals::insertCols( int * divisors, const fglmVector to )
211 {
212 // divisors runs from divisors[0]..divisors[size-1]
213 fglmASSERT( 0 < divisors[0] && divisors[0] <= _nfunc, "wrong number of divisors" );
214 int k, l;
215 int numElems= to.numNonZeroElems();
216 matElem * elems;
217 matElem * elemp;
218 BOOLEAN owner = TRUE;
219 if ( numElems > 0 ) {
220 elems= (matElem *)omAlloc( numElems * sizeof( matElem ) );
221 for ( k= 1, l= 1, elemp= elems; k <= numElems; k++, elemp++ ) {
222 while ( nIsZero( to.getconstelem(l) ) ) l++;
223 elemp->row= l;
224 elemp->elem= nCopy( to.getconstelem( l ) );
225 l++; // hochzaehlen, damit wir nicht noch einmal die gleiche Stelle testen
226 }
227 }
228 else
229 elems= NULL;
230 for ( k= divisors[0]; k > 0; k-- ) {
231 fglmASSERT( 0 < divisors[k] && divisors[k] <= _nfunc, "wrong divisor" );
232 matHeader * colp = grow( divisors[k] );
233 colp->size= numElems;
234 colp->elems= elems;
235 colp->owner= owner;
236 owner= FALSE;
237 }
238 }
239
240 fglmVector
addCols(const int var,int basisSize,const fglmVector v) const241 idealFunctionals::addCols( const int var, int basisSize, const fglmVector v ) const
242 {
243 fglmVector result( basisSize );
244 matHeader * colp;
245 matElem * elemp;
246 number factor, temp;
247 int k, l;
248 int vsize = v.size();
249
250 fglmASSERT( currentSize[var-1]+1 >= vsize, "wrong v.size()" );
251 for ( k= 1, colp= func[var-1]; k <= vsize; k++, colp++ ) {
252 factor= v.getconstelem( k );
253 if ( ! nIsZero( factor ) ) {
254 for ( l= colp->size-1, elemp= colp->elems; l >= 0; l--, elemp++ ) {
255 temp= nMult( factor, elemp->elem );
256 number newelem= nAdd( result.getconstelem( elemp->row ), temp );
257 nDelete( & temp );
258 nNormalize( newelem );
259 result.setelem( elemp->row, newelem );
260 }
261 }
262 }
263 return result;
264 }
265
266 fglmVector
multiply(const fglmVector v,int var) const267 idealFunctionals::multiply( const fglmVector v, int var ) const
268 {
269 fglmASSERT( v.size() == _size, "multiply: v has wrong size");
270 fglmVector result( _size );
271 matHeader * colp;
272 matElem * elemp;
273 number factor, temp;
274 int k, l;
275 for ( k= 1, colp= func[var-1]; k <= _size; k++, colp++ ) {
276 factor= v.getconstelem( k );
277 if ( ! nIsZero( factor ) ) {
278 for ( l= colp->size-1, elemp= colp->elems; l >= 0; l--, elemp++ ) {
279 temp= nMult( factor, elemp->elem );
280 number newelem= nAdd( result.getconstelem( elemp->row ), temp );
281 nDelete( & temp );
282 nNormalize( newelem );
283 result.setelem( elemp->row, newelem );
284 }
285 }
286 }
287 return result;
288 }
289
290 // ============================================================
291 //! The old basis
292 // ============================================================
293
294 // Contains the data for a border-monomial, i.e. the monom itself
295 // ( as a Singular-poly ) and its normal-form, described by an
296 // fglmVector. The size of the Vector depends on the current size of
297 // the basis when the normalForm was computed.
298 // monom gets deleted when borderElem comes out of scope.
299 class borderElem
300 {
301 public:
302 poly monom;
303 fglmVector nf;
borderElem()304 borderElem() : monom(NULL), nf() {}
borderElem(poly p,fglmVector n)305 borderElem( poly p, fglmVector n ) : monom( p ), nf( n ) {}
~borderElem()306 ~borderElem() { if (monom!=NULL) pLmDelete(&monom); }
307 #ifndef HAVE_EXPLICIT_CONSTR
insertElem(poly p,fglmVector n)308 void insertElem( poly p, fglmVector n )
309 {
310 monom= p;
311 nf= n;
312 }
313 #endif
314 };
315
316 // This class contains the relevant data for the 'candidates'
317 // The declaration of class fglmSelem is found in fglm.h
318
fglmSelem(poly p,int var)319 fglmSelem::fglmSelem( poly p, int var ) : monom( p ), numVars( 0 )
320 {
321 for ( int k = (currRing->N); k > 0; k-- )
322 if ( pGetExp( monom, k ) > 0 )
323 numVars++;
324 divisors= (int *)omAlloc( (numVars+1)*sizeof( int ) );
325 divisors[0]= 0;
326 newDivisor( var );
327 }
328
329 void
cleanup()330 fglmSelem::cleanup()
331 {
332 omFreeSize( (ADDRESS)divisors, (numVars+1)*sizeof( int ) );
333 }
334
335 // The data-structure for the Functional-Finding-Algorithm.
336 class fglmSdata
337 {
338 private:
339 ideal theIdeal;
340 int idelems;
341 int* varpermutation;
342
343 int basisBS;
344 int basisMax;
345 int basisSize;
346 polyset basis; //. rem: runs from basis[1]..basis[dimen]
347
348 int borderBS;
349 int borderMax;
350 int borderSize;
351 borderElem * border; //. rem: runs from border[1]..border[dimen]
352
353 List<fglmSelem> nlist;
354 BOOLEAN _state;
355 public:
356 fglmSdata( const ideal thisIdeal );
357 ~fglmSdata();
358
state() const359 BOOLEAN state() const { return _state; };
getBasisSize() const360 int getBasisSize() const { return basisSize; };
361 int newBasisElem( poly & p );
362 void newBorderElem( poly & m, fglmVector v );
candidatesLeft() const363 BOOLEAN candidatesLeft() const { return ( nlist.isEmpty() ? FALSE : TRUE ); }
364 fglmSelem nextCandidate();
365 void updateCandidates();
366 int getEdgeNumber( const poly m ) const;
getSpanPoly(int number) const367 poly getSpanPoly( int number ) const { return pCopy( (theIdeal->m)[number-1] ); }
368 fglmVector getVectorRep( const poly m );
369 fglmVector getBorderDiv( const poly m, int & var ) const;
370 };
371
fglmSdata(const ideal thisIdeal)372 fglmSdata::fglmSdata( const ideal thisIdeal )
373 {
374 // An dieser Stelle kann die BlockSize ( =BS ) noch sinnvoller berechnet
375 // werden, jenachdem wie das Ideal aussieht.
376 theIdeal= thisIdeal;
377 idelems= IDELEMS( theIdeal );
378 varpermutation = (int*)omAlloc( ((currRing->N)+1)*sizeof(int) );
379 // Sort ring variables by increasing values (because of weighted orderings)
380 ideal perm = idMaxIdeal(1);
381 intvec *iv = idSort(perm,TRUE);
382 idDelete(&perm);
383 for(int i = (currRing->N); i > 0; i--) varpermutation[(currRing->N)+1-i] = (*iv)[i-1];
384 delete iv;
385
386 basisBS= 100;
387 basisMax= basisBS;
388 basisSize= 0;
389 basis= (polyset)omAlloc( basisMax*sizeof( poly ) );
390
391 borderBS= 100;
392 borderMax= borderBS;
393 borderSize= 0;
394 #ifndef HAVE_EXPLICIT_CONSTR
395 border= new borderElem[ borderMax ];
396 #else
397 border= (borderElem *)omAlloc( borderMax*sizeof( borderElem ) );
398 #endif
399 // rem: the constructors are called in newBorderElem().
400 _state= TRUE;
401 }
402
~fglmSdata()403 fglmSdata::~fglmSdata()
404 {
405 omFreeSize( (ADDRESS)varpermutation, ((currRing->N)+1)*sizeof(int) );
406 for ( int k = basisSize; k > 0; k-- )
407 pLmDelete( basis + k ); //. rem: basis runs from basis[1]..basis[basisSize]
408 omFreeSize( (ADDRESS)basis, basisMax*sizeof( poly ) );
409 #ifndef HAVE_EXPLICIT_CONSTR
410 delete [] border;
411 #else
412 for ( int l = borderSize; l > 0; l-- )
413 // rem: the polys of borderElem are deleted via ~borderElem()
414 border[l].~borderElem();
415 omFreeSize( (ADDRESS)border, borderMax*sizeof( borderElem ) );
416 #endif
417 }
418
419 // Inserts poly p without copying into basis, reAllocs Memory if necessary,
420 // increases basisSize and returns the new basisSize.
421 // Remember: The elements of basis are deleted via pDelete in ~fglmSdata!
422 // Sets m= NULL to indicate that now basis is ow(e?)ing the poly.
423 int
newBasisElem(poly & m)424 fglmSdata::newBasisElem( poly & m )
425 {
426 basisSize++;
427 if ( basisSize == basisMax ) {
428 basis= (polyset)omReallocSize( basis, basisMax*sizeof( poly ), (basisMax + basisBS)*sizeof( poly ) );
429 basisMax+= basisBS;
430 }
431 basis[basisSize]= m;
432 m= NULL;
433 return basisSize;
434 }
435
436 // Inserts poly p and fglmvector v without copying into border, reAllocs Memory
437 // if necessary, and increases borderSize.
438 // Remember: The poly of border is deleted via ~borderElem in ~fglmSdata!
439 // Sets m= NULL to indicate that now border is ow(e?)ing the poly.
440 void
newBorderElem(poly & m,fglmVector v)441 fglmSdata::newBorderElem( poly & m, fglmVector v )
442 {
443 borderSize++;
444 if ( borderSize == borderMax ) {
445 #ifndef HAVE_EXPLICIT_CONSTR
446 borderElem * tempborder = new borderElem[ borderMax+borderBS ];
447 for ( int k = 0; k < borderMax; k++ ) {
448 tempborder[k]= border[k];
449 border[k].insertElem( NULL, fglmVector() );
450 }
451 delete [] border;
452 border= tempborder;
453 #else
454 border= (borderElem *)omReallocSize( border, borderMax*sizeof( borderElem ), (borderMax + borderBS)*sizeof( borderElem ) );
455 #endif
456 borderMax+= borderBS;
457 }
458 #ifndef HAVE_EXPLICIT_CONSTR
459 border[borderSize].insertElem( m, v );
460 #else
461 border[borderSize].borderElem( m, v );
462 #endif
463 m= NULL;
464 }
465
466 fglmSelem
nextCandidate()467 fglmSdata::nextCandidate()
468 {
469 fglmSelem result = nlist.getFirst();
470 nlist.removeFirst();
471 return result;
472 }
473
474 // Multiplies basis[basisSize] with all ringvariables and inserts the new monomials
475 // into the list of candidates, according to the given order. If a monomial already
476 // exists, then "insertions" and "divisors" are updated.
477 // Assumes that ringvar(varperm[k]) < ringvar(varperm[l]) for k > l
478 void
updateCandidates()479 fglmSdata::updateCandidates()
480 {
481 ListIterator<fglmSelem> list = nlist;
482 fglmASSERT( basisSize > 0 && basisSize < basisMax, "Error(1) in fglmSdata::updateCandidates - wrong bassSize" );
483 poly m = basis[basisSize];
484 poly newmonom = NULL;
485 int k = (currRing->N);
486 BOOLEAN done = FALSE;
487 int state = 0;
488 while ( k >= 1 )
489 {
490 newmonom = pCopy( m );
491 pIncrExp( newmonom, varpermutation[k] );
492 pSetm( newmonom );
493 done= FALSE;
494 while ( list.hasItem() && (!done) )
495 {
496 if ( (state= pCmp( list.getItem().monom, newmonom )) < 0 )
497 list++;
498 else done= TRUE;
499 }
500 if ( !done )
501 {
502 nlist.append( fglmSelem( newmonom, varpermutation[k] ) );
503 break;
504 }
505 if ( state == 0 )
506 {
507 list.getItem().newDivisor( varpermutation[k] );
508 pLmDelete(&newmonom);
509 }
510 else
511 {
512 list.insert( fglmSelem( newmonom, varpermutation[k] ) );
513 }
514 k--;
515 }
516 while ( --k >= 1 )
517 {
518 newmonom= pCopy( m ); // HIER
519 pIncrExp( newmonom, varpermutation[k] );
520 pSetm( newmonom );
521 nlist.append( fglmSelem( newmonom, varpermutation[k] ) );
522 }
523 }
524
525 // if p == pHead( (theIdeal->m)[k] ) return k, 0 otherwise
526 // (Assumes that pLmEqual just checks the leading monomials without
527 // coefficients.)
528 int
getEdgeNumber(const poly m) const529 fglmSdata::getEdgeNumber( const poly m ) const
530 {
531 for ( int k = idelems; k > 0; k-- )
532 if ( pLmEqual( m, (theIdeal->m)[k-1] ) )
533 return k;
534 return 0;
535 }
536
537 // Returns the fglmVector v, s.t.
538 // p = v[1]*basis(1) + .. + v[basisSize]*basis(basisSize)
539 // So the size of v depends on the current size of the basis.
540 // Assumes that such an representation exists, i.e. all monoms in p have to be
541 // smaller than basis[basisSize] and that basis[k] < basis[l] for k < l.
542 fglmVector
getVectorRep(const poly p)543 fglmSdata::getVectorRep( const poly p )
544 {
545 fglmVector temp( basisSize );
546 poly m = p;
547 int num = basisSize;
548 while ( m != NULL ) {
549 int comp = pCmp( m, basis[num] );
550 if ( comp == 0 ) {
551 fglmASSERT( num > 0, "Error(1) in fglmSdata::getVectorRep" );
552 number newelem = nCopy( pGetCoeff( m ) );
553 temp.setelem( num, newelem );
554 num--;
555 pIter( m );
556 }
557 else {
558 if ( comp < 0 ) {
559 num--;
560 }
561 else {
562 // This is the place where we can detect if the sourceIdeal
563 // is not reduced. In this case m is not in basis[]. Since basis[]
564 // is ordered this is the case, if and only if basis[i]<m
565 // and basis[j]>m for all j>i
566 _state= FALSE;
567 return temp;
568 }
569 }
570 }
571 return temp;
572 }
573
574 // Searches through the border for a monomoial bm which devides m and returns
575 // its normalform in vector representation.
576 // var contains the number of the variable v, s.t. bm = m * v
577 fglmVector
getBorderDiv(const poly m,int & var) const578 fglmSdata::getBorderDiv( const poly m, int & var ) const
579 {
580 // int num2 = borderSize;
581 // while ( num2 > 0 ) {
582 // poly temp = border[num2].monom;
583 // if ( pDivisibleBy( temp, m ) ) {
584 // poly divisor = pDivideM( m, temp );
585 // int var = pIsPurePower( divisor );
586 // if ( (var != 0) && (pGetCoeff( divisor, var ) == 1) ) {
587 // Print( "poly %s divides poly %s", pString( temp ), pString( m ) );
588 // }
589 // }
590 // num2--;
591 // }
592 int num = borderSize;
593 while ( num > 0 ) {
594 poly temp = border[num].monom;
595 if ( pDivisibleBy( temp, m ) ) {
596 var = (currRing->N);
597 while ( var > 0 ) {
598 if ( (pGetExp( m, var ) - pGetExp( temp, var )) == 1 )
599 return border[num].nf;
600 var--;
601 }
602 }
603 num--;
604 }
605 return fglmVector();
606 }
607
608 void
internalCalculateFunctionals(const ideal,idealFunctionals & l,fglmSdata & data)609 internalCalculateFunctionals( const ideal /*& theIdeal*/, idealFunctionals & l,
610 fglmSdata & data )
611 {
612
613 // insert pOne() into basis and update the workingList:
614 poly one = pOne();
615 data.newBasisElem( one );
616 data.updateCandidates();
617
618 STICKYPROT(".");
619 while ( data.candidatesLeft() == TRUE ) {
620 fglmSelem candidate = data.nextCandidate();
621 if ( candidate.isBasisOrEdge() == TRUE ) {
622 int edge = data.getEdgeNumber( candidate.monom );
623 if ( edge != 0 )
624 {
625 // now candidate is an edge, i.e. we know its normalform:
626 // NF(p) = - ( tail(p)/LC(p) )
627 poly nf = data.getSpanPoly( edge );
628 pNorm( nf );
629 pLmDelete(&nf); //. deletes the leadingmonomial
630 nf= pNeg( nf );
631 fglmVector nfv = data.getVectorRep( nf );
632 l.insertCols( candidate.divisors, nfv );
633 data.newBorderElem( candidate.monom, nfv );
634 pDelete( &nf );
635 STICKYPROT( "+" );
636 }
637 else
638 {
639 int basis= data.newBasisElem( candidate.monom );
640 data.updateCandidates();
641 l.insertCols( candidate.divisors, basis );
642 STICKYPROT( "." );
643 }
644 }
645 else {
646 int var = 0;
647 fglmVector temp = data.getBorderDiv( candidate.monom, var );
648 fglmASSERT( var > 0, "this should never happen" );
649 fglmVector nfv = l.addCols( var, data.getBasisSize(), temp );
650 data.newBorderElem( candidate.monom, nfv );
651 l.insertCols( candidate.divisors, nfv );
652 STICKYPROT( "-" );
653 }
654 candidate.cleanup();
655 } //. while ( data.candidatesLeft() == TRUE )
656 l.endofConstruction();
657 STICKYPROT2( "\nvdim= %i\n", data.getBasisSize() );
658 return;
659 }
660
661 // Calculates the defining Functionals for the ideal "theIdeal" and
662 // returns them in "l".
663 // The ideal has to be zero-dimensional and reduced and has to be a
664 // real subset of the polynomal ring.
665 // In any case it has to be zero-dimensional and minimal (check this
666 // via fglmIdealcheck). Any minimal but not reduced ideal is detected.
667 // In this case it returns FglmNotReduced.
668 // If the base domain is Q, the leading coefficients of the polys
669 // have to be in Z.
670 // returns TRUE if the result is valid, FALSE if theIdeal
671 // is not reduced.
672 static BOOLEAN
CalculateFunctionals(const ideal & theIdeal,idealFunctionals & l)673 CalculateFunctionals( const ideal & theIdeal, idealFunctionals & l )
674 {
675 fglmSdata data( theIdeal );
676 internalCalculateFunctionals( theIdeal, l, data );
677 return ( data.state() );
678 }
679
680 static BOOLEAN
CalculateFunctionals(const ideal & theIdeal,idealFunctionals & l,poly & p,fglmVector & v)681 CalculateFunctionals( const ideal & theIdeal, idealFunctionals & l,
682 poly & p, fglmVector & v )
683 {
684 fglmSdata data( theIdeal );
685 internalCalculateFunctionals( theIdeal, l, data );
686 // STICKYPROT("Calculating vector rep\n");
687 v = data.getVectorRep( p );
688 // if ( v.isZero() )
689 // STICKYPROT("vectorrep is 0\n");
690 return ( data.state() );
691 }
692
693 // ============================================================
694 //! The new basis
695 // ============================================================
696
697 // The declaration of class fglmDelem is found in fglm.h
698
fglmDelem(poly & m,fglmVector mv,int v)699 fglmDelem::fglmDelem( poly & m, fglmVector mv, int v ) : v( mv ), insertions( 0 ), var( v )
700 {
701 monom= m;
702 m= NULL;
703 for ( int k = (currRing->N); k > 0; k-- )
704 if ( pGetExp( monom, k ) > 0 )
705 insertions++;
706 // Wir gehen davon aus, dass ein fglmDelem direkt bei der Erzeugung
707 // auch in eine Liste eingefuegt wird. Daher wird hier automatisch
708 // newDivisor aufgerufen ( v teilt ja m )
709 newDivisor();
710 }
711
712 void
cleanup()713 fglmDelem::cleanup()
714 {
715 if ( monom != NULL )
716 {
717 pLmDelete(&monom);
718 }
719 }
720
721 class oldGaussElem
722 {
723 public:
724 fglmVector v;
725 fglmVector p;
726 number pdenom;
727 number fac;
728
729 #ifndef HAVE_EXPLICIT_CONSTR
oldGaussElem()730 oldGaussElem() : v(), p(), pdenom( NULL ), fac( NULL ) {}
731 #endif
oldGaussElem(const fglmVector newv,const fglmVector newp,number & newpdenom,number & newfac)732 oldGaussElem( const fglmVector newv, const fglmVector newp, number & newpdenom, number & newfac ) : v( newv ), p( newp ), pdenom( newpdenom ), fac( newfac )
733 {
734 newpdenom= NULL;
735 newfac= NULL;
736 }
737 ~oldGaussElem();
738 #ifndef HAVE_EXPLICIT_CONSTR
insertElem(const fglmVector newv,const fglmVector newp,number & newpdenom,number & newfac)739 void insertElem( const fglmVector newv, const fglmVector newp, number & newpdenom, number & newfac )
740 {
741 v= newv;
742 p= newp;
743 pdenom= newpdenom;
744 fac= newfac;
745 newpdenom= NULL;
746 newfac= NULL;
747 }
748 #endif
749 };
750
~oldGaussElem()751 oldGaussElem::~oldGaussElem()
752 {
753 if (fac!=NULL) nDelete( & fac );
754 if (pdenom!=NULL) nDelete( & pdenom );
755 }
756
757
758 class fglmDdata
759 {
760 private:
761 int dimen;
762 oldGaussElem * gauss;
763 BOOLEAN * isPivot; // [1]..[dimen]
764 int * perm; // [1]..[dimen]
765 int basisSize; //. the CURRENT basisSize, i.e. basisSize <= dimen
766 polyset basis; // [1]..[dimen]. The monoms of the new Vectorspace-basis
767
768 int* varpermutation;
769
770 int groebnerBS;
771 int groebnerSize;
772 ideal destId;
773
774 List<fglmDelem> nlist;
775 public:
776 fglmDdata( int dimension );
777 ~fglmDdata();
778
getBasisSize() const779 int getBasisSize() const { return basisSize; }
candidatesLeft() const780 BOOLEAN candidatesLeft() const { return ( nlist.isEmpty() ? FALSE : TRUE ); }
781 fglmDelem nextCandidate();
782 void newBasisElem( poly & m, fglmVector v, fglmVector p, number & denom );
783 void updateCandidates( poly m, const fglmVector v );
784 void newGroebnerPoly( fglmVector & v, poly & p );
785 void gaussreduce( fglmVector & v, fglmVector & p, number & denom );
buildIdeal()786 ideal buildIdeal()
787 {
788 idSkipZeroes( destId );
789 return destId;
790 }
791 };
792
fglmDdata(int dimension)793 fglmDdata::fglmDdata( int dimension )
794 {
795 int k;
796 dimen= dimension;
797 basisSize= 0;
798 //. All arrays run from [1]..[dimen], thus omAlloc( dimen + 1 )!
799 #ifndef HAVE_EXPLICIT_CONSTR
800 gauss= new oldGaussElem[ dimen+1 ];
801 #else
802 gauss= (oldGaussElem *)omAlloc( (dimen+1)*sizeof( oldGaussElem ) );
803 #endif
804 isPivot= (BOOLEAN *)omAlloc( (dimen+1)*sizeof( BOOLEAN ) );
805 for ( k= dimen; k > 0; k-- ) isPivot[k]= FALSE;
806 perm= (int *)omAlloc( (dimen+1)*sizeof( int ) );
807 basis= (polyset)omAlloc( (dimen+1)*sizeof( poly ) );
808 varpermutation = (int*)omAlloc( ((currRing->N)+1)*sizeof(int) );
809 // Sort ring variables by increasing values (because of weighted orderings)
810 ideal perm_id = idMaxIdeal(1);
811 intvec *iv = idSort(perm_id,TRUE);
812 idDelete(&perm_id);
813 for(int i = (currRing->N); i > 0; i--) varpermutation[(currRing->N)+1-i] = (*iv)[i-1];
814 delete iv;
815
816 groebnerBS= 16;
817 groebnerSize= 0;
818 destId= idInit( groebnerBS, 1 );
819 }
820
~fglmDdata()821 fglmDdata::~fglmDdata()
822 {
823 // STICKYPROT2("dimen= %i", dimen);
824 // STICKYPROT2("basisSize= %i", basisSize);
825 // fglmASSERT( dimen == basisSize, "Es wurden nicht alle BasisElemente gefunden!" );
826 int k;
827 #ifndef HAVE_EXPLICIT_CONSTR
828 delete [] gauss;
829 #else
830 // use basisSize instead of dimen because of fglmquot!
831 for ( k= basisSize; k > 0; k-- )
832 gauss[k].~oldGaussElem();
833 omFreeSize( (ADDRESS)gauss, (dimen+1)*sizeof( oldGaussElem ) );
834 #endif
835 omFreeSize( (ADDRESS)isPivot, (dimen+1)*sizeof( BOOLEAN ) );
836 omFreeSize( (ADDRESS)perm, (dimen+1)*sizeof( int ) );
837 // use basisSize instead of dimen because of fglmquot!
838 //. Remember: There is no poly in basis[0], thus k > 0
839 for ( k= basisSize; k > 0; k-- )
840 pLmDelete( basis[k]);
841 omFreeSize( (ADDRESS)basis, (dimen+1)*sizeof( poly ) );
842 omFreeSize( (ADDRESS)varpermutation, ((currRing->N)+1)*sizeof(int) );
843 }
844
845 fglmDelem
nextCandidate()846 fglmDdata::nextCandidate()
847 {
848 fglmDelem result = nlist.getFirst();
849 nlist.removeFirst();
850 return result;
851 }
852
853 void
newBasisElem(poly & m,fglmVector v,fglmVector p,number & denom)854 fglmDdata::newBasisElem( poly & m, fglmVector v, fglmVector p, number & denom )
855 {
856 // inserts m as a new basis monom. m is NOT copied but directly inserted.
857 // returns m=NULL to indicate, that now basis is oweing m.
858 basisSize++;
859 basis[basisSize]= m;
860 m= NULL;
861 int k= 1;
862 while ( nIsZero(v.getconstelem(k)) || isPivot[k] ) {
863 k++;
864 }
865 fglmASSERT( k <= dimen, "Error(1) in fglmDdata::pivot-search");
866 number pivot= v.getconstelem( k );
867 int pivotcol = k;
868 k++;
869 while ( k <= dimen ) {
870 if ( ! nIsZero( v.getconstelem(k) ) && ! isPivot[k] ) {
871 if ( nGreater( v.getconstelem( k ), pivot ) ) {
872 pivot= v.getconstelem( k );
873 pivotcol= k;
874 }
875 }
876 k++;
877 }
878 fglmASSERT( ! nIsZero( pivot ), "Error(2) fglmDdata::Pivotelement ist Null" );
879 isPivot[ pivotcol ]= TRUE;
880 perm[basisSize]= pivotcol;
881
882 pivot= nCopy( v.getconstelem( pivotcol ) );
883 #ifndef HAVE_EXPLICIT_CONSTR
884 gauss[basisSize].insertElem( v, p, denom, pivot );
885 #else
886 gauss[basisSize].oldGaussElem( v, p, denom, pivot );
887 #endif
888 }
889
890 void
updateCandidates(poly m,const fglmVector v)891 fglmDdata::updateCandidates( poly m, const fglmVector v )
892 {
893 ListIterator<fglmDelem> list = nlist;
894 poly newmonom = NULL;
895 int k = (currRing->N);
896 BOOLEAN done = FALSE;
897 int state = 0;
898 while ( k >= 1 )
899 {
900 newmonom = pCopy( m );
901 pIncrExp( newmonom, varpermutation[k] );
902 pSetm( newmonom );
903 done= FALSE;
904 while ( list.hasItem() && (!done) )
905 {
906 if ( (state= pCmp( list.getItem().monom, newmonom )) < 0 )
907 list++;
908 else done= TRUE;
909 }
910 if ( !done )
911 {
912 nlist.append( fglmDelem( newmonom, v, k ) );
913 break;
914 }
915 if ( state == 0 )
916 {
917 list.getItem().newDivisor();
918 pLmDelete( & newmonom );
919 }
920 else
921 {
922 list.insert( fglmDelem( newmonom, v, k ) );
923 }
924 k--;
925 }
926 while ( --k >= 1 )
927 {
928 newmonom= pCopy( m );
929 pIncrExp( newmonom, varpermutation[k] );
930 pSetm( newmonom );
931 nlist.append( fglmDelem( newmonom, v, k ) );
932 }
933 }
934
935 void
newGroebnerPoly(fglmVector & p,poly & m)936 fglmDdata::newGroebnerPoly( fglmVector & p, poly & m )
937 // Inserts gp = p[1]*basis(1)+..+p[basisSize]*basis(basisSize)+p[basisSize+1]*m as
938 // a new groebner polynomial for the ideal.
939 // All elements (monomials and coefficients) of gp are copied, instead of m.
940 // Assumes that p.length() == basisSize+1.
941 {
942 //. Baue das Polynom von oben nach unten:
943 fglmASSERT( p.size() == basisSize+1, "GP::newGroebnerPoly: p has wrong size" );
944 int k;
945 poly result = m;
946 poly temp = result;
947 m= NULL;
948 if ( nGetChar() > 0 ) {
949 number lead = nCopy( p.getconstelem( basisSize+1 ) );
950 p /= lead;
951 nDelete( & lead );
952 }
953 if ( nGetChar() == 0 ) {
954 number gcd= p.gcd();
955 fglmASSERT( ! nIsZero( gcd ), "FATAL: gcd and thus p is zero" );
956 if ( ! nIsOne( gcd ) )
957 p /= gcd;
958 nDelete( & gcd );
959 }
960 pSetCoeff( result, nCopy( p.getconstelem( basisSize+1 ) ) );
961 for ( k= basisSize; k > 0; k-- ) {
962 if ( ! nIsZero( p.getconstelem( k ) ) ) {
963 temp->next= pCopy( basis[k] );
964 pIter( temp );
965 pSetCoeff( temp, nCopy( p.getconstelem( k ) ) );
966 }
967 }
968 pSetm( result );
969 if ( ! nGreaterZero( pGetCoeff( result ) ) ) result= pNeg( result );
970 if ( groebnerSize == IDELEMS( destId ) ) {
971 pEnlargeSet( & destId->m, IDELEMS( destId ), groebnerBS );
972 IDELEMS( destId )+= groebnerBS;
973 }
974 (destId->m)[groebnerSize]= result;
975 groebnerSize++;
976 }
977
978 void
gaussreduce(fglmVector & v,fglmVector & p,number & pdenom)979 fglmDdata::gaussreduce( fglmVector & v, fglmVector & p, number & pdenom )
980 {
981 int k;
982 number fac1, fac2;
983 number temp;
984 fglmASSERT( pdenom == NULL, "pdenom in gaussreduce should be NULL" );
985 pdenom= nInit( 1 );
986 number vdenom = v.clearDenom();
987 if ( ! nIsZero( vdenom ) && ! nIsOne( vdenom ) ) {
988 p.setelem( p.size(), vdenom );
989 }
990 else {
991 nDelete( &vdenom );
992 }
993 number gcd = v.gcd();
994 if ( ! nIsZero( gcd ) && ! nIsOne( gcd ) ) {
995 v /= gcd;
996 number temp= nMult( pdenom, gcd );
997 nDelete( &pdenom );
998 pdenom= temp;
999 }
1000 nDelete( & gcd );
1001
1002 for ( k= 1; k <= basisSize; k++ ) {
1003
1004 if ( ! v.elemIsZero( perm[k] ) ) {
1005 fac1= gauss[k].fac;
1006 fac2= nCopy( v.getconstelem( perm[k] ) );
1007 v.nihilate( fac1, fac2, gauss[k].v );
1008 fac1= nMult( fac1, gauss[k].pdenom );
1009 temp= nMult( fac2, pdenom );
1010 nDelete( &fac2 );
1011 fac2= temp;
1012 p.nihilate( fac1, fac2, gauss[k].p );
1013 temp= nMult( pdenom, gauss[k].pdenom );
1014 nDelete( &pdenom );
1015 pdenom= temp;
1016
1017 nDelete( & fac1 );
1018 nDelete( & fac2 );
1019 number gcd = v.gcd();
1020 if ( ! nIsZero( gcd ) && ! nIsOne( gcd ) )
1021 {
1022 v /= gcd;
1023 number temp= nMult( pdenom, gcd );
1024 nDelete( &pdenom );
1025 pdenom= temp;
1026 }
1027 nDelete( & gcd );
1028 gcd= p.gcd();
1029 temp= n_SubringGcd( pdenom, gcd, currRing->cf );
1030 nDelete( &gcd );
1031 gcd= temp;
1032 if ( ! nIsZero( gcd ) && ! nIsOne( gcd ) )
1033 {
1034 p /= gcd;
1035 temp= nDiv( pdenom, gcd );
1036 nDelete( & pdenom );
1037 pdenom= temp;
1038 nNormalize( pdenom );
1039 }
1040 nDelete( & gcd );
1041 }
1042 }
1043 }
1044
1045 static ideal
GroebnerViaFunctionals(const idealFunctionals & l,fglmVector iv=fglmVector ())1046 GroebnerViaFunctionals( const idealFunctionals & l,
1047 fglmVector iv = fglmVector() )
1048 // If iv is zero, calculates the groebnerBasis for the ideal which is
1049 // defined by l.
1050 // If iv is not zero, then the groebnerBasis if i:p is calculated where
1051 // i is defined by l and iv is the vector-representation of nf(p) wrt. i
1052 // The dimension of l has to be finite.
1053 // The result is in reduced form.
1054 {
1055 fglmDdata data( l.dimen() );
1056
1057 // insert pOne() and update workinglist according to iv:
1058 fglmVector initv;
1059 if ( iv.isZero() ) {
1060 // STICKYPROT("initv is zero\n");
1061 initv = fglmVector( l.dimen(), 1 );
1062 }
1063 else {
1064 // STICKYPROT("initv is not zero\n");
1065 initv = iv;
1066 }
1067
1068 poly one = pOne();
1069 data.updateCandidates( one, initv );
1070 number nOne = nInit( 1 );
1071 data.newBasisElem( one, initv, fglmVector( 1, 1 ), nOne );
1072 STICKYPROT( "." );
1073 while ( data.candidatesLeft() == TRUE ) {
1074 fglmDelem candidate = data.nextCandidate();
1075 if ( candidate.isBasisOrEdge() == TRUE ) {
1076 // Now we have the chance to find a new groebner polynomial
1077
1078 // v is the vector-representation of candidate.monom
1079 // some elements of v are zeroed in data.gaussreduce(). Which
1080 // ones and how this was done is stored in p.
1081 // originalV containes the unchanged v, which is later inserted
1082 // into the working list (via data.updateCandidates().
1083 fglmVector v = l.multiply( candidate.v, candidate.var );
1084 fglmVector originalV = v;
1085 fglmVector p( data.getBasisSize()+1, data.getBasisSize()+1 );
1086 number pdenom = NULL;
1087 data.gaussreduce( v, p, pdenom );
1088 if ( v.isZero() ) {
1089 // Now v is linear dependend to the already found basis elements.
1090 // This means that v (rsp. candidate.monom) is the leading
1091 // monomial of the next groebner-basis polynomial.
1092 data.newGroebnerPoly( p, candidate.monom );
1093 nDelete( & pdenom );
1094 STICKYPROT( "+" );
1095 }
1096 else {
1097 // no linear dependence could be found, so v ( rsp. monom )
1098 // is a basis monomial. We store the zeroed version ( i.e. v
1099 // and not originalV ) as well as p, the denomiator and all
1100 // the other stuff.
1101 // erst updateCandidates, dann newBasisELem!!!
1102 data.updateCandidates( candidate.monom, originalV );
1103 data.newBasisElem( candidate.monom, v, p, pdenom );
1104 STICKYPROT( "." );
1105 }
1106 }
1107 else {
1108 STICKYPROT( "-" );
1109 candidate.cleanup();
1110 }
1111 } //. while data.candidatesLeft()
1112 STICKYPROT( "\n" );
1113 return ( data.buildIdeal() );
1114 }
1115 //<-
1116
1117 static ideal
FindUnivariatePolys(const idealFunctionals & l)1118 FindUnivariatePolys( const idealFunctionals & l )
1119 {
1120 fglmVector v;
1121 fglmVector p;
1122 ideal destIdeal = idInit( (currRing->N), 1 );
1123
1124 int i;
1125 BOOLEAN isZero;
1126 int *varpermutation = (int*)omAlloc( ((currRing->N)+1)*sizeof(int) );
1127 ideal perm = idMaxIdeal(1);
1128 intvec *iv = idSort(perm,TRUE);
1129 idDelete(&perm);
1130 for(i = (currRing->N); i > 0; i--) varpermutation[(currRing->N)+1-i] = (*iv)[i-1];
1131 delete iv;
1132
1133 for (i= 1; i <= (currRing->N); i++ )
1134 {
1135 // main loop
1136 STICKYPROT2( "(%i)", i /*varpermutation[i]*/);
1137 gaussReducer gauss( l.dimen() );
1138 isZero= FALSE;
1139 v= fglmVector( l.dimen(), 1 );
1140 while ( !isZero )
1141 {
1142 if ( (isZero= gauss.reduce( v )))
1143 {
1144 STICKYPROT( "+" );
1145 p= gauss.getDependence();
1146 number gcd= p.gcd();
1147 if ( ! nIsOne( gcd ) )
1148 {
1149 p /= gcd;
1150 }
1151 nDelete( & gcd );
1152 int k;
1153 poly temp = NULL;
1154 poly result=NULL;
1155 for ( k= p.size(); k > 0; k-- )
1156 {
1157 number n = nCopy( p.getconstelem( k ) );
1158 if ( ! nIsZero( n ) )
1159 {
1160 if ( temp == NULL )
1161 {
1162 result= pOne();
1163 temp= result;
1164 }
1165 else
1166 {
1167 temp->next= pOne();
1168 pIter( temp );
1169 }
1170 pSetCoeff( temp, n );
1171 pSetExp( temp, i /*varpermutation[i]*/, k-1 );
1172 pSetm( temp );
1173 }
1174 }
1175 if ( ! nGreaterZero( pGetCoeff( result ) ) ) result= pNeg( result );
1176 (destIdeal->m)[i-1]= result;
1177 }
1178 else
1179 {
1180 STICKYPROT( "." );
1181 gauss.store();
1182 v= l.multiply( v, i /*varpermutation[i]*/ );
1183 }
1184 }
1185 }
1186 STICKYPROT( "\n" );
1187 omFreeSize( (ADDRESS)varpermutation, ((currRing->N)+1)*sizeof(int) );
1188 return destIdeal;
1189 }
1190
1191 // for a descritption of the parameters see fglm.h
1192 BOOLEAN
fglmzero(ring sourceRing,ideal & sourceIdeal,ring destRing,ideal & destIdeal,BOOLEAN switchBack,BOOLEAN deleteIdeal)1193 fglmzero( ring sourceRing, ideal & sourceIdeal, ring destRing, ideal & destIdeal, BOOLEAN switchBack, BOOLEAN deleteIdeal )
1194 {
1195 ring initialRing = currRing;
1196 BOOLEAN fglmok;
1197
1198 if ( currRing != sourceRing )
1199 {
1200 rChangeCurrRing( sourceRing );
1201 }
1202 idealFunctionals L( 100, rVar(currRing) );
1203 fglmok = CalculateFunctionals( sourceIdeal, L );
1204 if ( deleteIdeal == TRUE )
1205 idDelete( & sourceIdeal );
1206 rChangeCurrRing( destRing );
1207 if ( fglmok == TRUE )
1208 {
1209 L.map( sourceRing );
1210 destIdeal= GroebnerViaFunctionals( L );
1211 }
1212 if ( (switchBack) && (currRing != initialRing) )
1213 rChangeCurrRing( initialRing );
1214 return fglmok;
1215 }
1216
1217 BOOLEAN
fglmquot(ideal sourceIdeal,poly quot,ideal & destIdeal)1218 fglmquot( ideal sourceIdeal, poly quot, ideal & destIdeal)
1219 {
1220 BOOLEAN fglmok;
1221 fglmVector v;
1222
1223 idealFunctionals L( 100, (currRing->N) );
1224 // STICKYPROT("calculating normal form\n");
1225 // poly p = kNF( sourceIdeal, currRing->qideal, quot );
1226 // STICKYPROT("calculating functionals\n");
1227 fglmok = CalculateFunctionals( sourceIdeal, L, quot, v );
1228 if ( fglmok == TRUE ) {
1229 // STICKYPROT("calculating groebner basis\n");
1230 destIdeal= GroebnerViaFunctionals( L, v );
1231 }
1232 return fglmok;
1233 }
1234
1235 BOOLEAN
FindUnivariateWrapper(ideal source,ideal & destIdeal)1236 FindUnivariateWrapper( ideal source, ideal & destIdeal )
1237 {
1238 BOOLEAN fglmok;
1239
1240 idealFunctionals L( 100, (currRing->N) );
1241 fglmok = CalculateFunctionals( source, L );
1242 if ( fglmok == TRUE ) {
1243 destIdeal= FindUnivariatePolys( L );
1244 return TRUE;
1245 }
1246 else
1247 return FALSE;
1248 }
1249
1250 // ----------------------------------------------------------------------------
1251 // Local Variables: ***
1252 // compile-command: "make Singular" ***
1253 // page-delimiter: "^\\(\\|//!\\)" ***
1254 // fold-internal-margins: nil ***
1255 // End: ***
1256