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