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