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