001/*
002 * $Id: GBAlgorithmBuilder.java 5868 2018-07-20 15:44:13Z kredel $
003 */
004
005package edu.jas.application;
006
007
008import java.io.Serializable;
009
010import org.apache.logging.log4j.Logger;
011import org.apache.logging.log4j.LogManager; 
012
013import edu.jas.arith.BigInteger;
014import edu.jas.arith.BigRational;
015import edu.jas.gb.GBOptimized;
016import edu.jas.gb.GBProxy;
017import edu.jas.gb.GroebnerBaseAbstract;
018import edu.jas.gb.GroebnerBaseParallel;
019import edu.jas.gb.GroebnerBaseSeqIter;
020import edu.jas.gb.GroebnerBaseF5zSigSeqIter;
021import edu.jas.gb.GroebnerBaseGGVSigSeqIter;
022import edu.jas.gb.GroebnerBaseArriSigSeqIter;
023import edu.jas.gb.GroebnerBaseParIter;
024import edu.jas.gb.OrderedMinPairlist;
025import edu.jas.gb.OrderedPairlist;
026import edu.jas.gb.OrderedSyzPairlist;
027import edu.jas.gb.PairList;
028import edu.jas.gbufd.GBFactory;
029import edu.jas.gbufd.GroebnerBaseFGLM;
030import edu.jas.gbufd.GroebnerBaseWalk;
031import edu.jas.gbufd.GroebnerBasePseudoParallel;
032import edu.jas.gbufd.GroebnerBaseQuotient;
033import edu.jas.gbufd.GroebnerBaseRational;
034import edu.jas.kern.ComputerThreads;
035import edu.jas.poly.GenPolynomial;
036import edu.jas.poly.GenPolynomialRing;
037import edu.jas.structure.GcdRingElem;
038import edu.jas.structure.RingFactory;
039import edu.jas.ufd.Quotient;
040import edu.jas.ufd.QuotientRing;
041
042
043/**
044 * Builder for commutative Gröbner bases algorithm implementations.
045 * @author Heinz Kredel
046 * @usage To create objects that implement the <code>GroebnerBase</code>
047 *        interface one can use the <code>GBFactory</code> or this
048 *        <code>GBAlgorithmBuilder</code>. This class will select and compose an
049 *        appropriate implementation based on the types of polynomial
050 *        coefficients C and the desired properties. To build an implementation
051 *        start with the static method <code>polynomialRing()</code> to define
052 *        the polynomial ring. Then continue to construct the algorithm with the
053 *        methods
054 *        <ul>
055 *        <li><code>optimize()</code> or <code>optimize(boolean)</code> for term
056 *        order (variable order) optimization (true for return of permuted
057 *        polynomials),</li>
058 *        <li><code>normalPairlist()</code> (default),
059 *        <code>syzygyPairlist()</code> or <code>simplePairlist()</code> for
060 *        pair-list selection strategies,</li>
061 *        <li><code>fractionFree()</code> for clearing denominators and
062 *        computing with pseudo reduction,</li>
063 *        <li><code>graded()</code> for using the FGLM algorithm to first
064 *        compute a Gr&ouml;bner base with respect to a graded term order and
065 *        then constructing a Gr&ouml;bner base wrt. a lexicographical term
066 *        order,</li>
067 *        <li><code>walk()</code> for using the Gr&ouml;bner walk algorithm to first
068 *        compute a Gr&ouml;bner base with respect to a graded term order and
069 *        then constructing a Gr&ouml;bner base wrt. a lexicographical term
070 *        order,</li>
071 *        <li><code>iterated()</code> for using the iterative GB algorithm to 
072 *        compute a Gr&ouml;bner base adding one polynomial after another,</li>
073 *        <li><code>F5()</code>, <code>GGV()</code> and <code>Arri()</code> for using 
074 *        the respective iterative signature based GB algorithm (over field coefficients) to 
075 *        compute a Gr&ouml;bner base adding one polynomial after another,</li>
076 *        <li><code>parallel()</code> additionaly compute a Gr&ouml;bner base
077 *        over a field or integral domain in parallel,</li>
078 *        <li><code>euclideanDomain()</code> for computing a e-Gr&ouml;bner
079 *        base,</li>
080 *        <li><code>domainAlgorithm(Algo)</code> for computing a d- or
081 *        e-Gr&ouml;bner base,</li>
082 *        </ul>
083 *        Finally call the method <code>build()</code> to obtain an
084 *        implementaton of class <code>GroebnerBaseAbstract</code>. For example
085 * 
086 *        <pre>
087 * 
088 * GenPolynomialRing&lt;C&gt; pf = new GenPolynomialRing&lt;C&gt;(cofac, vars);
089 * GroebnerBaseAbstract&lt;C&gt; engine;
090 * engine = GBAlgorithmBuilder.&lt;C&gt; polynomialRing(pf).fractionFree().parallel().optimize().build();
091 * c = engine.GB(A);
092 * </pre>
093 * 
094 *        For example, if the coefficient type is BigRational, the usage looks
095 *        like
096 * 
097 *        <pre>
098 * 
099 * GenPolynomialRing&lt;BigRational&gt; pf = new GenPolynomialRing&lt;BigRational&gt;(cofac, vars);
100 * GroebnerBaseAbstract&lt;BigRational&gt; engine;
101 * engine = GBAlgorithmBuilder.&lt;BigRational&gt; polynomialRing(pf).fractionFree().parallel().optimize().build();
102 * c = engine.GB(A);
103 * </pre>
104 * 
105 *        <b>Note:</b> Not all combinations are meanigful
106 * 
107 * @see edu.jas.gb.GroebnerBase
108 * @see edu.jas.gbufd.GBFactory
109 */
110
111public class GBAlgorithmBuilder<C extends GcdRingElem<C>> implements Serializable {
112
113
114    private static final Logger logger = LogManager.getLogger(GBAlgorithmBuilder.class);
115
116
117    /**
118     * The current GB algorithm implementation.
119     */
120    private GroebnerBaseAbstract<C> algo;
121
122
123    /**
124     * The current polynomial ring.
125     */
126    public final GenPolynomialRing<C> ring;
127
128
129    /**
130     * Requested pairlist strategy.
131     */
132    public final PairList<C> strategy;
133
134
135    /**
136     * Constructor not for use.
137     */
138    protected GBAlgorithmBuilder() {
139        throw new IllegalArgumentException("do not use this constructor");
140    }
141
142
143    /**
144     * Constructor.
145     * @param ring the polynomial ring.
146     */
147    public GBAlgorithmBuilder(GenPolynomialRing<C> ring) {
148        this(ring, null);
149    }
150
151
152    /**
153     * Constructor.
154     * @param ring the polynomial ring.
155     * @param algo already determined algorithm.
156     */
157    public GBAlgorithmBuilder(GenPolynomialRing<C> ring, GroebnerBaseAbstract<C> algo) {
158        this(ring, algo, null);
159    }
160
161
162    /**
163     * Constructor.
164     * @param ring the polynomial ring.
165     * @param algo already determined algorithm.
166     * @param strategy pairlist strategy.
167     */
168    public GBAlgorithmBuilder(GenPolynomialRing<C> ring, GroebnerBaseAbstract<C> algo, PairList<C> strategy) {
169        if (ring == null) {
170            throw new IllegalArgumentException("ring may not be null");
171        }
172        this.ring = ring;
173        if (strategy == null) {
174            strategy = new OrderedPairlist<C>();
175        } else {
176            if (algo == null) { // or overwrite?
177                algo = GBFactory.<C> getImplementation(ring.coFac, strategy);
178            }
179        }
180        this.algo = algo; // null accepted
181        this.strategy = strategy;
182    }
183
184
185    /**
186     * Build the GB algorithm implementaton.
187     * @return GB algorithm implementaton as GroebnerBaseAbstract object.
188     */
189    public GroebnerBaseAbstract<C> build() {
190        if (algo == null) {
191            if (strategy == null) { // should not happen
192                algo = GBFactory.<C> getImplementation(ring.coFac);
193            } else {
194                algo = GBFactory.<C> getImplementation(ring.coFac, strategy);
195            }
196        }
197        return algo;
198    }
199
200
201    /**
202     * Define polynomial ring.
203     * @param fac the commutative polynomial ring.
204     * @return GBAlgorithmBuilder object.
205     */
206    public static <C extends GcdRingElem<C>> GBAlgorithmBuilder<C> polynomialRing(GenPolynomialRing<C> fac) {
207        return new GBAlgorithmBuilder<C>(fac);
208    }
209
210
211    /**
212     * Select syzygy critical pair-list strategy. Gebauer and M&ouml;ller
213     * algorithm.
214     * @return GBAlgorithmBuilder object.
215     */
216    public GBAlgorithmBuilder<C> syzygyPairlist() {
217        return new GBAlgorithmBuilder<C>(ring, algo, new OrderedSyzPairlist<C>());
218    }
219
220
221    /**
222     * Select normal critical pair-list strategy. Buchberger, Winkler and Kredel
223     * algorithm.
224     * @return GBAlgorithmBuilder object.
225     */
226    public GBAlgorithmBuilder<C> normalPairlist() {
227        return new GBAlgorithmBuilder<C>(ring, algo, new OrderedPairlist<C>());
228    }
229
230
231    /**
232     * Select simple critical pair-list strategy. Original Buchberger algorithm.
233     * @return GBAlgorithmBuilder object.
234     */
235    public GBAlgorithmBuilder<C> simplePairlist() {
236        return new GBAlgorithmBuilder<C>(ring, algo, new OrderedMinPairlist<C>());
237    }
238
239
240    /**
241     * Request term order optimization. Call optimize(true) for return of
242     * permuted polynomials.
243     * @return GBAlgorithmBuilder object.
244     */
245    public GBAlgorithmBuilder<C> optimize() {
246        return optimize(true);
247    }
248
249
250    /**
251     * Request term order optimization.
252     * @param rP true for return of permuted polynomials, false for inverse
253     *            permuted polynomials and new GB computation.
254     * @return GBAlgorithmBuilder object.
255     */
256    public GBAlgorithmBuilder<C> optimize(boolean rP) {
257        if (algo == null) {
258            algo = GBFactory.<C> getImplementation(ring.coFac, strategy);
259        }
260        GroebnerBaseAbstract<C> bb = new GBOptimized<C>(algo, rP);
261        return new GBAlgorithmBuilder<C>(ring, bb, strategy);
262    }
263
264
265    /**
266     * Request fraction free algorithm. For BigRational and Quotient
267     * coefficients denominators are cleared and pseudo reduction is used.
268     * @return GBAlgorithmBuilder object.
269     */
270    @SuppressWarnings({"cast", "unchecked"})
271    public GBAlgorithmBuilder<C> fractionFree() {
272        if (algo != null) {
273            logger.warn("selected algorithm ignored: " + algo + ", use fractionFree before");
274        }
275        if (((Object) ring.coFac) instanceof BigRational) {
276            BigRational cf = (BigRational) (Object) ring.coFac;
277            PairList<BigRational> sty = (PairList) strategy;
278            GroebnerBaseAbstract<BigRational> bb = GBFactory.getImplementation(cf, GBFactory.Algo.ffgb, sty);
279            GroebnerBaseAbstract<C> cbb = (GroebnerBaseAbstract<C>) (GroebnerBaseAbstract) bb;
280            return new GBAlgorithmBuilder<C>(ring, cbb, strategy);
281        }
282        if (((Object) ring.coFac) instanceof QuotientRing) {
283            QuotientRing<C> cf = (QuotientRing<C>) (Object) ring.coFac;
284            PairList<Quotient<C>> sty = (PairList) strategy;
285            GroebnerBaseAbstract<Quotient<C>> bb = GBFactory.<C> getImplementation(cf, GBFactory.Algo.ffgb,
286                            sty);
287            GroebnerBaseAbstract<C> cbb = (GroebnerBaseAbstract<C>) (GroebnerBaseAbstract) bb;
288            return new GBAlgorithmBuilder<C>(ring, cbb, strategy);
289        }
290        logger.warn("no fraction free algorithm implemented for " + ring);
291        return this;
292    }
293
294
295    /**
296     * Request e-GB algorithm.
297     * @return GBAlgorithmBuilder object.
298     */
299    public GBAlgorithmBuilder<C> euclideanDomain() {
300        return domainAlgorithm(GBFactory.Algo.egb);
301    }
302
303
304    /**
305     * Request d-, e- or i-GB algorithm.
306     * @param a algorithm from GBFactory.Algo.
307     * @return GBAlgorithmBuilder object.
308     */
309    @SuppressWarnings({"cast", "unchecked"})
310    public GBAlgorithmBuilder<C> domainAlgorithm(GBFactory.Algo a) {
311        if (strategy != null) {
312            logger.warn("strategy " + strategy + " ignored for algorithm " + a);
313        }
314        if (((Object) ring.coFac) instanceof BigInteger) {
315            BigInteger cf = (BigInteger) (Object) ring.coFac;
316            GroebnerBaseAbstract<BigInteger> bb = GBFactory.getImplementation(cf, a);
317            GroebnerBaseAbstract<C> cbb = (GroebnerBaseAbstract<C>) (GroebnerBaseAbstract) bb;
318            return new GBAlgorithmBuilder<C>(ring, cbb);
319        }
320        if (((Object) ring.coFac) instanceof GenPolynomial) {
321            GenPolynomialRing<C> cf = (GenPolynomialRing) (Object) ring.coFac;
322            GroebnerBaseAbstract<GenPolynomial<C>> bb = GBFactory.<C> getImplementation(cf, a);
323            GroebnerBaseAbstract<C> cbb = (GroebnerBaseAbstract<C>) (GroebnerBaseAbstract) bb;
324            return new GBAlgorithmBuilder<C>(ring, cbb);
325        }
326        logger.warn("no domain algorithm implemented for " + ring);
327        return this;
328    }
329
330
331    /**
332     * Request parallel algorithm. Additionaly run a parallel algorithm via
333     * GBProxy.
334     * @return GBAlgorithmBuilder object.
335     */
336    @SuppressWarnings("unchecked")
337    public GBAlgorithmBuilder<C> parallel() {
338        return parallel(ComputerThreads.N_CPUS);
339    }
340
341
342    /**
343     * Request parallel algorithm. Additionaly run a parallel algorithm via
344     * GBProxy.
345     * @param threads number of threads requested.
346     * @return GBAlgorithmBuilder object.
347     */
348    @SuppressWarnings({"cast", "unchecked"})
349    public GBAlgorithmBuilder<C> parallel(int threads) {
350        if (ComputerThreads.NO_THREADS) {
351            logger.warn("parallel algorithms disabled");
352            return this;
353        }
354        if (algo == null) {
355            algo = GBFactory.<C> getImplementation(ring.coFac, strategy);
356        }
357        if (algo instanceof GroebnerBaseSeqIter) { // iterative requested
358            GroebnerBaseAbstract<C> bb;
359            bb = (GroebnerBaseAbstract) new GroebnerBaseParIter<C>(threads, strategy);
360            GroebnerBaseAbstract<C> pbb = new GBProxy<C>(algo, bb);
361            return new GBAlgorithmBuilder<C>(ring, pbb, strategy);
362        } else if (((RingFactory) ring.coFac) instanceof BigRational) {
363            GroebnerBaseAbstract<C> bb;
364            if (algo instanceof GroebnerBaseRational) { // fraction free requested
365                PairList<BigInteger> pli;
366                if (strategy instanceof OrderedMinPairlist) {
367                    pli = new OrderedMinPairlist<BigInteger>();
368                } else if (strategy instanceof OrderedSyzPairlist) {
369                    pli = new OrderedSyzPairlist<BigInteger>();
370                } else {
371                    pli = new OrderedPairlist<BigInteger>();
372                }
373                bb = (GroebnerBaseAbstract) new GroebnerBaseRational<BigRational>(threads, pli);
374            } else {
375                bb = (GroebnerBaseAbstract) new GroebnerBaseParallel<C>(threads, strategy);
376            }
377            GroebnerBaseAbstract<C> pbb = new GBProxy<C>(algo, bb);
378            return new GBAlgorithmBuilder<C>(ring, pbb, strategy);
379        } else if (((RingFactory) ring.coFac) instanceof QuotientRing) {
380            GroebnerBaseAbstract<C> bb;
381            if (algo instanceof GroebnerBaseQuotient) { // fraction free requested
382                PairList<GenPolynomial<C>> pli;
383                if (strategy instanceof OrderedMinPairlist) {
384                    pli = new OrderedMinPairlist<GenPolynomial<C>>();
385                } else if (strategy instanceof OrderedSyzPairlist) {
386                    pli = new OrderedSyzPairlist<GenPolynomial<C>>();
387                } else {
388                    pli = new OrderedPairlist<GenPolynomial<C>>();
389                }
390                QuotientRing<C> fac = (QuotientRing) ring.coFac;
391                bb = (GroebnerBaseAbstract) new GroebnerBaseQuotient<C>(threads, fac, pli); // pl not possible
392            } else {
393                bb = (GroebnerBaseAbstract) new GroebnerBaseParallel<C>(threads, strategy);
394            }
395            GroebnerBaseAbstract<C> pbb = new GBProxy<C>(algo, bb);
396            return new GBAlgorithmBuilder<C>(ring, pbb);
397        } else if (ring.coFac.isField()) {
398            GroebnerBaseAbstract<C> bb = new GroebnerBaseParallel<C>(threads, strategy);
399            GroebnerBaseAbstract<C> pbb = new GBProxy<C>(algo, bb);
400            return new GBAlgorithmBuilder<C>(ring, pbb, strategy);
401        } else if (ring.coFac.getONE() instanceof GcdRingElem) {
402            GroebnerBaseAbstract<C> bb = new GroebnerBasePseudoParallel<C>(threads, ring.coFac, strategy);
403            GroebnerBaseAbstract<C> pbb = new GBProxy<C>(algo, bb);
404            return new GBAlgorithmBuilder<C>(ring, pbb, strategy);
405        }
406        logger.warn("no parallel algorithm implemented for " + ring);
407        return this;
408    }
409
410
411    /**
412     * Request FGLM algorithm.
413     * @return GBAlgorithmBuilder object.
414     */
415    @SuppressWarnings("unchecked")
416    public GBAlgorithmBuilder<C> graded() {
417        if (ring.coFac.isField()) {
418            GroebnerBaseAbstract<C> bb;
419            if (algo == null) {
420                bb = new GroebnerBaseFGLM<C>();
421            } else {
422                bb = new GroebnerBaseFGLM<C>(algo);
423            }
424            return new GBAlgorithmBuilder<C>(ring, bb, strategy);
425        }
426        logger.warn("no FGLM algorithm implemented for " + ring);
427        return this;
428    }
429
430
431    /**
432     * Request Groebner walk algorithm.
433     * @return GBAlgorithmBuilder object.
434     */
435    @SuppressWarnings("unchecked")
436    public GBAlgorithmBuilder<C> walk() {
437        if (ring.coFac.isField()) {
438            GroebnerBaseAbstract<C> bb;
439            if (algo == null) {
440                bb = new GroebnerBaseWalk<C>();
441            } else {
442                bb = new GroebnerBaseWalk<C>(algo);
443            }
444            return new GBAlgorithmBuilder<C>(ring, bb, strategy);
445        }
446        logger.warn("no Groebner walk algorithm implemented for " + ring);
447        return this;
448    }
449
450
451    /**
452     * Request iterated GB algorithm.
453     * @return GBAlgorithmBuilder object.
454     */
455    @SuppressWarnings("unchecked")
456    public GBAlgorithmBuilder<C> iterated() {
457        if (ring.coFac.isField()) {
458            GroebnerBaseAbstract<C> bb;
459            bb = new GroebnerBaseSeqIter<C>(strategy); 
460            // if (algo instanceof GBProxy) ... assemble parallel todo
461            if (algo != null) {
462                logger.warn("algorithm " + algo + " ignored for " + bb);
463            }
464            return new GBAlgorithmBuilder<C>(ring, bb, strategy);
465        }
466        logger.warn("no iterated GB algorithm implemented for " + ring);
467        return this;
468    }
469
470
471    /**
472     * Request iterated F5 signature based GB algorithm.
473     * @return GBAlgorithmBuilder object.
474     */
475    @SuppressWarnings("unchecked")
476    public GBAlgorithmBuilder<C> F5() {
477        if (ring.coFac.isField()) {
478            GroebnerBaseAbstract<C> bb;
479            bb = new GroebnerBaseF5zSigSeqIter<C>(); 
480            // if (algo instanceof GBProxy) ... assemble parallel todo
481            if (algo != null) {
482                logger.warn("algorithm " + algo + " ignored for " + bb);
483            }
484            if (strategy != null) {
485                logger.warn("strategy " + strategy + " ignored for " + bb);
486            }
487            return new GBAlgorithmBuilder<C>(ring, bb, strategy);
488        }
489        logger.warn("no iterated F5 GB algorithm implemented for " + ring);
490        return this;
491    }
492
493
494    /**
495     * Request iterated GGV signature based GB algorithm.
496     * @return GBAlgorithmBuilder object.
497     */
498    @SuppressWarnings("unchecked")
499    public GBAlgorithmBuilder<C> GGV() {
500        if (ring.coFac.isField()) {
501            GroebnerBaseAbstract<C> bb;
502            bb = new GroebnerBaseGGVSigSeqIter<C>(); 
503            // if (algo instanceof GBProxy) ... assemble parallel todo
504            if (algo != null) {
505                logger.warn("algorithm " + algo + " ignored for " + bb);
506            }
507            if (strategy != null) {
508                logger.warn("strategy " + strategy + " ignored for " + bb);
509            }
510            return new GBAlgorithmBuilder<C>(ring, bb, strategy);
511        }
512        logger.warn("no iterated GGV GB algorithm implemented for " + ring);
513        return this;
514    }
515
516
517    /**
518     * Request iterated Arri signature based GB algorithm.
519     * @return GBAlgorithmBuilder object.
520     */
521    @SuppressWarnings("unchecked")
522    public GBAlgorithmBuilder<C> Arri() {
523        if (ring.coFac.isField()) {
524            GroebnerBaseAbstract<C> bb;
525            bb = new GroebnerBaseArriSigSeqIter<C>(); 
526            // if (algo instanceof GBProxy) ... assemble parallel todo
527            if (algo != null) {
528                logger.warn("algorithm " + algo + " ignored for " + bb);
529            }
530            if (strategy != null) {
531                logger.warn("strategy " + strategy + " ignored for " + bb);
532            }
533            return new GBAlgorithmBuilder<C>(ring, bb, strategy);
534        }
535        logger.warn("no iterated Arri GB algorithm implemented for " + ring);
536        return this;
537    }
538
539
540    /**
541     * String representation of the GB algorithm implementation.
542     * @see java.lang.Object#toString()
543     */
544    @Override
545    public String toString() {
546        StringBuffer s = new StringBuffer(" ");
547        if (algo != null) {
548            s.append(algo.toString());
549            s.append(" for ");
550        }
551        s.append(ring.toString());
552        if (strategy != null) {
553            s.append(" strategy=");
554            s.append(strategy.toString());
555        }
556        return s.toString();
557    }
558
559
560    /**
561     * Get a scripting compatible string representation.
562     * @return script compatible representation for this Element.
563     * @see edu.jas.structure.Element#toScript()
564     */
565    public String toScript() {
566        // Python case
567        StringBuffer s = new StringBuffer(" ");
568        if (algo != null) {
569            s.append(algo.toString()); // nonsense
570            s.append(" ");
571        }
572        s.append(ring.toScript());
573        if (strategy != null) {
574            s.append(",strategy=");
575            s.append(strategy.toString());
576        }
577        return s.toString();
578    }
579
580}