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