001/*
002 * $Id: GroebnerBasePseudoRecParallel.java 5061 2015-01-01 19:45:33Z kredel $
003 */
004
005package edu.jas.gbufd;
006
007
008import java.util.ArrayList;
009import java.util.Collections;
010import java.util.List;
011import java.util.concurrent.Semaphore;
012
013import org.apache.log4j.Logger;
014
015import edu.jas.gb.GroebnerBaseAbstract;
016import edu.jas.gb.OrderedPairlist;
017import edu.jas.gb.Pair;
018import edu.jas.gb.PairList;
019import edu.jas.poly.ExpVector;
020import edu.jas.poly.GenPolynomial;
021import edu.jas.poly.GenPolynomialRing;
022import edu.jas.structure.GcdRingElem;
023import edu.jas.structure.RingFactory;
024import edu.jas.ufd.GCDFactory;
025import edu.jas.ufd.GreatestCommonDivisorAbstract;
026import edu.jas.util.Terminator;
027import edu.jas.util.ThreadPool;
028
029
030/**
031 * Groebner Base with recursive pseudo reduction multi-threaded parallel
032 * algorithm. Implements coefficient fraction free Groebner bases.
033 * Coefficients can for example be (commutative) multivariate polynomials. 
034 * @param <C> coefficient type
035 * @author Heinz Kredel
036 * 
037 * @see edu.jas.application.GBAlgorithmBuilder
038 * @see edu.jas.gbufd.GBFactory
039 */
040
041public class GroebnerBasePseudoRecParallel<C extends GcdRingElem<C>> extends
042                GroebnerBaseAbstract<GenPolynomial<C>> {
043
044
045    private static final Logger logger = Logger.getLogger(GroebnerBasePseudoRecParallel.class);
046
047
048    private final boolean debug = logger.isDebugEnabled();
049
050
051    /**
052     * Number of threads to use.
053     */
054    protected final int threads;
055
056
057    /**
058     * Pool of threads to use.
059     */
060    protected transient final ThreadPool pool;
061
062
063    /**
064     * Greatest common divisor engine for coefficient content and primitive
065     * parts.
066     */
067    protected final GreatestCommonDivisorAbstract<C> engine;
068
069
070    /**
071     * Pseudo reduction engine.
072     */
073    protected final PseudoReduction<C> redRec;
074
075
076    /**
077     * Pseudo reduction engine.
078     */
079    protected final PseudoReduction<GenPolynomial<C>> red;
080
081
082    /**
083     * Coefficient ring factory.
084     */
085    protected final RingFactory<GenPolynomial<C>> cofac;
086
087
088    /**
089     * Base coefficient ring factory.
090     */
091    protected final RingFactory<C> baseCofac;
092
093
094    /**
095     * Constructor.
096     * @param threads number of threads to use.
097     * @param rf coefficient ring factory.
098     */
099    public GroebnerBasePseudoRecParallel(int threads, RingFactory<GenPolynomial<C>> rf) {
100        this(threads, rf, new PseudoReductionPar<GenPolynomial<C>>(), new ThreadPool(threads),
101                        new OrderedPairlist<GenPolynomial<C>>(new GenPolynomialRing<GenPolynomial<C>>(rf, 1))); // 1=hack
102    }
103
104
105    /**
106     * Constructor.
107     * @param threads number of threads to use.
108     * @param rf coefficient ring factory. <b>Note:</b> red must be an instance
109     *            of PseudoReductionPar.
110     * @param red pseudo reduction engine.
111     */
112    public GroebnerBasePseudoRecParallel(int threads, RingFactory<GenPolynomial<C>> rf,
113                    PseudoReduction<GenPolynomial<C>> red) {
114        this(threads, rf, red, new ThreadPool(threads));
115    }
116
117
118    /**
119     * Constructor.
120     * @param threads number of threads to use.
121     * @param rf coefficient ring factory. <b>Note:</b> red must be an instance
122     *            of PseudoReductionPar.
123     * @param red pseudo reduction engine.
124     * @param pool ThreadPool to use.
125     */
126    public GroebnerBasePseudoRecParallel(int threads, RingFactory<GenPolynomial<C>> rf,
127                    PseudoReduction<GenPolynomial<C>> red, ThreadPool pool) {
128        this(threads, rf, red, pool, new OrderedPairlist<GenPolynomial<C>>(
129                        new GenPolynomialRing<GenPolynomial<C>>(rf, 1))); // 1=hack
130    }
131
132
133    /**
134     * Constructor.
135     * @param threads number of threads to use.
136     * @param rf coefficient ring factory. <b>Note:</b> red must be an instance
137     *            of PseudoReductionPar.
138     * @param pl pair selection strategy
139     */
140    public GroebnerBasePseudoRecParallel(int threads, RingFactory<GenPolynomial<C>> rf,
141                    PairList<GenPolynomial<C>> pl) {
142        this(threads, rf, new PseudoReductionPar<GenPolynomial<C>>(), new ThreadPool(threads), pl);
143    }
144
145
146    /**
147     * Constructor.
148     * @param threads number of threads to use.
149     * @param rf coefficient ring factory. <b>Note:</b> red must be an instance
150     *            of PseudoReductionPar.
151     * @param red pseudo reduction engine.
152     * @param pool ThreadPool to use.
153     * @param pl pair selection strategy
154     */
155    @SuppressWarnings("cast")
156    public GroebnerBasePseudoRecParallel(int threads, RingFactory<GenPolynomial<C>> rf,
157                    PseudoReduction<GenPolynomial<C>> red, ThreadPool pool, PairList<GenPolynomial<C>> pl) {
158        super(red, pl);
159        if (!(red instanceof PseudoReductionPar)) {
160            logger.warn("parallel GB should use parallel aware reduction");
161        }
162        this.red = red;
163        this.redRec = (PseudoReduction<C>) (PseudoReduction) red;
164        cofac = rf;
165        if (threads < 1) {
166            threads = 1;
167        }
168        this.threads = threads;
169        GenPolynomialRing<C> rp = (GenPolynomialRing<C>) cofac;
170        baseCofac = rp.coFac;
171        //engine = (GreatestCommonDivisorAbstract<C>)GCDFactory.<C>getImplementation( baseCofac );
172        //not used: 
173        engine = GCDFactory.<C> getProxy(baseCofac);
174        this.pool = pool;
175    }
176
177
178    /**
179     * Cleanup and terminate ThreadPool.
180     */
181    @Override
182    public void terminate() {
183        if (pool == null) {
184            return;
185        }
186        pool.terminate();
187    }
188
189
190    /**
191     * Cancel ThreadPool.
192     */
193    @Override
194    public int cancel() {
195        if (pool == null) {
196            return 0;
197        }
198        int s = pool.cancel();
199        return s;
200    }
201
202
203    /**
204     * Groebner base using pairlist class.
205     * @param modv module variable number.
206     * @param F polynomial list.
207     * @return GB(F) a Groebner base of F.
208     */
209    public List<GenPolynomial<GenPolynomial<C>>> GB(int modv, List<GenPolynomial<GenPolynomial<C>>> F) {
210        List<GenPolynomial<GenPolynomial<C>>> G = normalizeZerosOnes(F);
211        G = engine.recursivePrimitivePart(G);
212        if (G.size() <= 1) {
213            return G;
214        }
215        GenPolynomialRing<GenPolynomial<C>> ring = G.get(0).ring;
216        if (ring.coFac.isField()) { // TODO remove
217            throw new IllegalArgumentException("coefficients from a field");
218        }
219        PairList<GenPolynomial<C>> pairlist = strategy.create(modv, ring);
220        pairlist.put(G);
221
222        /*
223        GenPolynomial<GenPolynomial<C>> p;
224        List<GenPolynomial<GenPolynomial<C>>> G = new ArrayList<GenPolynomial<GenPolynomial<C>>>();
225        PairList<GenPolynomial<C>> pairlist = null;
226        int l = F.size();
227        ListIterator<GenPolynomial<GenPolynomial<C>>> it = F.listIterator();
228        while (it.hasNext()) {
229            p = it.next();
230            if (p.length() > 0) {
231                p = engine.recursivePrimitivePart(p); //p.monic();
232                p = p.abs();
233                if (p.isConstant()) {
234                    G.clear();
235                    G.add(p);
236                    return G; // since no threads are activated
237                }
238                G.add(p);
239                if (pairlist == null) {
240                    //pairlist = new OrderedPairlist<C>(modv, p.ring);
241                    pairlist = strategy.create(modv, p.ring);
242                }
243                // putOne not required
244                pairlist.put(p);
245            } else {
246                l--;
247            }
248        }
249        if (l <= 1) {
250            return G; // since no threads are activated
251        }
252        */
253        logger.info("start " + pairlist);
254
255        Terminator fin = new Terminator(threads);
256        PseudoReducerRec<C> R;
257        for (int i = 0; i < threads; i++) {
258            R = new PseudoReducerRec<C>(fin, G, pairlist, engine);
259            pool.addJob(R);
260        }
261        fin.waitDone();
262        if (Thread.currentThread().isInterrupted()) {
263            throw new RuntimeException("interrupt before minimalGB");
264        }
265        logger.debug("#parallel list = " + G.size());
266        G = minimalGB(G);
267        logger.info("" + pairlist);
268        return G;
269    }
270
271
272    /**
273     * Minimal ordered Groebner basis.
274     * @param Gp a Groebner base.
275     * @return a reduced Groebner base of Gp.
276     */
277    @Override
278    public List<GenPolynomial<GenPolynomial<C>>> minimalGB(List<GenPolynomial<GenPolynomial<C>>> Gp) {
279        List<GenPolynomial<GenPolynomial<C>>> G = normalizeZerosOnes(Gp);
280        /*
281        if (Gp == null || Gp.size() <= 1) {
282            return Gp;
283        }
284        // remove zero polynomials
285        List<GenPolynomial<GenPolynomial<C>>> G = new ArrayList<GenPolynomial<GenPolynomial<C>>>(Gp.size());
286        for (GenPolynomial<GenPolynomial<C>> a : Gp) {
287            if (a != null && !a.isZERO()) { // always true in GB()
288                // already positive a = a.abs();
289                G.add(a);
290            }
291        }
292        */
293        if (G.size() <= 1) {
294            return G;
295        }
296        // remove top reducible polynomials
297        GenPolynomial<GenPolynomial<C>> a;
298        List<GenPolynomial<GenPolynomial<C>>> F;
299        F = new ArrayList<GenPolynomial<GenPolynomial<C>>>(G.size());
300        while (G.size() > 0) {
301            a = G.remove(0);
302            if (red.isTopReducible(G, a) || red.isTopReducible(F, a)) {
303                // drop polynomial 
304                if (debug) {
305                    System.out.println("dropped " + a);
306                    List<GenPolynomial<GenPolynomial<C>>> ff;
307                    ff = new ArrayList<GenPolynomial<GenPolynomial<C>>>(G);
308                    ff.addAll(F);
309                    //a = red.normalform(ff, a);
310                    a = redRec.normalformRecursive(ff, a);
311                    if (!a.isZERO()) {
312                        System.out.println("error, nf(a) " + a);
313                    }
314                }
315            } else {
316                F.add(a);
317            }
318        }
319        G = F;
320        if (G.size() <= 1) {
321            return G;
322        }
323        Collections.reverse(G); // important for lex GB
324        // reduce remaining polynomials
325        @SuppressWarnings("cast")
326        PseudoMiReducerRec<C>[] mirs = (PseudoMiReducerRec<C>[]) new PseudoMiReducerRec[G.size()];
327        int i = 0;
328        F = new ArrayList<GenPolynomial<GenPolynomial<C>>>(G.size());
329        while (G.size() > 0) {
330            a = G.remove(0);
331            List<GenPolynomial<GenPolynomial<C>>> R = new ArrayList<GenPolynomial<GenPolynomial<C>>>(G.size()
332                            + F.size());
333            R.addAll(G);
334            R.addAll(F);
335            // System.out.println("doing " + a.length());
336            mirs[i] = new PseudoMiReducerRec<C>(R, a, engine);
337            pool.addJob(mirs[i]);
338            i++;
339            F.add(a);
340        }
341        G = F;
342        F = new ArrayList<GenPolynomial<GenPolynomial<C>>>(G.size());
343        for (i = 0; i < mirs.length; i++) {
344            a = mirs[i].getNF();
345            F.add(a);
346        }
347        return F;
348    }
349
350
351    /**
352     * Groebner base simple test.
353     * @param modv module variable number.
354     * @param F recursive polynomial list.
355     * @return true, if F is a Groebner base, else false.
356     */
357    @Override
358    public boolean isGBsimple(int modv, List<GenPolynomial<GenPolynomial<C>>> F) {
359        if (F == null || F.isEmpty()) {
360            return true;
361        }
362        GenPolynomial<GenPolynomial<C>> pi, pj, s, h;
363        ExpVector ei, ej, eij;
364        for (int i = 0; i < F.size(); i++) {
365            pi = F.get(i);
366            ei = pi.leadingExpVector();
367            for (int j = i + 1; j < F.size(); j++) {
368                pj = F.get(j);
369                ej = pj.leadingExpVector();
370                if (!red.moduleCriterion(modv, ei, ej)) {
371                    continue;
372                }
373                eij = ei.lcm(ej);
374                if (!red.criterion4(ei, ej, eij)) {
375                    continue;
376                }
377                //if (!criterion3(i, j, eij, F)) {
378                //    continue;
379                //}
380                s = red.SPolynomial(pi, pj);
381                if (s.isZERO()) {
382                    continue;
383                }
384                //System.out.println("i, j = " + i + ", " + j); 
385                h = redRec.normalformRecursive(F, s);
386                if (!h.isZERO()) {
387                    logger.info("no GB: pi = " + pi + ", pj = " + pj);
388                    logger.info("s  = " + s + ", h = " + h);
389                    return false;
390                }
391            }
392        }
393        return true;
394    }
395
396}
397
398
399/**
400 * Pseudo GB Reducing worker threads.
401 */
402class PseudoReducerRec<C extends GcdRingElem<C>> implements Runnable {
403
404
405    private final List<GenPolynomial<GenPolynomial<C>>> G;
406
407
408    private final PairList<GenPolynomial<C>> pairlist;
409
410
411    private final Terminator fin;
412
413
414    private final PseudoReductionPar<GenPolynomial<C>> red;
415
416
417    private final PseudoReductionPar<C> redRec;
418
419
420    private final GreatestCommonDivisorAbstract<C> engine;
421
422
423    private static final Logger logger = Logger.getLogger(PseudoReducerRec.class);
424
425
426    PseudoReducerRec(Terminator fin, List<GenPolynomial<GenPolynomial<C>>> G, PairList<GenPolynomial<C>> L,
427                    GreatestCommonDivisorAbstract<C> engine) {
428        this.fin = fin;
429        this.G = G;
430        pairlist = L;
431        red = new PseudoReductionPar<GenPolynomial<C>>();
432        redRec = new PseudoReductionPar<C>();
433        this.engine = engine;
434        fin.initIdle(1);
435    }
436
437
438    /**
439     * to string
440     */
441    @Override
442    public String toString() {
443        return "PseudoReducer";
444    }
445
446
447    public void run() {
448        Pair<GenPolynomial<C>> pair;
449        GenPolynomial<GenPolynomial<C>> pi, pj, S, H;
450        //boolean set = false;
451        int reduction = 0;
452        int sleeps = 0;
453        while (pairlist.hasNext() || fin.hasJobs()) {
454            while (!pairlist.hasNext()) {
455                // wait
456                //fin.beIdle(); set = true;
457                try {
458                    sleeps++;
459                    if (sleeps % 10 == 0) {
460                        logger.info(" reducer is sleeping");
461                    } else {
462                        logger.debug("r");
463                    }
464                    Thread.sleep(100);
465                } catch (InterruptedException e) {
466                    break;
467                }
468                if (!fin.hasJobs()) {
469                    break;
470                }
471            }
472            if (!pairlist.hasNext() && !fin.hasJobs()) {
473                break;
474            }
475
476            fin.notIdle(); // before pairlist get
477            pair = pairlist.removeNext();
478            if (Thread.currentThread().isInterrupted()) {
479                fin.initIdle(1);
480                throw new RuntimeException("interrupt after removeNext");
481            }
482            if (pair == null) {
483                fin.initIdle(1);
484                continue;
485            }
486
487            pi = pair.pi;
488            pj = pair.pj;
489            if (logger.isDebugEnabled()) {
490                logger.debug("pi    = " + pi);
491                logger.debug("pj    = " + pj);
492            }
493
494            S = red.SPolynomial(pi, pj);
495            if (S.isZERO()) {
496                pair.setZero();
497                fin.initIdle(1);
498                continue;
499            }
500            if (logger.isDebugEnabled()) {
501                logger.debug("ht(S) = " + S.leadingExpVector());
502            }
503
504            //H = red.normalform(G, S); //mod
505            H = redRec.normalformRecursive(G, S);
506            reduction++;
507            if (H.isZERO()) {
508                pair.setZero();
509                fin.initIdle(1);
510                continue;
511            }
512            if (logger.isDebugEnabled()) {
513                logger.info("ht(H) = " + H.leadingExpVector());
514            }
515
516            H = engine.recursivePrimitivePart(H); //H.monic();
517            H = H.abs();
518            // System.out.println("H   = " + H);
519            if (H.isONE()) {
520                // putOne not required
521                pairlist.put(H);
522                synchronized (G) {
523                    G.clear();
524                    G.add(H);
525                }
526                fin.allIdle();
527                return;
528            }
529            if (logger.isDebugEnabled()) {
530                logger.debug("H = " + H);
531            }
532            synchronized (G) {
533                G.add(H);
534            }
535            pairlist.put(H);
536            fin.initIdle(1);
537        }
538        fin.allIdle();
539        logger.info("terminated, done " + reduction + " reductions");
540    }
541}
542
543
544/**
545 * Pseudo Reducing worker threads for minimal GB.
546 */
547class PseudoMiReducerRec<C extends GcdRingElem<C>> implements Runnable {
548
549
550    private final List<GenPolynomial<GenPolynomial<C>>> G;
551
552
553    private GenPolynomial<GenPolynomial<C>> H;
554
555
556    //private final PseudoReductionPar<GenPolynomial<C>> red;
557
558
559    private final PseudoReductionPar<C> redRec;
560
561
562    private final Semaphore done = new Semaphore(0);
563
564
565    private final GreatestCommonDivisorAbstract<C> engine;
566
567
568    private static final Logger logger = Logger.getLogger(PseudoMiReducerRec.class);
569
570
571    PseudoMiReducerRec(List<GenPolynomial<GenPolynomial<C>>> G, GenPolynomial<GenPolynomial<C>> p,
572                    GreatestCommonDivisorAbstract<C> engine) {
573        this.G = G;
574        this.engine = engine;
575        H = p;
576        //red = new PseudoReductionPar<GenPolynomial<C>>();
577        redRec = new PseudoReductionPar<C>();
578    }
579
580
581    /**
582     * to string
583     */
584    @Override
585    public String toString() {
586        return "PseudoMiReducerRec";
587    }
588
589
590    /**
591     * getNF. Blocks until the normal form is computed.
592     * @return the computed normal form.
593     */
594    public GenPolynomial<GenPolynomial<C>> getNF() {
595        try {
596            done.acquire(); //done.P();
597        } catch (InterruptedException e) {
598            throw new RuntimeException("interrupt in getNF");
599        }
600        return H;
601    }
602
603
604    public void run() {
605        if (logger.isDebugEnabled()) {
606            logger.debug("ht(H) = " + H.leadingExpVector());
607        }
608        try {
609            //H = red.normalform(G, H); //mod
610            H = redRec.normalformRecursive(G, H);
611            H = engine.recursivePrimitivePart(H); //H.monic();
612            H = H.abs();
613            done.release(); //done.V();
614        } catch (RuntimeException e) {
615            Thread.currentThread().interrupt();
616            //throw new RuntimeException("interrupt in getNF");
617        }
618        if (logger.isDebugEnabled()) {
619            logger.debug("ht(H) = " + H.leadingExpVector());
620        }
621        // H = H.monic();
622    }
623
624}