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}