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