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