001/*
002 * $Id: GroebnerBaseDistributedMPJ.java 5796 2018-03-26 09:24:00Z kredel $
003 */
004
005package edu.jas.gb;
006
007
008import java.io.IOException;
009import java.util.ArrayList;
010import java.util.Collections;
011import java.util.List;
012import java.util.ListIterator;
013import java.util.concurrent.Semaphore;
014
015import org.apache.log4j.Logger;
016
017import edu.jas.kern.MPJEngine;
018import edu.jas.poly.ExpVector;
019import edu.jas.poly.GenPolynomial;
020import edu.jas.poly.GenPolynomialRing;
021import edu.jas.poly.PolyUtil;
022import edu.jas.structure.RingElem;
023import edu.jas.util.DistHashTableMPJ;
024import edu.jas.util.MPJChannel;
025import edu.jas.util.Terminator;
026import edu.jas.util.ThreadPool;
027
028import mpi.Comm;
029
030
031/**
032 * Groebner Base distributed algorithm with MPJ. Implements a distributed memory
033 * parallel version of Groebner bases. Using MPJ and pairlist class, distributed
034 * tasks do reduction.
035 * @param <C> coefficient type
036 * @author Heinz Kredel
037 */
038
039public class GroebnerBaseDistributedMPJ<C extends RingElem<C>> extends GroebnerBaseAbstract<C> {
040
041
042    private static final Logger logger = Logger.getLogger(GroebnerBaseDistributedMPJ.class);
043
044
045    /**
046     * Number of threads to use.
047     */
048    protected final int threads;
049
050
051    /**
052     * Default number of threads.
053     */
054    public static final int DEFAULT_THREADS = 2;
055
056
057    /*
058     * Pool of threads to use. <b>Note:</b> No ComputerThreads for one node
059     * tests
060     */
061    protected transient final ThreadPool pool;
062
063
064    /*
065     * Underlying MPJ engine.
066     */
067    protected transient final Comm engine;
068
069
070    /**
071     * Constructor.
072     */
073    public GroebnerBaseDistributedMPJ() throws IOException {
074        this(DEFAULT_THREADS);
075    }
076
077
078    /**
079     * Constructor.
080     * @param threads number of threads to use.
081     */
082    public GroebnerBaseDistributedMPJ(int threads) throws IOException {
083        this(threads, new ThreadPool(threads));
084    }
085
086
087    /**
088     * Constructor.
089     * @param threads number of threads to use.
090     * @param pool ThreadPool to use.
091     */
092    public GroebnerBaseDistributedMPJ(int threads, ThreadPool pool) throws IOException {
093        this(threads, pool, new OrderedPairlist<C>());
094    }
095
096
097    /**
098     * Constructor.
099     * @param threads number of threads to use.
100     * @param pl pair selection strategy
101     */
102    public GroebnerBaseDistributedMPJ(int threads, PairList<C> pl) throws IOException {
103        this(threads, new ThreadPool(threads), pl);
104    }
105
106
107    /**
108     * Constructor.
109     * @param threads number of threads to use.
110     * @param pool ThreadPool to use.
111     * @param pl pair selection strategy
112     */
113    public GroebnerBaseDistributedMPJ(int threads, ThreadPool pool, PairList<C> pl) throws IOException {
114        super(new ReductionPar<C>(), pl);
115        this.engine = MPJEngine.getCommunicator();
116        int size = engine.Size();
117        if (size < 2) {
118            throw new IllegalArgumentException("Minimal 2 MPJ processes required, not " + size);
119        }
120        if (threads != size || pool.getNumber() != size) {
121            throw new IllegalArgumentException(
122                            "threads != size: " + threads + " != " + size + ", #pool " + pool.getNumber());
123        }
124        this.threads = threads;
125        this.pool = pool;
126    }
127
128
129    /**
130     * Cleanup and terminate ThreadPool.
131     */
132    @Override
133    public void terminate() {
134        if (pool == null) {
135            return;
136        }
137        //pool.terminate();
138        pool.cancel();
139    }
140
141
142    /**
143     * Distributed Groebner base.
144     * @param modv number of module variables.
145     * @param F polynomial list.
146     * @return GB(F) a Groebner base of F or null, if a IOException occurs or on
147     *         MPJ client part.
148     */
149    public List<GenPolynomial<C>> GB(int modv, List<GenPolynomial<C>> F) {
150        try {
151            if (engine.Rank() == 0) {
152                return GBmaster(modv, F);
153            }
154        } catch (IOException e) {
155            logger.info("GBmaster: " + e);
156            e.printStackTrace();
157            return null;
158        }
159        pool.terminate(); // not used on clients
160        try {
161            clientPart(0);
162        } catch (IOException e) {
163            logger.info("clientPart: " + e);
164            e.printStackTrace();
165        }
166        return null;
167    }
168
169
170    /**
171     * Distributed Groebner base, part for MPJ master.
172     * @param modv number of module variables.
173     * @param F polynomial list.
174     * @return GB(F) a Groebner base of F or null, if a IOException occurs.
175     */
176    List<GenPolynomial<C>> GBmaster(int modv, List<GenPolynomial<C>> F) throws IOException {
177        List<GenPolynomial<C>> G = normalizeZerosOnes(F);
178        G = PolyUtil.<C> monic(G);
179        if (G.size() <= 1) {
180            //return G; 
181        }
182        if (G.isEmpty()) {
183            throw new IllegalArgumentException("empty F / zero ideal not allowed");
184        }
185        GenPolynomialRing<C> ring = G.get(0).ring;
186        if (!ring.coFac.isField()) {
187            throw new IllegalArgumentException("coefficients not from a field");
188        }
189        PairList<C> pairlist = strategy.create(modv, ring);
190        pairlist.put(G);
191
192        /*
193        List<GenPolynomial<C>> G = new ArrayList<GenPolynomial<C>>();
194        GenPolynomial<C> p;
195        PairList<C> pairlist = null;
196        boolean oneInGB = false;
197        int l = F.size();
198        int unused = 0;
199        ListIterator<GenPolynomial<C>> it = F.listIterator();
200        while (it.hasNext()) {
201            p = it.next();
202            if (p.length() > 0) {
203                p = p.monic();
204                if (p.isONE()) {
205                    oneInGB = true;
206                    G.clear();
207                    G.add(p);
208                    //return G; must signal termination to others
209                }
210                if (!oneInGB) {
211                    G.add(p);
212                }
213                if (pairlist == null) {
214                    pairlist = strategy.create(modv, p.ring);
215                    if (!p.ring.coFac.isField()) {
216                        throw new IllegalArgumentException("coefficients not from a field");
217                    }
218                }
219                // theList not updated here
220                if (p.isONE()) {
221                    unused = pairlist.putOne();
222                } else {
223                    unused = pairlist.put(p);
224                }
225            } else {
226                l--;
227            }
228        }
229        //if (l <= 1) {
230        //return G; must signal termination to others
231        //}
232        */
233        logger.info("done pairlist, initialize DHT: " + pairlist);
234
235        DistHashTableMPJ<Integer, GenPolynomial<C>> theList = new DistHashTableMPJ<Integer, GenPolynomial<C>>(
236                        engine);
237        theList.init();
238        //logger.info("done DHT: " + theList);
239
240        List<GenPolynomial<C>> al = pairlist.getList();
241        for (int i = 0; i < al.size(); i++) {
242            // no wait required
243            GenPolynomial<C> nn = theList.put(Integer.valueOf(i), al.get(i));
244            if (nn != null) {
245                logger.info("double polynomials " + i + ", nn = " + nn + ", al(i) = " + al.get(i));
246            }
247        }
248
249        Terminator fin = new Terminator(threads - 1);
250        MPJReducerServer<C> R;
251        for (int i = 1; i < threads; i++) {
252            logger.debug("addJob " + i + " of " + threads);
253            MPJChannel chan = new MPJChannel(engine, i); // closed in server
254            R = new MPJReducerServer<C>(i, fin, chan, theList, pairlist);
255            pool.addJob(R);
256        }
257        logger.debug("main loop waiting");
258        fin.waitDone();
259        int ps = theList.size();
260        logger.info("#distributed list = " + ps);
261        // make sure all polynomials arrived: not needed in master
262        // G = (ArrayList)theList.values();
263        G = pairlist.getList();
264        if (ps != G.size()) {
265            logger.info("#distributed list = " + theList.size() + " #pairlist list = " + G.size());
266        }
267        long time = System.currentTimeMillis();
268        List<GenPolynomial<C>> Gp = minimalGB(G); // not jet distributed but threaded
269        time = System.currentTimeMillis() - time;
270        logger.debug("parallel gbmi = " + time);
271        G = Gp;
272        logger.info("theList.terminate()");
273        theList.terminate();
274        logger.info("end" + pairlist);
275        return G;
276    }
277
278
279    /**
280     * GB distributed client.
281     * @param rank of the MPJ where the server runs on.
282     * @throws IOException
283     */
284    public void clientPart(int rank) throws IOException {
285        if (rank != 0) {
286            throw new UnsupportedOperationException("only master at rank 0 implemented: " + rank);
287        }
288        Comm engine = MPJEngine.getCommunicator();
289
290        DistHashTableMPJ<Integer, GenPolynomial<C>> theList = new DistHashTableMPJ<Integer, GenPolynomial<C>>();
291        theList.init();
292
293        MPJChannel chan = new MPJChannel(engine, rank);
294
295        MPJReducerClient<C> R = new MPJReducerClient<C>(chan, theList);
296        R.run();
297
298        chan.close();
299        theList.terminate();
300        return;
301    }
302
303
304    /**
305     * Minimal ordered groebner basis.
306     * @param Fp a Groebner base.
307     * @return a reduced Groebner base of Fp.
308     */
309    @SuppressWarnings("unchecked")
310    @Override
311    public List<GenPolynomial<C>> minimalGB(List<GenPolynomial<C>> Fp) {
312        GenPolynomial<C> a;
313        ArrayList<GenPolynomial<C>> G;
314        G = new ArrayList<GenPolynomial<C>>(Fp.size());
315        ListIterator<GenPolynomial<C>> it = Fp.listIterator();
316        while (it.hasNext()) {
317            a = it.next();
318            if (a.length() != 0) { // always true
319                // already monic  a = a.monic();
320                G.add(a);
321            }
322        }
323        if (G.size() <= 1) {
324            return G;
325        }
326
327        ExpVector e;
328        ExpVector f;
329        GenPolynomial<C> p;
330        ArrayList<GenPolynomial<C>> F;
331        F = new ArrayList<GenPolynomial<C>>(G.size());
332        boolean mt;
333
334        while (G.size() > 0) {
335            a = G.remove(0);
336            e = a.leadingExpVector();
337
338            it = G.listIterator();
339            mt = false;
340            while (it.hasNext() && !mt) {
341                p = it.next();
342                f = p.leadingExpVector();
343                mt = e.multipleOf(f);
344            }
345            it = F.listIterator();
346            while (it.hasNext() && !mt) {
347                p = it.next();
348                f = p.leadingExpVector();
349                mt = e.multipleOf(f);
350            }
351            if (!mt) {
352                F.add(a);
353            } else {
354                // System.out.println("dropped " + a.length());
355            }
356        }
357        G = F;
358        if (G.size() <= 1) {
359            return G;
360        }
361        Collections.reverse(G); // important for lex GB
362
363        MiMPJReducerServer<C>[] mirs = (MiMPJReducerServer<C>[]) new MiMPJReducerServer[G.size()];
364        int i = 0;
365        F = new ArrayList<GenPolynomial<C>>(G.size());
366        while (G.size() > 0) {
367            a = G.remove(0);
368            // System.out.println("doing " + a.length());
369            List<GenPolynomial<C>> R = new ArrayList<GenPolynomial<C>>(G.size() + F.size());
370            R.addAll(G);
371            R.addAll(F);
372            mirs[i] = new MiMPJReducerServer<C>(R, a);
373            pool.addJob(mirs[i]);
374            i++;
375            F.add(a);
376        }
377        G = F;
378        F = new ArrayList<GenPolynomial<C>>(G.size());
379        for (i = 0; i < mirs.length; i++) {
380            a = mirs[i].getNF();
381            F.add(a);
382        }
383        return F;
384    }
385
386}
387
388
389/**
390 * Distributed server reducing worker threads.
391 * @param <C> coefficient type
392 */
393
394class MPJReducerServer<C extends RingElem<C>> implements Runnable {
395
396
397    /*
398     * Termination detection coordinator.
399     */
400    private final Terminator finaler;
401
402
403    /*
404     * Underlying MPJ engine.
405     */
406    //protected transient final Comm engine;
407
408
409    /*
410     * MPJ channel.
411     */
412    private final MPJChannel pairChannel;
413
414
415    /*
416     * GB rank.
417     */
418    final int rank;
419
420
421    /*
422     * Distributed HashTable of polynomials.
423     */
424    private final DistHashTableMPJ<Integer, GenPolynomial<C>> theList;
425
426
427    /*
428     * Critical pair list of polynomials.
429     */
430    private final PairList<C> pairlist;
431
432
433    private static final Logger logger = Logger.getLogger(MPJReducerServer.class);
434
435
436    /**
437     * Constructor.
438     * @param r MPJ rank of partner.
439     * @param fin termination coordinator to use.
440     * @param c MPJ channel to use.
441     * @param dl DHT to use.
442     * @param L pair selection strategy
443     */
444    MPJReducerServer(int r, Terminator fin, MPJChannel c, DistHashTableMPJ<Integer, GenPolynomial<C>> dl,
445                    PairList<C> L) {
446        rank = r;
447        finaler = fin;
448        //engine = e;
449        theList = dl;
450        pairlist = L;
451        pairChannel = c;
452        logger.debug("reducer server constructor: "); // + r);
453    }
454
455
456    /**
457     * Main method.
458     */
459    @SuppressWarnings("unchecked")
460    public void run() {
461        logger.debug("reducer server running: "); // + this);
462        // try {
463        //     pairChannel = new MPJChannel(engine, rank);
464        // } catch (IOException e) {
465        //     e.printStackTrace();
466        //     return;
467        // }
468        if (logger.isInfoEnabled()) {
469            logger.info("reducer server running: pairChannel = " + pairChannel);
470        }
471        Pair<C> pair;
472        GenPolynomial<C> H = null;
473        boolean set = false;
474        boolean goon = true;
475        int polIndex = -1;
476        int red = 0;
477        int sleeps = 0;
478
479        // while more requests
480        while (goon) {
481            // receive request
482            logger.debug("receive request");
483            Object req = null;
484            try {
485                req = pairChannel.receive();
486            } catch (IOException e) {
487                goon = false;
488                e.printStackTrace();
489            } catch (ClassNotFoundException e) {
490                goon = false;
491                e.printStackTrace();
492            }
493            //logger.debug("received request, req = " + req);
494            if (req == null) {
495                goon = false;
496                break;
497            }
498            if (!(req instanceof GBTransportMessReq)) {
499                goon = false;
500                break;
501            }
502
503            // find pair
504            logger.debug("find pair");
505            while (!pairlist.hasNext()) { // wait
506                if (!set) {
507                    finaler.beIdle();
508                    set = true;
509                }
510                if (!finaler.hasJobs() && !pairlist.hasNext()) {
511                    goon = false;
512                    break;
513                }
514                try {
515                    sleeps++;
516                    if (sleeps % 10 == 0) {
517                        logger.info(" reducer is sleeping");
518                    }
519                    Thread.sleep(100);
520                } catch (InterruptedException e) {
521                    goon = false;
522                    break;
523                }
524            }
525            if (!pairlist.hasNext() && !finaler.hasJobs()) {
526                goon = false;
527                break; //continue; //break?
528            }
529            if (set) {
530                set = false;
531                finaler.notIdle();
532            }
533
534            pair = pairlist.removeNext();
535            /*
536             * send pair to client, receive H
537             */
538            logger.debug("send pair = " + pair);
539            GBTransportMess msg = null;
540            if (pair != null) {
541                msg = new GBTransportMessPairIndex(pair);
542            } else {
543                msg = new GBTransportMess(); //End();
544                // goon ?= false;
545            }
546            try {
547                pairChannel.send(msg);
548            } catch (IOException e) {
549                e.printStackTrace();
550                goon = false;
551                break;
552            }
553            logger.debug("#distributed list = " + theList.size());
554            Object rh = null;
555            try {
556                rh = pairChannel.receive();
557            } catch (IOException e) {
558                e.printStackTrace();
559                goon = false;
560                break;
561            } catch (ClassNotFoundException e) {
562                e.printStackTrace();
563                goon = false;
564                break;
565            }
566            //logger.debug("received H polynomial");
567            if (rh == null) {
568                if (pair != null) {
569                    pair.setZero();
570                }
571            } else if (rh instanceof GBTransportMessPoly) {
572                // update pair list
573                red++;
574                H = ((GBTransportMessPoly<C>) rh).pol;
575                if (logger.isDebugEnabled()) {
576                    logger.debug("H = " + H);
577                }
578                if (H == null) {
579                    if (pair != null) {
580                        pair.setZero();
581                    }
582                } else {
583                    if (H.isZERO()) {
584                        pair.setZero();
585                    } else {
586                        if (H.isONE()) {
587                            polIndex = pairlist.putOne();
588                            //GenPolynomial<C> nn = 
589                            theList.putWait(Integer.valueOf(polIndex), H);
590                            goon = false;
591                            break;
592                        }
593                        polIndex = pairlist.put(H);
594                        // use putWait ? but still not all distributed
595                        //GenPolynomial<C> nn = 
596                        theList.putWait(Integer.valueOf(polIndex), H);
597                    }
598                }
599            }
600        }
601        logger.info("terminated, done " + red + " reductions");
602
603        /*
604         * send end mark to client
605         */
606        logger.debug("send end");
607        try {
608            pairChannel.send(new GBTransportMessEnd());
609        } catch (IOException e) {
610            if (logger.isDebugEnabled()) {
611                e.printStackTrace();
612            }
613        }
614        finaler.beIdle();
615        pairChannel.close();
616    }
617
618}
619
620
621/**
622 * Distributed clients reducing worker threads.
623 */
624
625class MPJReducerClient<C extends RingElem<C>> implements Runnable {
626
627
628    private final MPJChannel pairChannel;
629
630
631    private final DistHashTableMPJ<Integer, GenPolynomial<C>> theList;
632
633
634    private final ReductionPar<C> red;
635
636
637    private static final Logger logger = Logger.getLogger(MPJReducerClient.class);
638
639
640    /**
641     * Constructor.
642     * @param pc MPJ communication channel.
643     * @param dl DHT to use.
644     */
645    MPJReducerClient(MPJChannel pc, DistHashTableMPJ<Integer, GenPolynomial<C>> dl) {
646        pairChannel = pc;
647        theList = dl;
648        red = new ReductionPar<C>();
649    }
650
651
652    /**
653     * Main run method.
654     */
655    @SuppressWarnings("unchecked")
656    public void run() {
657        logger.debug("reducer client running");
658        Pair<C> pair = null;
659        GenPolynomial<C> pi, pj, ps;
660        GenPolynomial<C> S;
661        GenPolynomial<C> H = null;
662        //boolean set = false;
663        boolean goon = true;
664        int reduction = 0;
665        //int sleeps = 0;
666        Integer pix, pjx, psx;
667
668        while (goon) {
669            /* protocol:
670             * request pair, process pair, send result
671             */
672            // pair = (Pair) pairlist.removeNext();
673            Object req = new GBTransportMessReq();
674            logger.debug("send request");
675            try {
676                pairChannel.send(req);
677            } catch (IOException e) {
678                goon = false;
679                e.printStackTrace();
680                break;
681            }
682            logger.debug("receive pair, goon");
683            Object pp = null;
684            try {
685                pp = pairChannel.receive();
686            } catch (IOException e) {
687                goon = false;
688                if (logger.isDebugEnabled()) {
689                    e.printStackTrace();
690                }
691                break;
692            } catch (ClassNotFoundException e) {
693                goon = false;
694                e.printStackTrace();
695            }
696            if (logger.isDebugEnabled()) {
697                logger.debug("received pair = " + pp);
698            }
699            H = null;
700            if (pp == null) { // should not happen
701                continue;
702            }
703            if (pp instanceof GBTransportMessEnd) {
704                goon = false;
705                continue;
706            }
707            if (pp instanceof GBTransportMessPair || pp instanceof GBTransportMessPairIndex) {
708                pi = pj = ps = null;
709                if (pp instanceof GBTransportMessPair) {
710                    pair = ((GBTransportMessPair<C>) pp).pair;
711                    if (pair != null) {
712                        pi = pair.pi;
713                        pj = pair.pj;
714                        //logger.debug("pair: pix = " + pair.i 
715                        //               + ", pjx = " + pair.j);
716                    }
717                }
718                if (pp instanceof GBTransportMessPairIndex) {
719                    pix = ((GBTransportMessPairIndex) pp).i;
720                    pjx = ((GBTransportMessPairIndex) pp).j;
721                    psx = ((GBTransportMessPairIndex) pp).s;
722                    pi = theList.getWait(pix);
723                    pj = theList.getWait(pjx);
724                    ps = theList.getWait(psx);
725                }
726
727                if (pi != null && pj != null) {
728                    S = red.SPolynomial(pi, pj);
729                    //System.out.println("S   = " + S);
730                    if (S.isZERO()) {
731                        // pair.setZero(); does not work in dist
732                    } else {
733                        if (logger.isDebugEnabled()) {
734                            logger.info("ht(S) = " + S.leadingExpVector());
735                        }
736                        H = red.normalform(theList, S);
737                        reduction++;
738                        if (H.isZERO()) {
739                            // pair.setZero(); does not work in dist
740                        } else {
741                            H = H.monic();
742                            if (logger.isInfoEnabled()) {
743                                logger.info("ht(H) = " + H.leadingExpVector());
744                            }
745                        }
746                    }
747                } else {
748                    logger.info("pi = " + pi + ", pj = " + pj + ", ps = " + ps);
749                }
750            }
751
752            // send H or must send null
753            if (logger.isDebugEnabled()) {
754                logger.debug("#distributed list = " + theList.size());
755                logger.debug("send H polynomial = " + H);
756            }
757            try {
758                pairChannel.send(new GBTransportMessPoly<C>(H));
759            } catch (IOException e) {
760                goon = false;
761                e.printStackTrace();
762            }
763        }
764        logger.info("terminated, done " + reduction + " reductions");
765        pairChannel.close();
766    }
767}
768
769
770/**
771 * Distributed server reducing worker threads for minimal GB Not jet distributed
772 * but threaded.
773 */
774
775class MiMPJReducerServer<C extends RingElem<C>> implements Runnable {
776
777
778    private final List<GenPolynomial<C>> G;
779
780
781    private GenPolynomial<C> H;
782
783
784    private final Semaphore done = new Semaphore(0);
785
786
787    private final Reduction<C> red;
788
789
790    private static final Logger logger = Logger.getLogger(MiMPJReducerServer.class);
791
792
793    /**
794     * Constructor.
795     * @param G polynomial list.
796     * @param p polynomial.
797     */
798    @SuppressWarnings("unchecked")
799    MiMPJReducerServer(List<GenPolynomial<C>> G, GenPolynomial<C> p) {
800        this.G = G;
801        H = p;
802        red = new ReductionPar<C>();
803    }
804
805
806    /**
807     * getNF. Blocks until the normal form is computed.
808     * @return the computed normal form.
809     */
810    public GenPolynomial<C> getNF() {
811        try {
812            done.acquire(); //done.P();
813        } catch (InterruptedException e) {
814        }
815        return H;
816    }
817
818
819    /**
820     * Main run method.
821     */
822    public void run() {
823        if (logger.isDebugEnabled()) {
824            logger.debug("ht(H) = " + H.leadingExpVector());
825        }
826        H = red.normalform(G, H); //mod
827        done.release(); //done.V();
828        if (logger.isDebugEnabled()) {
829            logger.debug("ht(H) = " + H.leadingExpVector());
830        }
831        // H = H.monic();
832    }
833}