001/*
002 * $Id: GroebnerBaseWalk.java 5731 2017-02-11 11:38:15Z kredel $
003 */
004
005package edu.jas.gbufd;
006
007
008import java.util.ArrayList;
009import java.util.List;
010import java.util.Set;
011import java.util.SortedSet;
012
013import org.apache.log4j.Logger;
014
015import edu.jas.gb.GroebnerBaseAbstract;
016import edu.jas.gb.ReductionAbstract;
017import edu.jas.poly.ExpVector;
018import edu.jas.poly.GenPolynomial;
019import edu.jas.poly.GenPolynomialRing;
020import edu.jas.poly.Monomial;
021import edu.jas.poly.PolyUtil;
022import edu.jas.poly.PolynomialList;
023import edu.jas.poly.TermOrder;
024import edu.jas.poly.TermOrderByName;
025import edu.jas.structure.GcdRingElem;
026import edu.jas.structure.RingFactory;
027
028
029/**
030 * Groebner Base sequential Groebner Walk algorithm. Implements Groebner base
031 * computation via Groebner Walk algorithm. See "The generic Groebner walk" by
032 * Fukuda, Jensen, Lauritzen, Thomas, 2005.
033 *
034 * The start term order t1 can be set by a constructor. The target term order t2
035 * is taken from the input polynomials term order.
036 *
037 * @param <C> coefficient type
038 * @author Heinz Kredel
039 * 
040 * @see edu.jas.application.GBAlgorithmBuilder
041 * @see edu.jas.gbufd.GBFactory
042 */
043public class GroebnerBaseWalk<C extends GcdRingElem<C>> extends GroebnerBaseAbstract<C> {
044
045
046    private static final Logger logger = Logger.getLogger(GroebnerBaseWalk.class);
047
048
049    private static final boolean debug = logger.isDebugEnabled();
050
051
052    /**
053     * The backing GB algorithm implementation.
054     */
055    protected GroebnerBaseAbstract<C> sgb;
056
057
058    /**
059     * The start term order t1.
060     */
061    //protected TermOrder startTO = TermOrderByName.IGRLEX.blockOrder(2); 
062    protected TermOrder startTO = TermOrderByName.IGRLEX;
063
064
065    /**
066     * Print intermediate GB after this number of iterations.
067     */
068    int iterPrint = 100;
069
070
071    /**
072     * Constructor.
073     */
074    public GroebnerBaseWalk() {
075        super();
076        sgb = null;
077    }
078
079
080    /**
081     * Constructor.
082     * @param coFac coefficient ring of polynomial ring.
083     */
084    public GroebnerBaseWalk(RingFactory<C> coFac) {
085        this(GBFactory.<C> getImplementation(coFac));
086    }
087
088
089    /**
090     * Constructor.
091     * @param coFac coefficient ring of polynomial ring.
092     * @param t1 start term order.
093     */
094    public GroebnerBaseWalk(RingFactory<C> coFac, TermOrder t1) {
095        this(GBFactory.<C> getImplementation(coFac), t1);
096    }
097
098
099    /**
100     * Constructor.
101     * @param gb backing GB algorithm.
102     */
103    public GroebnerBaseWalk(GroebnerBaseAbstract<C> gb) {
104        super(gb.red, gb.strategy);
105        sgb = gb;
106    }
107
108
109    /**
110     * Constructor.
111     * @param gb backing GB algorithm.
112     * @param t1 start term order.
113     */
114    public GroebnerBaseWalk(GroebnerBaseAbstract<C> gb, TermOrder t1) {
115        this(gb);
116        startTO = t1;
117    }
118
119
120    /**
121     * Get the String representation with GB engine.
122     * @see java.lang.Object#toString()
123     */
124    @Override
125    public String toString() {
126        if (sgb == null) {
127            return "GroebnerBaseWalk(" + startTO.toScript() + ")";
128        }
129        return "GroebnerBaseWalk( " + sgb.toString() + ", " + startTO.toScript() + " )";
130    }
131
132
133    /**
134     * Groebner base using Groebner Walk algorithm.
135     * @param modv module variable number.
136     * @param F polynomial list in target term order.
137     * @return GB(F) a INVLEX / target term order Groebner base of F.
138     */
139    public List<GenPolynomial<C>> GB(int modv, List<GenPolynomial<C>> F) {
140        List<GenPolynomial<C>> G = normalizeZerosOnes(F);
141        G = PolyUtil.<C> monic(G);
142        if (G == null || G.size() == 0) {
143            return G;
144        }
145        GenPolynomialRing<C> pfac = G.get(0).ring;
146        if (!pfac.coFac.isField()) {
147            throw new IllegalArgumentException("coefficients not from a field: " + pfac.coFac);
148        }
149        if (G.size() <= 1) {
150            GenPolynomial<C> p = pfac.copy(G.get(0)); // change term order
151            G.clear();
152            G.add(p);
153            return G;
154        }
155        // compute in graded / start term order
156        TermOrder grord = startTO; //new TermOrder(TermOrder.IGRLEX);
157        GenPolynomialRing<C> gfac = new GenPolynomialRing<C>(pfac, grord);
158        if (debug) {
159            logger.info("gfac = " + gfac.toScript());
160        }
161        List<GenPolynomial<C>> Fp = gfac.copy(F);
162        logger.info("Term order: graded = " + grord + ", target = " + pfac.tord);
163
164        // compute graded / start term order Groebner base
165        if (sgb == null) {
166            sgb = GBFactory.<C> getImplementation(pfac.coFac, strategy);
167        }
168        List<GenPolynomial<C>> Gp = sgb.GB(modv, Fp);
169        logger.info("graded / start GB = " + Gp);
170        if (grord.equals(pfac.tord)) {
171            return Gp;
172        }
173        if (Gp.size() == 1) { // also dimension -1
174            GenPolynomial<C> p = pfac.copy(Gp.get(0)); // change term order
175            G.clear();
176            G.add(p);
177            return G;
178        }
179        // info on FGLM
180        int z = commonZeroTest(Gp);
181        if (z == 0) {
182            logger.info("ideal zero dimensional, can use also FGLM algorithm");
183        }
184        // compute target term order Groebner base via Groebner Walk
185        G = walkGroebnerToTarget(modv, Gp, pfac);
186        return G;
187    }
188
189
190    /**
191     * Converts Groebner bases w.r.t. total degree / start term order to
192     * Groebner base w.r.t to inverse lexicographical / target term order.
193     * @param modv module variable number.
194     * @param Gl Groebner base with respect to graded / start term order.
195     * @param ufac target polynomial ring and term order.
196     * @return Groebner base w.r.t inverse lexicographical / target term order
197     */
198    public List<GenPolynomial<C>> walkGroebnerToTarget(int modv, List<GenPolynomial<C>> Gl,
199                    GenPolynomialRing<C> ufac) {
200        if (Gl == null || Gl.size() == 0) {
201            throw new IllegalArgumentException("G may not be null or empty");
202        }
203        //Polynomial ring of input Groebner basis Gl
204        GenPolynomialRing<C> ring = Gl.get(0).ring;
205        if (debug) {
206            logger.info("ring = " + ring.toScript());
207        }
208        //Polynomial ring of newGB with INVLEX / target term order
209        //TermOrder lexi = ufac.tord; //new TermOrder(TermOrder.INVLEX);
210        logger.info("G walk from ev1 = " + ring.tord.toScript() + " to ev2 = " + ufac.tord.toScript());
211        List<GenPolynomial<C>> Giter = Gl;
212        // determine initial marks
213        List<ExpVector> marks = new ArrayList<ExpVector>();
214        List<Monomial<C>> M = new ArrayList<Monomial<C>>();
215        for (GenPolynomial<C> p : Giter) {
216            marks.add(p.leadingExpVector());
217            M.add(new Monomial<C>(p.leadingMonomial()));
218        }
219        logger.info("marks = " + marks);
220        List<Monomial<C>> Mp = new ArrayList<Monomial<C>>(M);
221        // weight matrix for target term order
222        long[][] ufweight = TermOrderByName.weightForOrder(ufac.tord, ring.nvar);
223        //TermOrder word = TermOrder.reverseWeight(ufweight);
224        TermOrder word = new TermOrder(ufweight);
225        logger.info("weight order: " + word);
226        ufweight = word.getWeight(); // because of weightDeg usage
227
228        // loop throught term orders
229        ExpVector w = null;
230        int iter = 0; // count #loops
231        boolean done = false;
232        while (!done) {
233            iter++;
234            // determine V and w
235            PolynomialList<C> Pl = new PolynomialList<C>(ring, Giter);
236            SortedSet<ExpVector> delta = Pl.deltaExpVectors(marks);
237            //logger.info("delta(marks) = " + delta);
238            logger.info("w_old = " + w);
239            ExpVector v = facetNormal(ring.tord, ufac.tord, delta, ring.evzero, ufweight);
240            logger.info("minimal v = " + v);
241            if (v == null) {
242                done = true;
243                break; // finished
244            }
245            w = v;
246            // determine facet polynomials for w
247            List<GenPolynomial<C>> iG = new ArrayList<GenPolynomial<C>>();
248            int i = 0;
249            for (GenPolynomial<C> f : Giter) {
250                ExpVector h = marks.get(i++);
251                GenPolynomial<C> ing = f.leadingFacetPolynomial(h, w);
252                if (debug) {
253                    logger.info("ing_g = [" + ing + "], lt(ing) = "
254                                    + ing.ring.toScript(ing.leadingExpVector()) + ", f = "
255                                    + f.ring.toScript(f.leadingExpVector()));
256                }
257                iG.add(ing);
258            }
259            List<GenPolynomial<C>> inOmega = ufac.copy(iG);
260            if (debug) {
261                logger.info("inOmega = " + inOmega);
262                logger.info("inOmega.ring: " + inOmega.get(0).ring.toScript());
263            }
264
265            // INVLEX / target term order GB of inOmega
266            List<GenPolynomial<C>> inOG = sgb.GB(modv, inOmega);
267            if (debug) {
268                logger.info("GB(inOmega) = " + inOG);
269            }
270            // remark polynomials 
271            marks.clear();
272            M.clear();
273            for (GenPolynomial<C> p : inOG) {
274                marks.add(p.leadingExpVector());
275                M.add(new Monomial<C>(p.leadingMonomial()));
276            }
277            logger.info("new marks/M = " + marks);
278            // lift and reduce
279            List<GenPolynomial<C>> G = liftReductas(M, Mp, Giter, inOG);
280            if (debug || (iter % iterPrint == 0)) {
281                logger.info("lift(" + iter + ") inOG, new GB: " + G);
282            }
283            if (G.size() == 1) { // will not happen
284                GenPolynomial<C> p = ufac.copy(G.get(0)); // change term order
285                G.clear();
286                G.add(p);
287                return G;
288            }
289            // iterate
290            Giter = G;
291            Mp.clear();
292            Mp.addAll(M);
293        }
294        return Giter;
295    }
296
297
298    /**
299     * Determine new facet normal.
300     * @param t1 old term order.
301     * @param t2 new term order.
302     * @param delta exponent vectors deltas.
303     * @param zero exponent vector.
304     * @param t2weight weight representation of t2.
305     * @return new facet normal v or null if no new facet normal exists.
306     */
307    public ExpVector facetNormal(TermOrder t1, TermOrder t2, Set<ExpVector> delta, ExpVector zero,
308                    long[][] t2weight) {
309        TermOrder.EVComparator ev1 = t1.getAscendComparator();
310        TermOrder.EVComparator ev2 = t2.getAscendComparator();
311        ExpVector v = null;
312        long d = 0; // = Long.MIN_VALUE;
313        for (ExpVector e : delta) {
314            if (ev1.compare(zero, e) >= 0 || ev2.compare(zero, e) <= 0) { //ring.evzero
315                //logger.info("skip e = " + e);
316                continue;
317            }
318            int s = 0;
319            long d2 = 0;
320            ExpVector vt = null;
321            ExpVector et = null;
322            if (v == null) {
323                v = e;
324                logger.info("init v = " + v);
325                continue;
326            }
327            for (long[] tau : t2weight) {
328                //logger.info("current tau = " + Arrays.toString(tau));
329                //compare
330                d = v.weightDeg(tau);
331                d2 = e.weightDeg(tau);
332                vt = v.scalarMultiply(d2);
333                et = e.scalarMultiply(d);
334                s = ev1.compare(et, vt);
335                if (s == 0) {
336                    continue; // next tau
337                } else if (s > 0) { // <, (> by example)
338                    v = e;
339                    break;
340                } else {
341                    break;
342                }
343            }
344            //logger.info("step s  = " + s + ", d = " + d + ", d2 = " + d2 + ", e = " + e); 
345            //   + ", v = " + v + ", et = " + et + ", vt = " + vt);
346        }
347        return v;
348    }
349
350
351    /**
352     * Cleanup and terminate ThreadPool.
353     */
354    @Override
355    public void terminate() {
356        if (sgb == null) {
357            return;
358        }
359        sgb.terminate();
360    }
361
362
363    /**
364     * Cancel ThreadPool.
365     */
366    @Override
367    public int cancel() {
368        if (sgb == null) {
369            return 0;
370        }
371        return sgb.cancel();
372    }
373
374
375    /**
376     * Lift leading polynomials to full Groebner base with respect to term
377     * order.
378     * @param M new leading monomial list of polynomials as marks.
379     * @param Mp old leading monomial list of polynomials as marks.
380     * @param G Groebner base polynomials for lift.
381     * @param A polynomial list of leading omega polynomials to lift.
382     * @return lift(A) a Groebner base wrt M of ideal(G).
383     */
384    public List<GenPolynomial<C>> liftReductas(List<Monomial<C>> M, List<Monomial<C>> Mp,
385                    List<GenPolynomial<C>> G, List<GenPolynomial<C>> A) {
386        if (G == null || M == null || Mp == null || G.isEmpty()) {
387            throw new IllegalArgumentException("null or empty lists not allowed");
388        }
389        if (A == null || A.isEmpty()) {
390            return A;
391        }
392        if (G.size() != Mp.size() || A.size() != M.size()) {
393            throw new IllegalArgumentException("equal sized lists required");
394        }
395        GenPolynomial<C> pol = G.get(0);
396        if (pol == null) {
397            throw new IllegalArgumentException("null polynomial not allowed");
398        }
399        // remove mark monomials
400        List<GenPolynomial<C>> Gp = new ArrayList<GenPolynomial<C>>(G.size());
401        int i = 0;
402        int len = G.size();
403        for (i = 0; i < len; i++) {
404            Monomial<C> mon = Mp.get(i);
405            GenPolynomial<C> s = G.get(i).subtract(mon);
406            Gp.add(s);
407        }
408        if (debug) {
409            logger.info("lifter GB: Gp  = " + Gp + ", Mp = " + Mp);
410        }
411        // compute f^Gp for f in A
412        //GenPolynomialRing<C> oring = pol.ring;
413        logger.info("liftReductas: G = " + G.size() + ", Mp = " + Mp.size()); // + ", old = " + oring.toScript());
414        List<GenPolynomial<C>> Ap = A; //oring.copy(A);
415        //logger.info("to lift Ap = " + Ap);
416        ReductionAbstract<C> sred = (ReductionAbstract<C>) sgb.red; //new ReductionSeq<C>();
417        List<GenPolynomial<C>> red = new ArrayList<GenPolynomial<C>>();
418        GenPolynomialRing<C> tring = A.get(0).ring;
419        len = Ap.size();
420        for (i = 0; i < len; i++) {
421            GenPolynomial<C> a = Ap.get(i);
422            GenPolynomial<C> r = sred.normalformMarked(Mp, Gp, a);
423            red.add(r);
424        }
425        logger.info("liftReductas: red(A) = " + red.size());
426        // combine f - f^Gp in tring
427        if (debug) {
428            logger.info("tring = " + tring.toScript()); // + ", M = " + M);
429        }
430        List<GenPolynomial<C>> nb = new ArrayList<GenPolynomial<C>>(red.size());
431        for (i = 0; i < A.size(); i++) {
432            GenPolynomial<C> x = tring.copy(A.get(i)); // Ap? A!
433            GenPolynomial<C> r = tring.copy(red.get(i));
434            GenPolynomial<C> s = x.subtract(r);
435            Monomial<C> m = M.get(i);
436            s.doAddTo(m.coefficient().negate(), m.exponent()); // remove marked term
437            if (!s.coefficient(m.exponent()).isZERO()) {
438                System.out.println("L-M: x = " + x + ", r = " + r);
439                throw new IllegalArgumentException("mark not removed: " + s + ", m = " + m);
440            }
441            nb.add(s);
442        }
443        if (debug) {
444            logger.info("lifted-M, nb = " + nb.size());
445        }
446        // minimal GB with preserved marks
447        //Collections.reverse(nb); // important for lex GB
448        len = nb.size();
449        i = 0;
450        while (i < len) {
451            GenPolynomial<C> a = nb.remove(0);
452            Monomial<C> m = M.remove(0); // in step with element from nb
453            if (debug) {
454                logger.info("doing " + a + ", lt = " + tring.toScript(m.exponent()));
455            }
456            //a = sgb.red.normalform(nb, a);
457            a = sred.normalformMarked(M, nb, a);
458            if (debug) {
459                logger.info("done, a = " + a + ", lt = " + tring.toScript(a.leadingExpVector()));
460            }
461            nb.add(a); // adds as last
462            M.add(m);
463            i++;
464        }
465        // re-add mark after minimal
466        for (i = 0; i < len; i++) {
467            GenPolynomial<C> a = nb.get(i);
468            Monomial<C> m = M.get(i);
469            a.doAddTo(m.coefficient(), m.exponent()); // re-add marked term
470            nb.set(i, a);
471        }
472        logger.info("liftReductas: nb = " + nb.size() + ", M = " + M.size());
473        //Collections.reverse(nb); // undo reverse
474        return nb;
475    }
476
477}