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