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