001/* 002 * $Id: SolvableGroebnerBaseParallel.java 5956 2018-10-29 21:58:01Z kredel $ 003 */ 004 005package edu.jas.gb; 006 007 008import java.util.ArrayList; 009import java.util.List; 010import java.util.ListIterator; 011import java.util.concurrent.Semaphore; 012import java.util.concurrent.Executors; 013import java.util.concurrent.ExecutorService; 014import java.util.concurrent.ThreadPoolExecutor; 015import java.util.concurrent.TimeUnit; 016 017import org.apache.logging.log4j.Logger; 018import org.apache.logging.log4j.LogManager; 019 020import edu.jas.poly.ExpVector; 021import edu.jas.poly.GenSolvablePolynomial; 022import edu.jas.poly.GenSolvablePolynomialRing; 023import edu.jas.poly.PolynomialList; 024import edu.jas.poly.PolyUtil; 025import edu.jas.structure.RingElem; 026import edu.jas.util.Terminator; 027 028 029/** 030 * Solvable Groebner Base parallel algorithm. Implements a shared memory 031 * parallel version of Groebner bases. Threads maintain pairlist. 032 * @param <C> coefficient type 033 * @author Heinz Kredel 034 */ 035 036public class SolvableGroebnerBaseParallel<C extends RingElem<C>> extends SolvableGroebnerBaseAbstract<C> { 037 038 039 private static final Logger logger = LogManager.getLogger(SolvableGroebnerBaseParallel.class); 040 041 042 //private static final boolean debug = logger.isDebugEnabled(); 043 044 045 /** 046 * Number of threads to use. 047 */ 048 protected final int threads; 049 050 051 /** 052 * Pool of threads to use. 053 */ 054 protected transient final ExecutorService pool; 055 056 057 /** 058 * Constructor. 059 */ 060 public SolvableGroebnerBaseParallel() { 061 this(2); 062 } 063 064 065 /** 066 * Constructor. 067 * @param threads number of threads to use. 068 */ 069 public SolvableGroebnerBaseParallel(int threads) { 070 this(threads, Executors.newFixedThreadPool(threads)); 071 } 072 073 074 /** 075 * Constructor. 076 * @param threads number of threads to use. 077 * @param pool ExecutorService to use. 078 */ 079 public SolvableGroebnerBaseParallel(int threads, ExecutorService pool) { 080 this(threads, pool, new SolvableReductionPar<C>()); 081 } 082 083 084 /** 085 * Constructor. 086 * @param threads number of threads to use. 087 * @param sred parallelism aware reduction engine 088 */ 089 public SolvableGroebnerBaseParallel(int threads, SolvableReduction<C> sred) { 090 this(threads, Executors.newFixedThreadPool(threads), sred); 091 } 092 093 094 /** 095 * Constructor. 096 * @param threads number of threads to use. 097 * @param pl pair selection strategy 098 */ 099 public SolvableGroebnerBaseParallel(int threads, PairList<C> pl) { 100 this(threads, Executors.newFixedThreadPool(threads), new SolvableReductionPar<C>(), pl); 101 } 102 103 104 /** 105 * Constructor. 106 * @param threads number of threads to use. 107 * @param sred parallelism aware reduction engine 108 * @param pl pair selection strategy 109 */ 110 public SolvableGroebnerBaseParallel(int threads, SolvableReduction<C> sred, PairList<C> pl) { 111 this(threads, Executors.newFixedThreadPool(threads), sred, pl); 112 } 113 114 115 /** 116 * Constructor. 117 * @param threads number of threads to use. 118 * @param pool ExecutorService to use. 119 * @param sred parallelism aware reduction engine 120 */ 121 public SolvableGroebnerBaseParallel(int threads, ExecutorService pool, SolvableReduction<C> sred) { 122 this(threads, pool, sred, new OrderedPairlist<C>()); 123 } 124 125 126 /** 127 * Constructor. 128 * @param threads number of threads to use. 129 * @param pool ExecutorService to use. 130 * @param sred parallelism aware reduction engine 131 * @param pl pair selection strategy 132 */ 133 public SolvableGroebnerBaseParallel(int threads, ExecutorService pool, SolvableReduction<C> sred, 134 PairList<C> pl) { 135 super(sred, pl); 136 if (!(sred instanceof SolvableReductionPar)) { 137 logger.warn("parallel GB should use parallel aware reduction"); 138 } 139 if (threads < 1) { 140 threads = 1; 141 } 142 this.threads = threads; 143 this.pool = pool; 144 } 145 146 147 /** 148 * Cleanup and terminate ExecutorService. 149 */ 150 @Override 151 public void terminate() { 152 if (pool == null) { 153 return; 154 } 155 pool.shutdown(); 156 try { 157 while (!pool.isTerminated()) { 158 //logger.info("await"); 159 boolean rest = pool.awaitTermination(1000L, TimeUnit.MILLISECONDS); 160 } 161 } catch (InterruptedException e) { 162 e.printStackTrace(); 163 } 164 logger.info(pool.toString()); 165 } 166 167 168 /** 169 * Cancel ExecutorService. 170 */ 171 @Override 172 public int cancel() { 173 if (pool == null) { 174 return 0; 175 } 176 int s = pool.shutdownNow().size(); 177 logger.info(pool.toString()); 178 return s; 179 } 180 181 182 /** 183 * Parallel Groebner base using sequential pair order class. Threads 184 * maintain pairlist. 185 * @param modv number of module variables. 186 * @param F polynomial list. 187 * @return GB(F) a Groebner base of F. 188 */ 189 public List<GenSolvablePolynomial<C>> leftGB(int modv, List<GenSolvablePolynomial<C>> F) { 190 List<GenSolvablePolynomial<C>> G = normalizeZerosOnes(F); 191 G = PolynomialList.castToSolvableList(PolyUtil.<C> monic(PolynomialList.castToList(G))); 192 if (G.size() <= 1) { 193 return G; 194 } 195 GenSolvablePolynomialRing<C> ring = G.get(0).ring; 196 if (!ring.coFac.isField() && ring.coFac.isCommutative()) { 197 throw new IllegalArgumentException("coefficients not from a field"); 198 } 199 PairList<C> pairlist = strategy.create(modv, ring); 200 pairlist.put(PolynomialList.castToList(G)); 201 logger.info("start " + pairlist); 202 203 Terminator fin = new Terminator(threads); 204 LeftSolvableReducer<C> R; 205 for (int i = 0; i < threads; i++) { 206 R = new LeftSolvableReducer<C>(fin, G, pairlist); 207 pool.execute(R); 208 } 209 fin.waitDone(); 210 logger.debug("#parallel list = " + G.size()); 211 G = leftMinimalGB(G); 212 // not in this context // pool.terminate(); 213 logger.info("end " + pairlist); 214 return G; 215 } 216 217 218 /** 219 * Minimal ordered groebner basis, parallel. 220 * @param Fp a Groebner base. 221 * @return minimalGB(F) a minimal Groebner base of Fp. 222 */ 223 @Override 224 public List<GenSolvablePolynomial<C>> leftMinimalGB(List<GenSolvablePolynomial<C>> Fp) { 225 GenSolvablePolynomial<C> a; 226 ArrayList<GenSolvablePolynomial<C>> G; 227 G = new ArrayList<GenSolvablePolynomial<C>>(Fp.size()); 228 ListIterator<GenSolvablePolynomial<C>> it = Fp.listIterator(); 229 while (it.hasNext()) { 230 a = it.next(); 231 if (a.length() != 0) { // always true 232 // already monic a = a.monic(); 233 G.add(a); 234 } 235 } 236 if (G.size() <= 1) { 237 return G; 238 } 239 240 ExpVector e; 241 ExpVector f; 242 GenSolvablePolynomial<C> p; 243 ArrayList<GenSolvablePolynomial<C>> F; 244 F = new ArrayList<GenSolvablePolynomial<C>>(G.size()); 245 boolean mt; 246 while (G.size() > 0) { 247 a = G.remove(0); 248 e = a.leadingExpVector(); 249 250 it = G.listIterator(); 251 mt = false; 252 while (it.hasNext() && !mt) { 253 p = it.next(); 254 f = p.leadingExpVector(); 255 mt = e.multipleOf(f); 256 } 257 it = F.listIterator(); 258 while (it.hasNext() && !mt) { 259 p = it.next(); 260 f = p.leadingExpVector(); 261 mt = e.multipleOf(f); 262 } 263 if (!mt) { 264 F.add(a); // no thread at this point 265 } else { 266 // System.out.println("dropped " + a.length()); 267 } 268 } 269 G = F; 270 if (G.size() <= 1) { 271 return G; 272 } 273 274 @SuppressWarnings("unchecked") 275 SolvableMiReducer<C>[] mirs = (SolvableMiReducer<C>[]) new SolvableMiReducer[G.size()]; 276 int i = 0; 277 F = new ArrayList<GenSolvablePolynomial<C>>(G.size()); 278 while (G.size() > 0) { 279 a = G.remove(0); 280 // System.out.println("doing " + a.length()); 281 List<GenSolvablePolynomial<C>> R = new ArrayList<GenSolvablePolynomial<C>>(G.size() + F.size()); 282 R.addAll(G); 283 R.addAll(F); 284 mirs[i] = new SolvableMiReducer<C>(R, a); 285 pool.execute(mirs[i]); 286 i++; 287 F.add(a); 288 } 289 G = F; 290 F = new ArrayList<GenSolvablePolynomial<C>>(G.size()); 291 for (i = 0; i < mirs.length; i++) { 292 a = mirs[i].getNF(); 293 F.add(a); 294 } 295 return F; 296 } 297 298 299 /** 300 * Solvable Extended Groebner base using critical pair class. 301 * @param modv module variable number. 302 * @param F solvable polynomial list. 303 * @return a container for an extended left Groebner base of F. 304 */ 305 @Override 306 public SolvableExtendedGB<C> extLeftGB(int modv, List<GenSolvablePolynomial<C>> F) { 307 throw new UnsupportedOperationException("parallel extLeftGB not implemented"); 308 } 309 310 311 /** 312 * Twosided Groebner base using pairlist class. 313 * @param modv number of module variables. 314 * @param Fp solvable polynomial list. 315 * @return tsGB(Fp) a twosided Groebner base of F. 316 */ 317 @SuppressWarnings("unchecked") 318 public List<GenSolvablePolynomial<C>> twosidedGB(int modv, List<GenSolvablePolynomial<C>> Fp) { 319 List<GenSolvablePolynomial<C>> G = normalizeZerosOnes(Fp); 320 G = PolynomialList.castToSolvableList(PolyUtil.<C> monic(PolynomialList.castToList(G))); 321 if (G.size() < 1) { // 0 not 1 322 return G; 323 } 324 if (G.size() <= 1) { 325 if (G.get(0).isONE()) { 326 return G; 327 } 328 } 329 GenSolvablePolynomialRing<C> ring = G.get(0).ring; 330 if (!ring.coFac.isField() && ring.coFac.isCommutative()) { 331 throw new IllegalArgumentException("coefficients not from a field"); 332 } 333 // add also coefficient generators 334 List<GenSolvablePolynomial<C>> X; 335 X = PolynomialList.castToSolvableList(ring.generators(modv)); 336 logger.info("right multipliers = " + X); 337 List<GenSolvablePolynomial<C>> F = new ArrayList<GenSolvablePolynomial<C>>(G.size() * (1 + X.size())); 338 F.addAll(G); 339 GenSolvablePolynomial<C> p, x, q; 340 for (int i = 0; i < F.size(); i++) { // F changes 341 p = F.get(i); 342 for (int j = 0; j < X.size(); j++) { 343 x = X.get(j); 344 if (x.isONE()) { 345 continue; 346 } 347 q = p.multiply(x); 348 q = sred.leftNormalform(F, q); 349 if (!q.isZERO()) { 350 q = q.monic(); 351 if (q.isONE()) { 352 G.clear(); 353 G.add(q); 354 return G; 355 } 356 F.add(q); 357 } 358 } 359 } 360 //System.out.println("F generated = " + F); 361 G = F; 362 if (G.size() <= 1) { // 1 okay here 363 return G; 364 } 365 PairList<C> pairlist = strategy.create(modv, ring); 366 pairlist.put(PolynomialList.castToList(G)); 367 logger.info("start " + pairlist); 368 369 Terminator fin = new Terminator(threads); 370 TwosidedSolvableReducer<C> R; 371 for (int i = 0; i < threads; i++) { 372 R = new TwosidedSolvableReducer<C>(fin, modv, X, G, pairlist); 373 pool.execute(R); 374 } 375 fin.waitDone(); 376 logger.debug("#parallel list = " + G.size()); 377 G = leftMinimalGB(G); 378 // not in this context // pool.terminate(); 379 logger.info("end " + pairlist); 380 return G; 381 } 382 383} 384 385 386/** 387 * Reducing left worker threads. 388 * @param <C> coefficient type 389 */ 390class LeftSolvableReducer<C extends RingElem<C>> implements Runnable { 391 392 393 private final List<GenSolvablePolynomial<C>> G; 394 395 396 private final PairList<C> pairlist; 397 398 399 private final Terminator pool; 400 401 402 private final SolvableReductionPar<C> sred; 403 404 405 private static final Logger logger = LogManager.getLogger(LeftSolvableReducer.class); 406 407 408 private static final boolean debug = logger.isDebugEnabled(); 409 410 411 LeftSolvableReducer(Terminator fin, List<GenSolvablePolynomial<C>> G, PairList<C> L) { 412 pool = fin; 413 this.G = G; 414 pairlist = L; 415 sred = new SolvableReductionPar<C>(); 416 } 417 418 419 @SuppressWarnings("unchecked") 420 public void run() { 421 Pair<C> pair; 422 GenSolvablePolynomial<C> S; 423 GenSolvablePolynomial<C> H; 424 boolean set = false; 425 int reduction = 0; 426 int sleeps = 0; 427 while (pairlist.hasNext() || pool.hasJobs()) { 428 while (!pairlist.hasNext()) { 429 // wait 430 pool.beIdle(); 431 set = true; 432 try { 433 sleeps++; 434 if (sleeps % 10 == 0) { 435 logger.info(" reducer is sleeping"); 436 } else { 437 logger.debug("r"); 438 } 439 Thread.sleep(100); 440 } catch (InterruptedException e) { 441 pool.allIdle(); 442 logger.info("shutdown " + pool + " after: " + e); 443 //throw new RuntimeException("interrupt 1 in pairlist.hasNext loop"); 444 break; 445 } 446 if (Thread.currentThread().isInterrupted()) { 447 //pool.initIdle(1); 448 pool.allIdle(); 449 logger.info("shutdown after .isInterrupted(): " + pool); 450 //throw new RuntimeException("interrupt 2 in pairlist.hasNext loop"); 451 break; 452 } 453 if (!pool.hasJobs()) { 454 break; 455 } 456 } 457 if (!pairlist.hasNext() && !pool.hasJobs()) { 458 break; 459 } 460 if (set) { 461 pool.notIdle(); 462 set = false; 463 } 464 pair = pairlist.removeNext(); 465 if (pair == null) { 466 continue; 467 } 468 if (debug) { 469 logger.debug("pi = " + pair.pi); 470 logger.debug("pj = " + pair.pj); 471 } 472 S = sred.leftSPolynomial((GenSolvablePolynomial<C>) pair.pi, (GenSolvablePolynomial<C>) pair.pj); 473 if (S.isZERO()) { 474 continue; 475 } 476 if (debug) { 477 logger.debug("ht(S) = " + S.leadingExpVector()); 478 } 479 H = sred.leftNormalform(G, S); //mod 480 reduction++; 481 if (H.isZERO()) { 482 continue; 483 } 484 if (debug) { 485 logger.debug("ht(H) = " + H.leadingExpVector()); 486 } 487 H = H.monic(); 488 // System.out.println("H = " + H); 489 if (H.isONE()) { 490 pairlist.putOne(); // not really required 491 synchronized (G) { 492 G.clear(); 493 G.add(H); 494 } 495 pool.allIdle(); 496 return; 497 } 498 if (debug) { 499 logger.debug("H = " + H); 500 } 501 synchronized (G) { 502 G.add(H); 503 } 504 pairlist.put(H); 505 } 506 logger.info("terminated, done " + reduction + " reductions"); 507 } 508} 509 510 511/** 512 * Reducing twosided worker threads. 513 * @param <C> coefficient type 514 */ 515class TwosidedSolvableReducer<C extends RingElem<C>> implements Runnable { 516 517 518 private final List<GenSolvablePolynomial<C>> X; 519 520 521 private final List<GenSolvablePolynomial<C>> G; 522 523 524 private final PairList<C> pairlist; 525 526 527 private final int modv; 528 529 530 private final Terminator pool; 531 532 533 private final SolvableReductionPar<C> sred; 534 535 536 private static final Logger logger = LogManager.getLogger(TwosidedSolvableReducer.class); 537 538 539 private static final boolean debug = logger.isDebugEnabled(); 540 541 542 TwosidedSolvableReducer(Terminator fin, int modv, List<GenSolvablePolynomial<C>> X, 543 List<GenSolvablePolynomial<C>> G, PairList<C> L) { 544 pool = fin; 545 this.modv = modv; 546 this.X = X; 547 this.G = G; 548 pairlist = L; 549 sred = new SolvableReductionPar<C>(); 550 } 551 552 553 public void run() { 554 GenSolvablePolynomial<C> p, x, S, H; 555 Pair<C> pair; 556 boolean set = false; 557 int reduction = 0; 558 int sleeps = 0; 559 logger.debug("modv = " + modv); // avoid "unused" 560 while (pairlist.hasNext() || pool.hasJobs()) { 561 while (!pairlist.hasNext()) { 562 // wait 563 pool.beIdle(); 564 set = true; 565 try { 566 sleeps++; 567 if (sleeps % 10 == 0) { 568 logger.info(" reducer is sleeping"); 569 } else { 570 logger.debug("r"); 571 } 572 Thread.sleep(50); 573 } catch (InterruptedException e) { 574 break; 575 } 576 if (!pool.hasJobs()) { 577 break; 578 } 579 } 580 if (!pairlist.hasNext() && !pool.hasJobs()) { 581 break; 582 } 583 if (set) { 584 pool.notIdle(); 585 set = false; 586 } 587 pair = pairlist.removeNext(); 588 if (pair == null) { 589 continue; 590 } 591 if (debug) { 592 logger.debug("pi = " + pair.pi); 593 logger.debug("pj = " + pair.pj); 594 } 595 S = sred.leftSPolynomial((GenSolvablePolynomial<C>) pair.pi, (GenSolvablePolynomial<C>) pair.pj); 596 if (S.isZERO()) { 597 continue; 598 } 599 if (debug) { 600 logger.debug("ht(S) = " + S.leadingExpVector()); 601 } 602 H = sred.leftNormalform(G, S); //mod 603 reduction++; 604 if (H.isZERO()) { 605 continue; 606 } 607 if (debug) { 608 logger.debug("ht(H) = " + H.leadingExpVector()); 609 } 610 H = H.monic(); 611 // System.out.println("H = " + H); 612 if (H.isONE()) { 613 pairlist.putOne(); // not really required 614 synchronized (G) { 615 G.clear(); 616 G.add(H); 617 } 618 pool.allIdle(); 619 return; 620 } 621 if (debug) { 622 logger.debug("H = " + H); 623 } 624 synchronized (G) { 625 G.add(H); 626 } 627 pairlist.put(H); 628 for (int j = 0; j < X.size(); j++) { 629 x = X.get(j); 630 p = H.multiply(x); 631 p = sred.leftNormalform(G, p); 632 if (!p.isZERO()) { 633 p = p.monic(); 634 if (p.isONE()) { 635 synchronized (G) { 636 G.clear(); 637 G.add(p); 638 } 639 pool.allIdle(); 640 return; 641 } 642 synchronized (G) { 643 G.add(p); 644 } 645 pairlist.put(p); 646 } 647 } 648 } 649 logger.info("terminated, done " + reduction + " reductions"); 650 } 651} 652 653 654/** 655 * Reducing worker threads for minimal GB. 656 * @param <C> coefficient type 657 */ 658class SolvableMiReducer<C extends RingElem<C>> implements Runnable { 659 660 661 private final List<GenSolvablePolynomial<C>> G; 662 663 664 private GenSolvablePolynomial<C> H; 665 666 667 private final SolvableReductionPar<C> sred; 668 669 670 private final Semaphore done = new Semaphore(0); 671 672 673 private static final Logger logger = LogManager.getLogger(SolvableMiReducer.class); 674 675 676 private static final boolean debug = logger.isDebugEnabled(); 677 678 679 SolvableMiReducer(List<GenSolvablePolynomial<C>> G, GenSolvablePolynomial<C> p) { 680 this.G = G; 681 H = p; 682 sred = new SolvableReductionPar<C>(); 683 } 684 685 686 /** 687 * getNF. Blocks until the normal form is computed. 688 * @return the computed normal form. 689 */ 690 public GenSolvablePolynomial<C> getNF() { 691 try { 692 done.acquire(); //done.P(); 693 } catch (InterruptedException e) { 694 } 695 return H; 696 } 697 698 699 public void run() { 700 if (debug) { 701 logger.debug("ht(H) = " + H.leadingExpVector()); 702 } 703 H = sred.leftNormalform(G, H); //mod 704 done.release(); //done.V(); 705 if (debug) { 706 logger.debug("ht(H) = " + H.leadingExpVector()); 707 } 708 // H = H.monic(); 709 } 710 711}