001/*
002 * $Id: SolvableGroebnerBaseParallel.java 5869 2018-07-20 15:53:10Z kredel $
003 */
004
005package edu.jas.gb;
006
007
008import java.util.ArrayList;
009import java.util.List;
010import java.util.ListIterator;
011import java.util.concurrent.Semaphore;
012
013import org.apache.logging.log4j.Logger;
014import org.apache.logging.log4j.LogManager; 
015
016import edu.jas.poly.ExpVector;
017import edu.jas.poly.GenSolvablePolynomial;
018import edu.jas.poly.GenSolvablePolynomialRing;
019import edu.jas.poly.PolynomialList;
020import edu.jas.poly.PolyUtil;
021import edu.jas.structure.RingElem;
022import edu.jas.util.Terminator;
023import edu.jas.util.ThreadPool;
024
025
026/**
027 * Solvable Groebner Base parallel algorithm. Implements a shared memory
028 * parallel version of Groebner bases. Threads maintain pairlist.
029 * @param <C> coefficient type
030 * @author Heinz Kredel
031 */
032
033public class SolvableGroebnerBaseParallel<C extends RingElem<C>> extends SolvableGroebnerBaseAbstract<C> {
034
035
036    private static final Logger logger = LogManager.getLogger(SolvableGroebnerBaseParallel.class);
037
038
039    //private static final boolean debug = logger.isDebugEnabled();
040
041
042    /**
043     * Number of threads to use.
044     */
045    protected final int threads;
046
047
048    /**
049     * Pool of threads to use.
050     */
051    protected transient final ThreadPool pool;
052
053
054    /**
055     * Constructor.
056     */
057    public SolvableGroebnerBaseParallel() {
058        this(2);
059    }
060
061
062    /**
063     * Constructor.
064     * @param threads number of threads to use.
065     */
066    public SolvableGroebnerBaseParallel(int threads) {
067        this(threads, new ThreadPool(threads));
068    }
069
070
071    /**
072     * Constructor.
073     * @param threads number of threads to use.
074     * @param pool ThreadPool to use.
075     */
076    public SolvableGroebnerBaseParallel(int threads, ThreadPool pool) {
077        this(threads, pool, new SolvableReductionPar<C>());
078    }
079
080
081    /**
082     * Constructor.
083     * @param threads number of threads to use.
084     * @param sred parallelism aware reduction engine
085     */
086    public SolvableGroebnerBaseParallel(int threads, SolvableReduction<C> sred) {
087        this(threads, new ThreadPool(threads), sred);
088    }
089
090
091    /**
092     * Constructor.
093     * @param threads number of threads to use.
094     * @param pl pair selection strategy
095     */
096    public SolvableGroebnerBaseParallel(int threads, PairList<C> pl) {
097        this(threads, new ThreadPool(threads), new SolvableReductionPar<C>(), pl);
098    }
099
100
101    /**
102     * Constructor.
103     * @param threads number of threads to use.
104     * @param sred parallelism aware reduction engine
105     * @param pl pair selection strategy
106     */
107    public SolvableGroebnerBaseParallel(int threads, SolvableReduction<C> sred, PairList<C> pl) {
108        this(threads, new ThreadPool(threads), sred, pl);
109    }
110
111
112    /**
113     * Constructor.
114     * @param threads number of threads to use.
115     * @param pool ThreadPool to use.
116     * @param sred parallelism aware reduction engine
117     */
118    public SolvableGroebnerBaseParallel(int threads, ThreadPool pool, SolvableReduction<C> sred) {
119        this(threads, pool, sred, new OrderedPairlist<C>());
120    }
121
122
123    /**
124     * Constructor.
125     * @param threads number of threads to use.
126     * @param pool ThreadPool to use.
127     * @param sred parallelism aware reduction engine
128     * @param pl pair selection strategy
129     */
130    public SolvableGroebnerBaseParallel(int threads, ThreadPool pool, SolvableReduction<C> sred,
131                    PairList<C> pl) {
132        super(sred, pl);
133        if (!(sred instanceof SolvableReductionPar)) {
134            logger.warn("parallel GB should use parallel aware reduction");
135        }
136        if (threads < 1) {
137            threads = 1;
138        }
139        this.threads = threads;
140        this.pool = pool;
141    }
142
143
144    /**
145     * Cleanup and terminate ThreadPool.
146     */
147    @Override
148    public void terminate() {
149        if (pool == null) {
150            return;
151        }
152        pool.terminate();
153    }
154
155
156    /**
157     * Parallel Groebner base using sequential pair order class. Threads
158     * maintain pairlist.
159     * @param modv number of module variables.
160     * @param F polynomial list.
161     * @return GB(F) a Groebner base of F.
162     */
163    public List<GenSolvablePolynomial<C>> leftGB(int modv, List<GenSolvablePolynomial<C>> F) {
164        List<GenSolvablePolynomial<C>> G = normalizeZerosOnes(F);
165        G = PolynomialList.castToSolvableList(PolyUtil.<C> monic(PolynomialList.castToList(G)));
166        if (G.size() <= 1) {
167            return G;
168        }
169        GenSolvablePolynomialRing<C> ring = G.get(0).ring;
170        if (!ring.coFac.isField() && ring.coFac.isCommutative()) {
171            throw new IllegalArgumentException("coefficients not from a field");
172        }
173        PairList<C> pairlist = strategy.create(modv, ring);
174        pairlist.put(PolynomialList.castToList(G));
175        logger.info("start " + pairlist);
176
177        Terminator fin = new Terminator(threads);
178        LeftSolvableReducer<C> R;
179        for (int i = 0; i < threads; i++) {
180            R = new LeftSolvableReducer<C>(fin, G, pairlist);
181            pool.addJob(R);
182        }
183        fin.waitDone();
184        logger.debug("#parallel list = " + G.size());
185        G = leftMinimalGB(G);
186        // not in this context // pool.terminate();
187        logger.info("end   " + pairlist);
188        return G;
189    }
190
191
192    /**
193     * Minimal ordered groebner basis, parallel.
194     * @param Fp a Groebner base.
195     * @return minimalGB(F) a minimal Groebner base of Fp.
196     */
197    @Override
198    public List<GenSolvablePolynomial<C>> leftMinimalGB(List<GenSolvablePolynomial<C>> Fp) {
199        GenSolvablePolynomial<C> a;
200        ArrayList<GenSolvablePolynomial<C>> G;
201        G = new ArrayList<GenSolvablePolynomial<C>>(Fp.size());
202        ListIterator<GenSolvablePolynomial<C>> it = Fp.listIterator();
203        while (it.hasNext()) {
204            a = it.next();
205            if (a.length() != 0) { // always true
206                // already monic  a = a.monic();
207                G.add(a);
208            }
209        }
210        if (G.size() <= 1) {
211            return G;
212        }
213
214        ExpVector e;
215        ExpVector f;
216        GenSolvablePolynomial<C> p;
217        ArrayList<GenSolvablePolynomial<C>> F;
218        F = new ArrayList<GenSolvablePolynomial<C>>(G.size());
219        boolean mt;
220        while (G.size() > 0) {
221            a = G.remove(0);
222            e = a.leadingExpVector();
223
224            it = G.listIterator();
225            mt = false;
226            while (it.hasNext() && !mt) {
227                p = it.next();
228                f = p.leadingExpVector();
229                mt = e.multipleOf(f);
230            }
231            it = F.listIterator();
232            while (it.hasNext() && !mt) {
233                p = it.next();
234                f = p.leadingExpVector();
235                mt = e.multipleOf(f);
236            }
237            if (!mt) {
238                F.add(a); // no thread at this point
239            } else {
240                // System.out.println("dropped " + a.length());
241            }
242        }
243        G = F;
244        if (G.size() <= 1) {
245            return G;
246        }
247
248        @SuppressWarnings("cast")
249        SolvableMiReducer<C>[] mirs = (SolvableMiReducer<C>[]) new SolvableMiReducer[G.size()];
250        int i = 0;
251        F = new ArrayList<GenSolvablePolynomial<C>>(G.size());
252        while (G.size() > 0) {
253            a = G.remove(0);
254            // System.out.println("doing " + a.length());
255            List<GenSolvablePolynomial<C>> R = new ArrayList<GenSolvablePolynomial<C>>(G.size() + F.size());
256            R.addAll(G);
257            R.addAll(F);
258            mirs[i] = new SolvableMiReducer<C>(R, a);
259            pool.addJob(mirs[i]);
260            i++;
261            F.add(a);
262        }
263        G = F;
264        F = new ArrayList<GenSolvablePolynomial<C>>(G.size());
265        for (i = 0; i < mirs.length; i++) {
266            a = mirs[i].getNF();
267            F.add(a);
268        }
269        return F;
270    }
271
272
273    /**
274     * Solvable Extended Groebner base using critical pair class.
275     * @param modv module variable number.
276     * @param F solvable polynomial list.
277     * @return a container for an extended left Groebner base of F.
278     */
279    @Override
280    public SolvableExtendedGB<C> extLeftGB(int modv, List<GenSolvablePolynomial<C>> F) {
281        throw new UnsupportedOperationException("parallel extLeftGB not implemented");
282    }
283
284
285    /**
286     * Twosided Groebner base using pairlist class.
287     * @param modv number of module variables.
288     * @param Fp solvable polynomial list.
289     * @return tsGB(Fp) a twosided Groebner base of F.
290     */
291    @SuppressWarnings("unchecked")
292    public List<GenSolvablePolynomial<C>> twosidedGB(int modv, List<GenSolvablePolynomial<C>> Fp) {
293        List<GenSolvablePolynomial<C>> G = normalizeZerosOnes(Fp);
294        G = PolynomialList.castToSolvableList(PolyUtil.<C> monic(PolynomialList.castToList(G)));
295        if (G.size() < 1) { // 0 not 1
296            return G;
297        }
298        if (G.size() <= 1) { 
299            if (G.get(0).isONE()) {
300                return G; 
301            }
302        }
303        GenSolvablePolynomialRing<C> ring = G.get(0).ring;
304        if (!ring.coFac.isField() && ring.coFac.isCommutative()) {
305            throw new IllegalArgumentException("coefficients not from a field");
306        }
307        // add also coefficient generators
308        List<GenSolvablePolynomial<C>> X;
309        X = PolynomialList.castToSolvableList(ring.generators(modv)); 
310        logger.info("right multipliers = " + X);
311        List<GenSolvablePolynomial<C>> F = new ArrayList<GenSolvablePolynomial<C>>(G.size() * (1 + X.size()));
312        F.addAll(G); 
313        GenSolvablePolynomial<C> p, x, q;
314        for (int i = 0; i < F.size(); i++) { // F changes
315            p = F.get(i);
316            for (int j = 0; j < X.size(); j++) {
317                x = X.get(j);
318                if (x.isONE()) {
319                    continue;
320                }
321                q = p.multiply(x);
322                q = sred.leftNormalform(F, q);
323                if (!q.isZERO()) {
324                    q = q.monic();
325                    if (q.isONE()) {
326                        G.clear();
327                        G.add(q);
328                        return G;
329                    } 
330                    F.add(q);
331                }
332            }
333        }
334        //System.out.println("F generated = " + F);
335        G = F;
336        if (G.size() <= 1) { // 1 okay here
337            return G;
338        }
339        PairList<C> pairlist = strategy.create(modv, ring);
340        pairlist.put(PolynomialList.castToList(G));
341        logger.info("start " + pairlist);
342
343        Terminator fin = new Terminator(threads);
344        TwosidedSolvableReducer<C> R;
345        for (int i = 0; i < threads; i++) {
346            R = new TwosidedSolvableReducer<C>(fin, modv, X, G, pairlist);
347            pool.addJob(R);
348        }
349        fin.waitDone();
350        logger.debug("#parallel list = " + G.size());
351        G = leftMinimalGB(G);
352        // not in this context // pool.terminate();
353        logger.info("end   " + pairlist);
354        return G;
355    }
356
357}
358
359
360/**
361 * Reducing left worker threads.
362 * @param <C> coefficient type
363 */
364class LeftSolvableReducer<C extends RingElem<C>> implements Runnable {
365
366
367    private final List<GenSolvablePolynomial<C>> G;
368
369
370    private final PairList<C> pairlist;
371
372
373    private final Terminator pool;
374
375
376    private final SolvableReductionPar<C> sred;
377
378
379    private static final Logger logger = LogManager.getLogger(LeftSolvableReducer.class);
380
381
382    private static final boolean debug = logger.isDebugEnabled();
383
384
385    LeftSolvableReducer(Terminator fin, List<GenSolvablePolynomial<C>> G, PairList<C> L) {
386        pool = fin;
387        this.G = G;
388        pairlist = L;
389        sred = new SolvableReductionPar<C>();
390    }
391
392
393    @SuppressWarnings("unchecked")
394    public void run() {
395        Pair<C> pair;
396        GenSolvablePolynomial<C> S;
397        GenSolvablePolynomial<C> H;
398        boolean set = false;
399        int reduction = 0;
400        int sleeps = 0;
401        while (pairlist.hasNext() || pool.hasJobs()) {
402            while (!pairlist.hasNext()) {
403                // wait
404                pool.beIdle();
405                set = true;
406                try {
407                    sleeps++;
408                    if (sleeps % 10 == 0) {
409                        logger.info(" reducer is sleeping");
410                    } else {
411                        logger.debug("r");
412                    }
413                    Thread.sleep(100);
414                } catch (InterruptedException e) {
415                    pool.allIdle();
416                    logger.info("shutdown " + pool + " after: " + e);
417                    //throw new RuntimeException("interrupt 1 in pairlist.hasNext loop");
418                    break;
419                }
420                if (Thread.currentThread().isInterrupted()) {
421                    //pool.initIdle(1);
422                    pool.allIdle();
423                    logger.info("shutdown after .isInterrupted(): " + pool);
424                    //throw new RuntimeException("interrupt 2 in pairlist.hasNext loop");
425                    break;
426                }
427                if (!pool.hasJobs()) {
428                    break;
429                }
430            }
431            if (!pairlist.hasNext() && !pool.hasJobs()) {
432                break;
433            }
434            if (set) {
435                pool.notIdle();
436                set = false;
437            }
438            pair = pairlist.removeNext();
439            if (pair == null) {
440                continue;
441            }
442            if (debug) {
443                logger.debug("pi = " + pair.pi);
444                logger.debug("pj = " + pair.pj);
445            }
446            S = sred.leftSPolynomial((GenSolvablePolynomial<C>) pair.pi, (GenSolvablePolynomial<C>) pair.pj);
447            if (S.isZERO()) {
448                continue;
449            }
450            if (debug) {
451                logger.debug("ht(S) = " + S.leadingExpVector());
452            }
453            H = sred.leftNormalform(G, S); //mod
454            reduction++;
455            if (H.isZERO()) {
456                continue;
457            }
458            if (debug) {
459                logger.debug("ht(H) = " + H.leadingExpVector());
460            }
461            H = H.monic();
462            // System.out.println("H   = " + H);
463            if (H.isONE()) {
464                pairlist.putOne(); // not really required
465                synchronized (G) {
466                    G.clear();
467                    G.add(H);
468                }
469                pool.allIdle();
470                return;
471            }
472            if (debug) {
473                logger.debug("H = " + H);
474            }
475            synchronized (G) {
476                G.add(H);
477            }
478            pairlist.put(H);
479        }
480        logger.info("terminated, done " + reduction + " reductions");
481    }
482}
483
484
485/**
486 * Reducing twosided worker threads.
487 * @param <C> coefficient type
488 */
489class TwosidedSolvableReducer<C extends RingElem<C>> implements Runnable {
490
491
492    private final List<GenSolvablePolynomial<C>> X;
493
494
495    private final List<GenSolvablePolynomial<C>> G;
496
497
498    private final PairList<C> pairlist;
499
500
501    private final int modv;
502
503
504    private final Terminator pool;
505
506
507    private final SolvableReductionPar<C> sred;
508
509
510    private static final Logger logger = LogManager.getLogger(TwosidedSolvableReducer.class);
511
512
513    private static final boolean debug = logger.isDebugEnabled();
514
515
516    TwosidedSolvableReducer(Terminator fin, int modv, List<GenSolvablePolynomial<C>> X,
517                    List<GenSolvablePolynomial<C>> G, PairList<C> L) {
518        pool = fin;
519        this.modv = modv;
520        this.X = X;
521        this.G = G;
522        pairlist = L;
523        sred = new SolvableReductionPar<C>();
524    }
525
526
527    public void run() {
528        GenSolvablePolynomial<C> p, x, S, H;
529        Pair<C> pair;
530        boolean set = false;
531        int reduction = 0;
532        int sleeps = 0;
533        logger.debug("modv = " + modv); // avoid "unused"
534        while (pairlist.hasNext() || pool.hasJobs()) {
535            while (!pairlist.hasNext()) {
536                // wait
537                pool.beIdle();
538                set = true;
539                try {
540                    sleeps++;
541                    if (sleeps % 10 == 0) {
542                        logger.info(" reducer is sleeping");
543                    } else {
544                        logger.debug("r");
545                    }
546                    Thread.sleep(50);
547                } catch (InterruptedException e) {
548                    break;
549                }
550                if (!pool.hasJobs()) {
551                    break;
552                }
553            }
554            if (!pairlist.hasNext() && !pool.hasJobs()) {
555                break;
556            }
557            if (set) {
558                pool.notIdle();
559                set = false;
560            }
561            pair = pairlist.removeNext();
562            if (pair == null) {
563                continue;
564            }
565            if (debug) {
566                logger.debug("pi = " + pair.pi);
567                logger.debug("pj = " + pair.pj);
568            }
569            S = sred.leftSPolynomial((GenSolvablePolynomial<C>) pair.pi, (GenSolvablePolynomial<C>) pair.pj);
570            if (S.isZERO()) {
571                continue;
572            }
573            if (debug) {
574                logger.debug("ht(S) = " + S.leadingExpVector());
575            }
576            H = sred.leftNormalform(G, S); //mod
577            reduction++;
578            if (H.isZERO()) {
579                continue;
580            }
581            if (debug) {
582                logger.debug("ht(H) = " + H.leadingExpVector());
583            }
584            H = H.monic();
585            // System.out.println("H   = " + H);
586            if (H.isONE()) {
587                pairlist.putOne(); // not really required
588                synchronized (G) {
589                    G.clear();
590                    G.add(H);
591                }
592                pool.allIdle();
593                return;
594            }
595            if (debug) {
596                logger.debug("H = " + H);
597            }
598            synchronized (G) {
599                G.add(H);
600            }
601            pairlist.put(H);
602            for (int j = 0; j < X.size(); j++) {
603                x = X.get(j);
604                p = H.multiply(x);
605                p = sred.leftNormalform(G, p);
606                if (!p.isZERO()) {
607                    p = p.monic();
608                    if (p.isONE()) {
609                        synchronized (G) {
610                            G.clear();
611                            G.add(p);
612                        }
613                        pool.allIdle();
614                        return;
615                    }
616                    synchronized (G) {
617                        G.add(p);
618                    }
619                    pairlist.put(p);
620                }
621            }
622        }
623        logger.info("terminated, done " + reduction + " reductions");
624    }
625}
626
627
628/**
629 * Reducing worker threads for minimal GB.
630 * @param <C> coefficient type
631 */
632class SolvableMiReducer<C extends RingElem<C>> implements Runnable {
633
634
635    private final List<GenSolvablePolynomial<C>> G;
636
637
638    private GenSolvablePolynomial<C> H;
639
640
641    private final SolvableReductionPar<C> sred;
642
643
644    private final Semaphore done = new Semaphore(0);
645
646
647    private static final Logger logger = LogManager.getLogger(SolvableMiReducer.class);
648
649
650    private static final boolean debug = logger.isDebugEnabled();
651
652
653    SolvableMiReducer(List<GenSolvablePolynomial<C>> G, GenSolvablePolynomial<C> p) {
654        this.G = G;
655        H = p;
656        sred = new SolvableReductionPar<C>();
657    }
658
659
660    /**
661     * getNF. Blocks until the normal form is computed.
662     * @return the computed normal form.
663     */
664    public GenSolvablePolynomial<C> getNF() {
665        try {
666            done.acquire(); //done.P();
667        } catch (InterruptedException e) {
668        }
669        return H;
670    }
671
672
673    public void run() {
674        if (debug) {
675            logger.debug("ht(H) = " + H.leadingExpVector());
676        }
677        H = sred.leftNormalform(G, H); //mod
678        done.release(); //done.V();
679        if (debug) {
680            logger.debug("ht(H) = " + H.leadingExpVector());
681        }
682        // H = H.monic();
683    }
684
685}