001/* 002 * $Id: RelationTable.java 4108 2012-08-18 10:57:40Z kredel $ 003 */ 004 005package edu.jas.poly; 006 007 008import java.io.Serializable; 009import java.util.ArrayList; 010import java.util.HashMap; 011import java.util.Iterator; 012import java.util.LinkedList; 013import java.util.List; 014import java.util.ListIterator; 015import java.util.Map; 016 017import org.apache.log4j.Logger; 018 019import edu.jas.kern.PrettyPrint; 020import edu.jas.structure.RingElem; 021 022 023/** 024 * RelationTable for solvable polynomials. This class maintains the 025 * non-commutative multiplication relations of solvable polynomial rings. The 026 * table entries are initialized with relations of the form x<sub>j</sub> * 027 * x<sub>i</sub> = p<sub>ij</sub>. During multiplication the ralations are 028 * updated by relations of the form x<sub>j</sub><sup>k</sup> * 029 * x<sub>i</sub><sup>l</sup> = p<sub>ijkl</sub>. If no relation for 030 * x<sub>j</sub> * x<sub>i</sub> is found in the table, this multiplication is 031 * assumed to be commutative x<sub>i</sub> x<sub>j</sub>. 032 * @author Heinz Kredel 033 */ 034 035public class RelationTable<C extends RingElem<C>> implements Serializable { 036 037 038 /** 039 * The data structure for the relations. 040 */ 041 public final Map<List<Integer>, List> table; 042 043 044 /** 045 * The factory for the solvable polynomial ring. 046 */ 047 public final GenSolvablePolynomialRing<C> ring; 048 049 050 private static final Logger logger = Logger.getLogger(RelationTable.class); 051 052 053 private final boolean debug = true; //logger.isDebugEnabled(); 054 055 056 /** 057 * Constructor for RelationTable requires ring factory. Note: This 058 * constructor is called within the constructor of the ring factory, so 059 * methods of this class can only be used after the other constructor has 060 * terminated. 061 * @param r solvable polynomial ring factory. 062 */ 063 protected RelationTable(GenSolvablePolynomialRing<C> r) { 064 table = new HashMap<List<Integer>, List>(); 065 ring = r; 066 if (ring == null) { 067 throw new IllegalArgumentException("RelationTable no ring"); 068 } 069 } 070 071 072 /** 073 * RelationTable equals. Tests same keySets only, not relations itself. Will 074 * be improved in the future. 075 * @see java.lang.Object#equals(java.lang.Object) 076 */ 077 @Override 078 @SuppressWarnings("unchecked") 079 // not jet working 080 public boolean equals(Object p) { 081 if (!(p instanceof RelationTable)) { 082 System.out.println("no RelationTable"); 083 return false; 084 } 085 RelationTable<C> tab = null; 086 try { 087 tab = (RelationTable<C>) p; 088 } catch (ClassCastException ignored) { 089 } 090 if (tab == null) { 091 return false; 092 } 093 if (!ring.equals(tab.ring)) { 094 System.out.println("not same Ring " + ring.toScript() + ", " + tab.ring.toScript()); 095 return false; 096 } 097 for (List<Integer> k : table.keySet()) { 098 List a = table.get(k); 099 List b = tab.table.get(k); 100 if (b == null) { 101 return false; 102 } 103 // check contents, but only base relations 104 if (!a.equals(b)) { 105 return false; 106 } 107 } 108 for (List<Integer> k : tab.table.keySet()) { 109 List a = table.get(k); 110 List b = tab.table.get(k); 111 if (a == null) { 112 return false; 113 } 114 // check contents, but only base relations 115 if (!a.equals(b)) { 116 return false; 117 } 118 } 119 return true; 120 } 121 122 123 /** 124 * Hash code for this relation table. 125 * @see java.lang.Object#hashCode() 126 */ 127 @Override 128 public int hashCode() { 129 int h; 130 h = ring.hashCode(); 131 h = 31 * h + table.hashCode(); 132 return h; 133 } 134 135 136 /** 137 * Get the String representation. 138 * @see java.lang.Object#toString() 139 */ 140 @Override 141 public String toString() { 142 List v; 143 StringBuffer s = new StringBuffer("RelationTable["); 144 boolean first = true; 145 for (List<Integer> k : table.keySet()) { 146 if (first) { 147 first = false; 148 } else { 149 s.append(", "); 150 } 151 s.append(k.toString()); 152 v = table.get(k); 153 s.append("="); 154 s.append(v.toString()); 155 } 156 s.append("]"); 157 return s.toString(); 158 } 159 160 161 /** 162 * Get the String representation. 163 * @param vars names for the variables. 164 * @see java.lang.Object#toString() 165 */ 166 @SuppressWarnings("unchecked") 167 public String toString(String[] vars) { 168 if (vars == null) { 169 return toString(); 170 } 171 List v; 172 StringBuffer s = new StringBuffer("RelationTable\n("); 173 if (PrettyPrint.isTrue()) { 174 boolean first = true; 175 for (List<Integer> k : table.keySet()) { 176 if (first) { 177 first = false; 178 s.append("\n"); 179 } else { 180 s.append(",\n"); 181 } 182 v = table.get(k); 183 for (Iterator jt = v.iterator(); jt.hasNext();) { 184 ExpVectorPair ep = (ExpVectorPair) jt.next(); 185 s.append("( " + ep.getFirst().toString(vars) + " ), "); 186 s.append("( " + ep.getSecond().toString(vars) + " ), "); 187 GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next(); 188 s.append("( " + p.toString(vars) + " )"); 189 if (jt.hasNext()) { 190 s.append(",\n"); 191 } 192 } 193 } 194 } else { 195 boolean first = true; 196 for (List<Integer> k : table.keySet()) { 197 if (first) { 198 first = false; 199 } else { 200 s.append(",\n"); 201 } 202 v = table.get(k); 203 for (Iterator jt = v.iterator(); jt.hasNext();) { 204 ExpVectorPair ep = (ExpVectorPair) jt.next(); 205 s.append("( " + ep.getFirst().toString(vars) + " ), "); 206 s.append("( " + ep.getSecond().toString(vars) + " ), "); 207 GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next(); 208 s.append("( " + p.toString(vars) + " )"); 209 if (jt.hasNext()) { 210 s.append(",\n"); 211 } 212 } 213 } 214 } 215 s.append("\n)\n"); 216 return s.toString(); 217 } 218 219 220 /** 221 * Get a scripting compatible string representation. 222 * @return script compatible representation for this relation table. 223 */ 224 public String toScript() { 225 // Python case 226 String[] vars = ring.vars; 227 List v; 228 StringBuffer s = new StringBuffer("["); 229 boolean first = true; 230 for (List<Integer> k : table.keySet()) { 231 if (first) { 232 first = false; 233 s.append(""); 234 } else { 235 s.append(", "); 236 } 237 v = table.get(k); 238 for (Iterator jt = v.iterator(); jt.hasNext();) { 239 ExpVectorPair ep = (ExpVectorPair) jt.next(); 240 s.append("" + ep.getFirst().toScript(vars) + ", "); 241 s.append("" + ep.getSecond().toScript(vars) + ", "); 242 GenPolynomial<C> p = (GenPolynomial<C>) jt.next(); 243 s.append("( " + p.toScript() + " )"); 244 if (jt.hasNext()) { 245 s.append(", "); 246 } 247 } 248 } 249 s.append("]"); 250 return s.toString(); 251 } 252 253 254 /** 255 * Update or initialize RelationTable with new relation. relation is e * f = 256 * p. 257 * @param e first term. 258 * @param f second term. 259 * @param p solvable product polynomial. 260 */ 261 @SuppressWarnings("unchecked") 262 public synchronized void update(ExpVector e, ExpVector f, GenSolvablePolynomial<C> p) { 263 if (debug) { 264 //System.out.println("new relation = " + e + " .*. " + f + " = " + p); 265 if (p != null && p.ring.vars != null) { 266 logger.info("new relation = " + e.toString(p.ring.vars) + " .*. " + f.toString(p.ring.vars) 267 + " = " + p); 268 //logger.info("existing relations = " + toString(p.ring.vars)); 269 } else { 270 logger.info("new relation = " + e + " .*. " + f + " = " + p); 271 } 272 } 273 if (p == null || e == null || f == null) { 274 throw new IllegalArgumentException("RelationTable update e|f|p == null"); 275 } 276 if (debug) { 277 if (e.totalDeg() == 1 && f.totalDeg() == 1) { 278 int[] de = e.dependencyOnVariables(); 279 int[] df = f.dependencyOnVariables(); 280 logger.debug("update e ? f " + de[0] + " " + df[0]); 281 //int t = ring.tord.getDescendComparator().compare(e,f); 282 //System.out.println("update compare(e,f) = " + t); 283 if (de[0] == df[0]) { // error 284 throw new IllegalArgumentException("RelationTable update e==f"); 285 } 286 if (de[0] > df[0]) { // invalid update 287 logger.error("warning: update e > f " + e + " " + f + " changed"); 288 ExpVector tmp = e; 289 e = f; 290 f = tmp; 291 Map.Entry<ExpVector, C> m = p.leadingMonomial(); 292 GenPolynomial<C> r = p.subtract(m.getValue(), m.getKey()); 293 r = r.negate(); 294 p = (GenSolvablePolynomial<C>) r.sum(m.getValue(), m.getKey()); 295 } 296 } 297 } 298 ExpVector ef = e.sum(f); 299 ExpVector lp = p.leadingExpVector(); 300 if (!ef.equals(lp)) { // check for suitable term order 301 logger.error("relation term order = " + ring.tord); 302 throw new IllegalArgumentException("RelationTable update e*f != lt(p)"); 303 } 304 List<Integer> key = makeKey(e, f); 305 ExpVectorPair evp = new ExpVectorPair(e, f); 306 if (key.size() != 2) { 307 System.out.println("key = " + key + ", evp = " + evp); 308 } 309 List part = table.get(key); 310 if (part == null) { // initialization only 311 part = new LinkedList(); 312 part.add(evp); 313 part.add(p); 314 table.put(key, part); 315 return; 316 } 317 Object skip; 318 int index = -1; 319 synchronized (part) { // with lookup() 320 for (ListIterator it = part.listIterator(); it.hasNext();) { 321 ExpVectorPair look = (ExpVectorPair) it.next(); 322 skip = it.next(); // skip poly 323 if (look.isMultiple(evp)) { 324 index = it.nextIndex(); 325 // last index of or first index of: break 326 } 327 } 328 if (index < 0) { 329 index = 0; 330 } 331 part.add(index, evp); 332 part.add(index + 1, p); 333 } 334 // table.put( key, part ); // required?? 335 } 336 337 338 /** 339 * Update or initialize RelationTable with new relation. relation is e * f = 340 * p. 341 * @param E first term polynomial. 342 * @param F second term polynomial. 343 * @param p solvable product polynomial. 344 */ 345 public void update(GenPolynomial<C> E, GenPolynomial<C> F, GenSolvablePolynomial<C> p) { 346 if (E.isZERO() || F.isZERO()) { 347 throw new IllegalArgumentException("polynomials may not be zero: " + E + ", " + F); 348 } 349 C ce = E.leadingBaseCoefficient(); 350 C cf = F.leadingBaseCoefficient(); 351 if (!ce.isONE() || !cf.isONE()) { 352 throw new IllegalArgumentException("lbcf of polynomials must be one: " + ce + ", " + cf); 353 } 354 ExpVector e = E.leadingExpVector(); 355 ExpVector f = F.leadingExpVector(); 356 update(e, f, p); 357 } 358 359 360 /** 361 * Update or initialize RelationTable with new relation. relation is e * f = 362 * p. 363 * @param E first term polynomial. 364 * @param F second term polynomial. 365 * @param p product polynomial. 366 */ 367 public void update(GenPolynomial<C> E, GenPolynomial<C> F, GenPolynomial<C> p) { 368 if (p.isZERO()) { 369 throw new IllegalArgumentException("polynomial may not be zero: " + p); 370 } 371 if (p.isONE()) { 372 throw new IllegalArgumentException("product of polynomials may not be one: " + p); 373 } 374 GenSolvablePolynomial<C> sp = new GenSolvablePolynomial<C>(ring, p.val); 375 update(E, F, sp); 376 } 377 378 379 /** 380 * Lookup RelationTable for existing relation. Find p with e * f = p. If no 381 * relation for e * f is contained in the table then return the symmetric 382 * product p = 1 e f. 383 * @param e first term. 384 * @param f second term. 385 * @return t table relation container, contains e' and f' with e f = e' 386 * lt(p) f'. 387 */ 388 @SuppressWarnings("unchecked") 389 public TableRelation<C> lookup(ExpVector e, ExpVector f) { 390 List<Integer> key = makeKey(e, f); 391 List part = table.get(key); 392 if (part == null) { // symmetric product 393 ExpVector ef = e.sum(f); 394 GenSolvablePolynomial<C> p = ring.getONE().multiply(ef); 395 return new TableRelation<C>(null, null, p); 396 } 397 ExpVectorPair evp = new ExpVectorPair(e, f); 398 ExpVector ep = null; 399 ExpVector fp = null; 400 ExpVectorPair look = null; 401 GenSolvablePolynomial<C> p = null; 402 synchronized (part) { // with update() 403 for (Iterator it = part.iterator(); it.hasNext();) { 404 look = (ExpVectorPair) it.next(); 405 p = (GenSolvablePolynomial<C>) it.next(); 406 if (evp.isMultiple(look)) { 407 ep = e.subtract(look.getFirst()); 408 fp = f.subtract(look.getSecond()); 409 if (ep.isZERO()) { 410 ep = null; 411 } 412 if (fp.isZERO()) { 413 fp = null; 414 } 415 if (debug) { 416 if (p != null && p.ring.vars != null) { 417 logger.info("found relation = " + e.toString(p.ring.vars) + " .*. " 418 + f.toString(p.ring.vars) + " = " + p); 419 } else { 420 logger.info("found relation = " + e + " .*. " + f + " = " + p); 421 } 422 } 423 return new TableRelation<C>(ep, fp, p); 424 } 425 } 426 } 427 // unreacheable code! 428 throw new RuntimeException("no entry found in relation table for " + evp); 429 } 430 431 432 /** 433 * Construct a key for (e,f). 434 * @param e first term. 435 * @param f second term. 436 * @return k key for (e,f). 437 */ 438 protected List<Integer> makeKey(ExpVector e, ExpVector f) { 439 int[] de = e.dependencyOnVariables(); 440 int[] df = f.dependencyOnVariables(); 441 List<Integer> key = new ArrayList<Integer>(de.length + df.length); 442 for (int i = 0; i < de.length; i++) { 443 key.add(Integer.valueOf(de[i])); 444 } 445 for (int i = 0; i < df.length; i++) { 446 key.add(Integer.valueOf(df[i])); 447 } 448 return key; 449 } 450 451 452 /** 453 * Size of the table. 454 * @return n number of non-commutative relations. 455 */ 456 public int size() { 457 int s = 0; 458 if (table == null) { 459 return s; 460 } 461 for (Iterator<List> it = table.values().iterator(); it.hasNext();) { 462 List list = it.next(); 463 s += list.size() / 2; 464 } 465 return s; 466 } 467 468 469 /** 470 * Extend variables. Used e.g. in module embedding. Extend all ExpVectors 471 * and polynomials of the given relation table by i elements and put the 472 * relations into this table, i.e. this should be empty. 473 * @param tab a relation table to be extended and inserted into this. 474 */ 475 @SuppressWarnings("unchecked") 476 public void extend(RelationTable<C> tab) { 477 if (tab.table.size() == 0) { 478 return; 479 } 480 // assert this.size() == 0 481 int i = ring.nvar - tab.ring.nvar; 482 int j = 0; 483 long k = 0l; 484 List val; 485 for (List<Integer> key : tab.table.keySet()) { 486 val = tab.table.get(key); 487 for (Iterator jt = val.iterator(); jt.hasNext();) { 488 ExpVectorPair ep = (ExpVectorPair) jt.next(); 489 ExpVector e = ep.getFirst(); 490 ExpVector f = ep.getSecond(); 491 GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next(); 492 ExpVector ex = e.extend(i, j, k); 493 ExpVector fx = f.extend(i, j, k); 494 GenSolvablePolynomial<C> px = (GenSolvablePolynomial<C>) p.extend(ring, j, k); 495 this.update(ex, fx, px); 496 } 497 } 498 return; 499 } 500 501 502 /** 503 * Contract variables. Used e.g. in module embedding. Contract all 504 * ExpVectors and polynomials of the given relation table by i elements and 505 * put the relations into this table, i.e. this should be empty. 506 * @param tab a relation table to be contracted and inserted into this. 507 */ 508 @SuppressWarnings("unchecked") 509 public void contract(RelationTable<C> tab) { 510 if (tab.table.size() == 0) { 511 return; 512 } 513 // assert this.size() == 0 514 int i = tab.ring.nvar - ring.nvar; 515 List val; 516 for (List<Integer> key : tab.table.keySet()) { 517 val = tab.table.get(key); 518 for (Iterator jt = val.iterator(); jt.hasNext();) { 519 ExpVectorPair ep = (ExpVectorPair) jt.next(); 520 ExpVector e = ep.getFirst(); 521 ExpVector f = ep.getSecond(); 522 GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next(); 523 ExpVector ec = e.contract(i, e.length() - i); 524 ExpVector fc = f.contract(i, f.length() - i); 525 if (ec.isZERO() || fc.isZERO()) { 526 continue; 527 } 528 Map<ExpVector, GenPolynomial<C>> mc = p.contract(ring); 529 if (mc.size() != 1) { 530 continue; 531 } 532 GenSolvablePolynomial<C> pc = null; 533 for (GenPolynomial<C> x : mc.values()) { 534 if (pc != null) { 535 // should not happen 536 logger.info("e = " + e + ", f = " + f + ", ec = " + ec + ", fc = " + fc); 537 logger.info("p = " + p + ", mc = " + mc); 538 throw new RuntimeException("Map.size() != 1: " + mc.size()); 539 } 540 pc = (GenSolvablePolynomial<C>) x; 541 } 542 this.update(ec, fc, pc); 543 } 544 } 545 return; 546 } 547 548 549 /** 550 * Reverse variables and relations. Used e.g. in opposite rings. Reverse all 551 * ExpVectors and polynomials of the given relation table and put the 552 * modified relations into this table, i.e. this should be empty. 553 * @param tab a relation table to be reverted and inserted into this. 554 */ 555 @SuppressWarnings("unchecked") 556 public void reverse(RelationTable<C> tab) { 557 if (tab.table.size() == 0) { 558 return; 559 } 560 // assert this.size() == 0 561 if (table.size() != 0) { 562 logger.error("reverse table not empty"); 563 } 564 int k = -1; 565 if (ring.tord.getEvord2() != 0 && ring.partial) { 566 k = ring.tord.getSplit(); 567 } 568 logger.debug("k split = " + k); 569 //System.out.println("k split = " + k ); 570 for (List<Integer> key : tab.table.keySet()) { 571 List val = tab.table.get(key); 572 for (Iterator jt = val.iterator(); jt.hasNext();) { 573 ExpVectorPair ep = (ExpVectorPair) jt.next(); 574 ExpVector e = ep.getFirst(); 575 ExpVector f = ep.getSecond(); 576 GenSolvablePolynomial<C> p = (GenSolvablePolynomial<C>) jt.next(); 577 //logger.info("e pre reverse = " + e ); 578 //logger.info("f pre reverse = " + f ); 579 //logger.info("p pre reverse = " + p ); 580 ExpVector ex; 581 ExpVector fx; 582 GenSolvablePolynomial<C> px; 583 boolean change = true; // if relevant vars reversed 584 if (k >= 0) { 585 ex = e.reverse(k); 586 fx = f.reverse(k); 587 int[] ed = ex.dependencyOnVariables(); // = e 588 if (ed.length == 0 || ed[0] >= k) { // k >= 0 589 change = false; 590 } 591 int[] fd = fx.dependencyOnVariables(); // = f 592 if (fd.length == 0 || fd[0] >= k) { // k >= 0 593 change = false; 594 } 595 } else { 596 ex = e.reverse(); 597 fx = f.reverse(); 598 } 599 px = (GenSolvablePolynomial<C>) p.reverse(ring); 600 //System.out.println("change = " + change ); 601 if (!change) { 602 this.update(e, f, px); // same order 603 } else { 604 this.update(fx, ex, px); // opposite order 605 //this.update( ex, fx, px ); // same order 606 } 607 } 608 } 609 return; 610 } 611 612} 613 614 615/** 616 * TableRelation container for storage and printing in RelationTable. 617 * @author Heinz Kredel 618 */ 619 620class TableRelation<C extends RingElem<C>> implements Serializable { 621 622 623 /** 624 * First ExpVector of the data structure. 625 */ 626 public final ExpVector e; 627 628 629 /** 630 * Second ExpVector of the data structure. 631 */ 632 public final ExpVector f; 633 634 635 /** 636 * GenSolvablePolynomial of the data structure. 637 */ 638 public final GenSolvablePolynomial<C> p; 639 640 641 /** 642 * Constructor to setup the data structure. 643 * @param e first term. 644 * @param f second term. 645 * @param p product polynomial. 646 */ 647 public TableRelation(ExpVector e, ExpVector f, GenSolvablePolynomial<C> p) { 648 this.e = e; 649 this.f = f; 650 this.p = p; 651 } 652 653 654 /** 655 * Get the String representation. 656 * @see java.lang.Object#toString() 657 */ 658 @Override 659 public String toString() { 660 StringBuffer s = new StringBuffer("TableRelation["); 661 s.append("" + e); 662 s.append(" | "); 663 s.append("" + f); 664 s.append(" = "); 665 s.append("" + p); 666 s.append("]"); 667 return s.toString(); 668 } 669 670}