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