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