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