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