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