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