001/*
002 * $Id: GroebnerBaseParIter.java 5869 2018-07-20 15:53:10Z kredel $
003 */
004
005package edu.jas.gb;
006
007
008import java.util.ArrayList;
009import java.util.Collections;
010import java.util.List;
011import java.util.ListIterator;
012import java.util.concurrent.Semaphore;
013
014import org.apache.logging.log4j.Logger;
015import org.apache.logging.log4j.LogManager; 
016
017import edu.jas.poly.ExpVector;
018import edu.jas.poly.GenPolynomial;
019import edu.jas.poly.GenPolynomialRing;
020import edu.jas.poly.OrderedPolynomialList;
021import edu.jas.poly.PolyUtil;
022import edu.jas.structure.RingElem;
023import edu.jas.util.Terminator;
024import edu.jas.util.ThreadPool;
025
026
027/**
028 * Groebner Base parallel iterative algortihm. Implements a shared memory
029 * parallel version of Groebner bases.
030 * @param <C> coefficient type
031 * @author Heinz Kredel
032 * 
033 * @see edu.jas.application.GBAlgorithmBuilder
034 * @see edu.jas.gbufd.GBFactory
035 */
036
037public class GroebnerBaseParIter<C extends RingElem<C>> extends GroebnerBaseAbstract<C> {
038
039
040    private static final Logger logger = LogManager.getLogger(GroebnerBaseParIter.class);
041
042
043    private static final boolean debug = logger.isDebugEnabled();
044
045
046    /**
047     * Number of threads to use.
048     */
049    protected final int threads;
050
051
052    /**
053     * Pool of threads to use.
054     */
055    protected transient final ThreadPool pool;
056
057
058    /**
059     * Constructor.
060     */
061    public GroebnerBaseParIter() {
062        this(2);
063    }
064
065
066    /**
067     * Constructor.
068     * @param threads number of threads to use.
069     */
070    public GroebnerBaseParIter(int threads) {
071        this(threads, new ThreadPool(threads));
072    }
073
074
075    /**
076     * Constructor.
077     * @param threads number of threads to use.
078     * @param red parallelism aware reduction engine
079     */
080    public GroebnerBaseParIter(int threads, Reduction<C> red) {
081        this(threads, new ThreadPool(threads), red);
082    }
083
084
085    /**
086     * Constructor.
087     * @param threads number of threads to use.
088     * @param pl pair selection strategy
089     */
090    public GroebnerBaseParIter(int threads, PairList<C> pl) {
091        this(threads, new ThreadPool(threads), new ReductionPar<C>(), pl);
092    }
093
094
095    /**
096     * Constructor.
097     * @param threads number of threads to use.
098     * @param pool ThreadPool to use.
099     */
100    public GroebnerBaseParIter(int threads, ThreadPool pool) {
101        this(threads, pool, new ReductionPar<C>());
102    }
103
104
105    /**
106     * Constructor.
107     * @param pool ThreadPool to use.
108     * @param red Reduction engine
109     */
110    public GroebnerBaseParIter(int threads, ThreadPool pool, Reduction<C> red) {
111        this(threads, pool, red, new OrderedPairlist<C>());
112    }
113
114
115    /**
116     * Constructor.
117     * @param red Reduction engine
118     * @param pl pair selection strategy
119     */
120    public GroebnerBaseParIter(int threads, Reduction<C> red, PairList<C> pl) {
121        this(threads, new ThreadPool(threads), red, pl);
122    }
123
124
125    /**
126     * Constructor.
127     * @param threads number of threads to use.
128     * @param pool ThreadPool to use.
129     * @param red parallelism aware reduction engine
130     * @param pl pair selection strategy
131     */
132    public GroebnerBaseParIter(int threads, ThreadPool pool, Reduction<C> red, PairList<C> pl) {
133        super(red, pl);
134        if (!(red instanceof ReductionPar)) {
135            logger.warn("parallel GB should use parallel aware reduction");
136        }
137        if (threads < 1) {
138            threads = 1;
139        }
140        this.threads = threads;
141        this.pool = pool;
142    }
143
144
145    /**
146     * Cleanup and terminate ThreadPool.
147     */
148    @Override
149    public void terminate() {
150        if (pool == null) {
151            return;
152        }
153        pool.terminate();
154    }
155
156
157    /**
158     * Cancel ThreadPool.
159     */
160    @Override
161    public int cancel() {
162        if (pool == null) {
163            return 0;
164        }
165        int s = pool.cancel();
166        return s;
167    }
168
169
170    /**
171     * Parallel iterative Groebner base using pairlist class.
172     * @param modv number of module variables.
173     * @param F polynomial list.
174     * @return GB(F) a Groebner base of F.
175     */
176    public List<GenPolynomial<C>> GB(int modv, List<GenPolynomial<C>> F) {
177        List<GenPolynomial<C>> G = normalizeZerosOnes(F);
178        G = PolyUtil.<C> monic(G);
179        if (G.size() <= 1) {
180            return G;
181        }
182        // sort, no reverse
183        G = OrderedPolynomialList.<C> sort(G);
184        //no: Collections.reverse(G);
185        logger.info("G-sort = " + G);
186
187        List<GenPolynomial<C>> Gp = new ArrayList<GenPolynomial<C>>();
188        for (GenPolynomial<C> p : G) {
189            if (debug) {
190                logger.info("p = " + p);
191            }
192            Gp = GB(modv, Gp, p);
193            //System.out.println("GB(Gp+p) = " + Gp);
194            if (Gp.size() > 0) {
195                if (Gp.get(0).isONE()) {
196                    return Gp;
197                }
198            }
199        }
200        return Gp;
201    }
202
203
204    /**
205     * Groebner base using pairlist class.
206     * @param modv module variable number.
207     * @param G polynomial list of a Groebner base.
208     * @param f polynomial.
209     * @return GB(G,f) a Groebner base of G+(f).
210     */
211    public List<GenPolynomial<C>> GB(int modv, List<GenPolynomial<C>> G, GenPolynomial<C> f) {
212        List<GenPolynomial<C>> F = new ArrayList<GenPolynomial<C>>(G);
213        GenPolynomial<C> g = f.monic();
214        if (F.isEmpty()) {
215            F.add(g);
216            return F;
217        }
218        if (g.isZERO()) {
219            return F;
220        }
221        if (g.isONE()) {
222            F.clear();
223            F.add(g);
224            return F;
225        }
226        GenPolynomialRing<C> ring = G.get(0).ring;
227        if (!ring.coFac.isField()) {
228            throw new IllegalArgumentException("coefficients not from a field");
229        }
230        PairList<C> pairlist = strategy.create(modv, ring);
231        pairlist.setList(G);
232        G.add(g);
233        pairlist.put(g);
234        logger.info("start " + pairlist);
235
236        Terminator fin = new Terminator(threads);
237        for (int i = 0; i < threads; i++) {
238            ReducerIter<C> R = new ReducerIter<C>(fin, G, pairlist);
239            pool.addJob(R);
240        }
241        fin.waitDone();
242        if (Thread.currentThread().isInterrupted()) {
243            throw new RuntimeException("interrupt before minimalGB");
244        }
245        logger.debug("#parallel list = " + G.size());
246        G = minimalGB(G);
247        // not in this context // pool.terminate();
248        logger.info("end   " + pairlist);
249        return G;
250    }
251
252
253    /**
254     * Minimal ordered groebner basis, parallel.
255     * @param Fp a Groebner base.
256     * @return minimalGB(F) a minimal Groebner base of Fp.
257     */
258    @Override
259    public List<GenPolynomial<C>> minimalGB(List<GenPolynomial<C>> Fp) {
260        GenPolynomial<C> a;
261        ArrayList<GenPolynomial<C>> G;
262        G = new ArrayList<GenPolynomial<C>>(Fp.size());
263        ListIterator<GenPolynomial<C>> it = Fp.listIterator();
264        while (it.hasNext()) {
265            a = it.next();
266            if (a.length() != 0) { // always true
267                // already monic  a = a.monic();
268                G.add(a);
269            }
270        }
271        if (G.size() <= 1) {
272            return G;
273        }
274
275        ExpVector e;
276        ExpVector f;
277        GenPolynomial<C> p;
278        ArrayList<GenPolynomial<C>> F;
279        F = new ArrayList<GenPolynomial<C>>(G.size());
280        boolean mt;
281        while (G.size() > 0) {
282            a = G.remove(0);
283            e = a.leadingExpVector();
284
285            it = G.listIterator();
286            mt = false;
287            while (it.hasNext() && !mt) {
288                p = it.next();
289                f = p.leadingExpVector();
290                mt = e.multipleOf(f);
291            }
292            it = F.listIterator();
293            while (it.hasNext() && !mt) {
294                p = it.next();
295                f = p.leadingExpVector();
296                mt = e.multipleOf(f);
297            }
298            if (!mt) {
299                F.add(a); // no thread at this point
300            } else {
301                // System.out.println("dropped " + a.length());
302            }
303        }
304        G = F;
305        if (G.size() <= 1) {
306            return G;
307        }
308        Collections.reverse(G); // important for lex GB
309
310        @SuppressWarnings("cast")
311        MiReducerIter<C>[] mirs = (MiReducerIter<C>[]) new MiReducerIter[G.size()];
312        int i = 0;
313        F = new ArrayList<GenPolynomial<C>>(G.size());
314        while (G.size() > 0) {
315            a = G.remove(0);
316            List<GenPolynomial<C>> R = new ArrayList<GenPolynomial<C>>(G.size() + F.size());
317            R.addAll(G);
318            R.addAll(F);
319            // System.out.println("doing " + a.length());
320            mirs[i] = new MiReducerIter<C>(R, a);
321            pool.addJob(mirs[i]);
322            i++;
323            F.add(a);
324        }
325        G = F;
326        F = new ArrayList<GenPolynomial<C>>(G.size());
327        for (i = 0; i < mirs.length; i++) {
328            a = mirs[i].getNF();
329            F.add(a);
330        }
331        return F;
332    }
333
334}
335
336
337/**
338 * Reducing worker threads.
339 */
340class ReducerIter<C extends RingElem<C>> implements Runnable {
341
342
343    private final List<GenPolynomial<C>> G;
344
345
346    private final PairList<C> pairlist;
347
348
349    private final Terminator fin;
350
351
352    private final ReductionPar<C> red;
353
354
355    private static final Logger logger = LogManager.getLogger(ReducerIter.class);
356
357
358    ReducerIter(Terminator fin, List<GenPolynomial<C>> G, PairList<C> L) {
359        this.fin = fin;
360        this.fin.initIdle(1);
361        this.G = G;
362        pairlist = L;
363        red = new ReductionPar<C>();
364    }
365
366
367    /**
368     * to string
369     */
370    @Override
371    public String toString() {
372        return "ReducerIter";
373    }
374
375
376    public void run() {
377        Pair<C> pair;
378        GenPolynomial<C> pi, pj, S, H;
379        //boolean set = false;
380        int reduction = 0;
381        int sleeps = 0;
382        while (pairlist.hasNext() || fin.hasJobs()) {
383            while (!pairlist.hasNext()) {
384                // wait
385                //fin.beIdle(); set = true;
386                try {
387                    sleeps++;
388                    if (sleeps % 10 == 0) {
389                        logger.info(" reducer is sleeping");
390                    } else {
391                        logger.debug("r");
392                    }
393                    Thread.sleep(100);
394                } catch (InterruptedException e) {
395                    fin.allIdle();
396                    logger.info("shutdown " + fin + " after: " + e);
397                    //throw new RuntimeException("interrupt 1 in pairlist.hasNext loop");
398                    break;
399                }
400                if (Thread.currentThread().isInterrupted()) {
401                    //fin.initIdle(1);
402                    fin.allIdle();
403                    logger.info("shutdown after .isInterrupted(): " + fin);
404                    //throw new RuntimeException("interrupt 2 in pairlist.hasNext loop");
405                    break;
406                }
407                if (!fin.hasJobs()) {
408                    break;
409                }
410            }
411            if (!pairlist.hasNext() && !fin.hasJobs()) {
412                break;
413            }
414            //if ( set ) {
415            //fin.notIdle(); set = false;
416            //}
417
418            fin.notIdle(); // before pairlist get
419            pair = pairlist.removeNext();
420            if (Thread.currentThread().isInterrupted()) {
421                fin.initIdle(1);
422                throw new RuntimeException("interrupt after removeNext");
423            }
424            if (pair == null) {
425                fin.initIdle(1);
426                continue;
427            }
428
429            pi = pair.pi;
430            pj = pair.pj;
431            if (logger.isDebugEnabled()) {
432                logger.debug("pi    = " + pi);
433                logger.debug("pj    = " + pj);
434            }
435
436            S = red.SPolynomial(pi, pj);
437            if (S.isZERO()) {
438                pair.setZero();
439                fin.initIdle(1);
440                continue;
441            }
442            if (logger.isDebugEnabled()) {
443                logger.debug("ht(S) = " + S.leadingExpVector());
444            }
445
446            H = red.normalform(G, S); //mod
447            reduction++;
448            if (H.isZERO()) {
449                pair.setZero();
450                fin.initIdle(1);
451                continue;
452            }
453            if (logger.isDebugEnabled()) {
454                logger.info("ht(H) = " + H.leadingExpVector());
455            }
456
457            H = H.monic();
458            // System.out.println("H   = " + H);
459            if (H.isONE()) {
460                // putOne not required
461                pairlist.put(H);
462                synchronized (G) {
463                    G.clear();
464                    G.add(H);
465                }
466                fin.allIdle();
467                return;
468            }
469            if (logger.isDebugEnabled()) {
470                logger.debug("H = " + H);
471            }
472            synchronized (G) {
473                G.add(H);
474            }
475            pairlist.put(H);
476            fin.initIdle(1);
477        }
478        fin.allIdle();
479        logger.info("terminated, done " + reduction + " reductions");
480    }
481}
482
483
484/**
485 * Reducing worker threads for minimal GB.
486 */
487class MiReducerIter<C extends RingElem<C>> implements Runnable {
488
489
490    private final List<GenPolynomial<C>> G;
491
492
493    private GenPolynomial<C> H;
494
495
496    private final ReductionPar<C> red;
497
498
499    private final Semaphore done = new Semaphore(0);
500
501
502    private static final Logger logger = LogManager.getLogger(MiReducerIter.class);
503
504
505    MiReducerIter(List<GenPolynomial<C>> G, GenPolynomial<C> p) {
506        this.G = G;
507        H = p;
508        red = new ReductionPar<C>();
509    }
510
511
512    /**
513     * to string
514     */
515    @Override
516    public String toString() {
517        return "MiReducerIter";
518    }
519
520
521    /**
522     * getNF. Blocks until the normal form is computed.
523     * @return the computed normal form.
524     */
525    public GenPolynomial<C> getNF() {
526        try {
527            done.acquire(); //done.P();
528        } catch (InterruptedException e) {
529            throw new RuntimeException("interrupt in getNF");
530        }
531        return H;
532    }
533
534
535    public void run() {
536        if (logger.isDebugEnabled()) {
537            logger.debug("ht(H) = " + H.leadingExpVector());
538        }
539        try {
540            H = red.normalform(G, H); //mod
541            done.release(); //done.V();
542        } catch (RuntimeException e) {
543            Thread.currentThread().interrupt();
544            //throw new RuntimeException("interrupt in getNF");
545        }
546        if (logger.isDebugEnabled()) {
547            logger.debug("ht(H) = " + H.leadingExpVector());
548        }
549        // H = H.monic();
550    }
551
552}