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     * Twosided Groebner base using pairlist class.
288     * @param modv number of module variables.
289     * @param Fp solvable polynomial list.
290     * @return tsGB(Fp) a twosided Groebner base of F.
291     */
292    public List<GenSolvablePolynomial<C>> twosidedGB(int modv, List<GenSolvablePolynomial<C>> Fp) {
293        if (Fp == null || Fp.size() == 0) { // 0 not 1
294            return new ArrayList<GenSolvablePolynomial<C>>();
295        }
296        GenSolvablePolynomialRing<C> ring = Fp.get(0).ring; // assert != null
297        // add also coefficient generators
298        List<GenSolvablePolynomial<C>> X;
299        X = PolynomialList.castToSolvableList(ring.generators(modv)); 
300        logger.info("right multipliers = {}", X);
301        List<GenSolvablePolynomial<C>> F = new ArrayList<GenSolvablePolynomial<C>>(Fp.size() * (1 + X.size()));
302        F.addAll(Fp);
303        GenSolvablePolynomial<C> p, x, q;
304        for (int i = 0; i < F.size(); i++) { // F changes
305            p = F.get(i);
306            for (int j = 0; j < X.size(); j++) {
307                x = X.get(j);
308                if (x.isONE()) {
309                    continue;
310                }
311                q = p.multiply(x);
312                q = sred.leftNormalform(F, q);
313                if (!q.isZERO()) {
314                    F.add(q);
315                }
316            }
317        }
318        //System.out.println("F generated = " + F);
319        List<GenSolvablePolynomial<C>> G = new ArrayList<GenSolvablePolynomial<C>>();
320        CriticalPairList<C> pairlist = null;
321        int l = F.size();
322        ListIterator<GenSolvablePolynomial<C>> it = F.listIterator();
323        while (it.hasNext()) {
324            p = it.next();
325            if (p.length() > 0) {
326                p = p.monic();
327                if (p.isONE()) {
328                    G.clear();
329                    G.add(p);
330                    return G; // since no threads are activated
331                }
332                G.add(p);
333                if (pairlist == null) {
334                    pairlist = new CriticalPairList<C>(modv, p.ring);
335                    if (!p.ring.coFac.isField()) {
336                        throw new IllegalArgumentException("coefficients not from a field");
337                    }
338                }
339                // putOne not required
340                pairlist.put(p);
341            } else {
342                l--;
343            }
344        }
345        //System.out.println("G to check = " + G);
346        if (l <= 1) { // 1 ok
347            return G; // since no threads are activated
348        }
349        Terminator fin = new Terminator(threads);
350        TwosidedSolvableReducerSeqPair<C> R;
351        for (int i = 0; i < threads; i++) {
352            R = new TwosidedSolvableReducerSeqPair<C>(fin, X, G, pairlist);
353            pool.execute(R);
354        }
355        fin.waitDone();
356        logger.debug("#parallel list = {}", G.size());
357        G = leftMinimalGB(G);
358        // not in this context // pool.terminate();
359        logger.info("{}", pairlist);
360        return G;
361    }
362
363}
364
365
366/**
367 * Reducing left worker threads.
368 * @param <C> coefficient type
369 */
370class LeftSolvableReducerSeqPair<C extends RingElem<C>> implements Runnable {
371
372
373    private final List<GenSolvablePolynomial<C>> G;
374
375
376    private final CriticalPairList<C> pairlist;
377
378
379    private final Terminator pool;
380
381
382    private final SolvableReductionPar<C> sred;
383
384
385    private static final Logger logger = LogManager.getLogger(LeftSolvableReducerSeqPair.class);
386
387
388    private static final boolean debug = logger.isDebugEnabled();
389
390
391    LeftSolvableReducerSeqPair(Terminator fin, List<GenSolvablePolynomial<C>> G, CriticalPairList<C> L) {
392        pool = fin;
393        this.G = G;
394        pairlist = L;
395        sred = new SolvableReductionPar<C>();
396    }
397
398
399    @SuppressWarnings("unchecked")
400    public void run() {
401        CriticalPair<C> pair;
402        GenSolvablePolynomial<C> S;
403        GenSolvablePolynomial<C> H;
404        boolean set = false;
405        int reduction = 0;
406        int sleeps = 0;
407        while (pairlist.hasNext() || pool.hasJobs()) {
408            while (!pairlist.hasNext()) {
409                pairlist.update();
410                // wait
411                pool.beIdle();
412                set = true;
413                try {
414                    sleeps++;
415                    if (sleeps % 10 == 0) {
416                        logger.info(" reducer is sleeping");
417                    } else {
418                        logger.debug("r");
419                    }
420                    Thread.sleep(100);
421                } catch (InterruptedException e) {
422                    break;
423                }
424                if (!pool.hasJobs()) {
425                    break;
426                }
427            }
428            if (!pairlist.hasNext() && !pool.hasJobs()) {
429                break;
430            }
431            if (set) {
432                pool.notIdle();
433                set = false;
434            }
435            pair = pairlist.getNext();
436            if (pair == null) {
437                pairlist.update();
438                continue;
439            }
440            if (debug) {
441                logger.debug("pi = {}", pair.pi);
442                logger.debug("pj = {}", pair.pj);
443            }
444            S = sred.leftSPolynomial((GenSolvablePolynomial<C>) pair.pi, (GenSolvablePolynomial<C>) pair.pj);
445            if (S.isZERO()) {
446                pairlist.record(pair, S);
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                pairlist.record(pair, H);
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.update( pair, H );
465                pairlist.putOne(); // not really required
466                synchronized (G) {
467                    G.clear();
468                    G.add(H);
469                }
470                pool.allIdle();
471                return;
472            }
473            if (debug) {
474                logger.debug("H = {}", H);
475            }
476            synchronized (G) {
477                G.add(H);
478            }
479            pairlist.update(pair, H);
480            //pairlist.record( pair, H );
481            //pairlist.update();
482        }
483        logger.info("terminated, done {} reductions", reduction);
484    }
485}
486
487
488/**
489 * Reducing twosided worker threads.
490 * @param <C> coefficient type
491 */
492class TwosidedSolvableReducerSeqPair<C extends RingElem<C>> implements Runnable {
493
494
495    private final List<GenSolvablePolynomial<C>> X;
496
497
498    private final List<GenSolvablePolynomial<C>> G;
499
500
501    private final CriticalPairList<C> pairlist;
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(TwosidedSolvableReducerSeqPair.class);
511
512
513    private static final boolean debug = logger.isDebugEnabled();
514
515
516    TwosidedSolvableReducerSeqPair(Terminator fin, List<GenSolvablePolynomial<C>> X,
517                    List<GenSolvablePolynomial<C>> G, CriticalPairList<C> L) {
518        pool = fin;
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;
528        CriticalPair<C> pair;
529        GenSolvablePolynomial<C> S;
530        GenSolvablePolynomial<C> H;
531        boolean set = false;
532        int reduction = 0;
533        int sleeps = 0;
534        while (pairlist.hasNext() || pool.hasJobs()) {
535            while (!pairlist.hasNext()) {
536                pairlist.update();
537                // wait
538                pool.beIdle();
539                set = true;
540                try {
541                    sleeps++;
542                    if (sleeps % 10 == 0) {
543                        logger.info(" reducer is sleeping");
544                    } else {
545                        logger.debug("r");
546                    }
547                    Thread.sleep(50);
548                } catch (InterruptedException e) {
549                    break;
550                }
551                if (!pool.hasJobs()) {
552                    break;
553                }
554            }
555            if (!pairlist.hasNext() && !pool.hasJobs()) {
556                break;
557            }
558            if (set) {
559                pool.notIdle();
560                set = false;
561            }
562            pair = pairlist.getNext();
563            if (pair == null) {
564                pairlist.update();
565                continue;
566            }
567            if (debug) {
568                logger.debug("pi = {}", pair.pi);
569                logger.debug("pj = {}", pair.pj);
570            }
571            S = sred.leftSPolynomial((GenSolvablePolynomial<C>) pair.pi, (GenSolvablePolynomial<C>) pair.pj);
572            if (S.isZERO()) {
573                pairlist.record(pair, S);
574                continue;
575            }
576            if (debug) {
577                logger.debug("ht(S) = {}", S.leadingExpVector());
578            }
579            H = sred.leftNormalform(G, S); //mod
580            reduction++;
581            if (H.isZERO()) {
582                pairlist.record(pair, H);
583                continue;
584            }
585            if (debug) {
586                logger.debug("ht(H) = {}", H.leadingExpVector());
587            }
588            H = H.monic();
589            // System.out.println("H   = " + H);
590            if (H.isONE()) {
591                // pairlist.update( pair, H );
592                pairlist.putOne(); // not really required
593                synchronized (G) {
594                    G.clear();
595                    G.add(H);
596                }
597                pool.allIdle();
598                return;
599            }
600            if (debug) {
601                logger.debug("H = {}", H);
602            }
603            synchronized (G) {
604                G.add(H);
605            }
606            pairlist.update(pair, H);
607            for (int j = 0; j < X.size(); j++) {
608                x = X.get(j);
609                if (x.isONE()) {
610                    continue;
611                }
612                p = H.multiply(x);
613                p = sred.leftNormalform(G, p);
614                if (!p.isZERO()) {
615                    p = p.monic();
616                    if (p.isONE()) {
617                        synchronized (G) {
618                            G.clear();
619                            G.add(p);
620                        }
621                        pool.allIdle();
622                        return;
623                    }
624                    synchronized (G) {
625                        G.add(p);
626                    }
627                    pairlist.put(p);
628                }
629            }
630        }
631        logger.info("terminated, done {} reductions", reduction);
632    }
633}
634
635
636/**
637 * Reducing worker threads for minimal GB.
638 * @param <C> coefficient type
639 */
640class SolvableMiReducerSeqPair<C extends RingElem<C>> implements Runnable {
641
642
643    private final List<GenSolvablePolynomial<C>> G;
644
645
646    private GenSolvablePolynomial<C> H;
647
648
649    private final SolvableReductionPar<C> sred;
650
651
652    private final Semaphore done = new Semaphore(0);
653
654
655    private static final Logger logger = LogManager.getLogger(SolvableMiReducerSeqPair.class);
656
657
658    private static final boolean debug = logger.isDebugEnabled();
659
660
661    SolvableMiReducerSeqPair(List<GenSolvablePolynomial<C>> G, GenSolvablePolynomial<C> p) {
662        this.G = G;
663        H = p;
664        sred = new SolvableReductionPar<C>();
665    }
666
667
668    /**
669     * getNF. Blocks until the normal form is computed.
670     * @return the computed normal form.
671     */
672    public GenSolvablePolynomial<C> getNF() {
673        try {
674            done.acquire(); //done.P();
675        } catch (InterruptedException e) {
676        }
677        return H;
678    }
679
680
681    public void run() {
682        if (debug) {
683            logger.debug("ht(H) = {}", H.leadingExpVector());
684        }
685        H = sred.leftNormalform(G, H); //mod
686        done.release(); //done.V();
687        if (debug) {
688            logger.debug("ht(H) = {}", H.leadingExpVector());
689        }
690        // H = H.monic();
691    }
692
693}