All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jp.kobe_u.sugar.converter.ComparisonConverter Maven / Gradle / Ivy

Go to download

This is a JSR331 interface for the open source Java constraint programming library "Sugar" v. 2.1.3

The newest version!
package jp.kobe_u.sugar.converter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import jp.kobe_u.sugar.SugarException;
import jp.kobe_u.sugar.csp.CSP;
import jp.kobe_u.sugar.csp.Clause;
import jp.kobe_u.sugar.csp.IntegerDomain;
import jp.kobe_u.sugar.csp.IntegerVariable;
import jp.kobe_u.sugar.csp.LinearEqLiteral;
import jp.kobe_u.sugar.csp.LinearGeLiteral;
import jp.kobe_u.sugar.csp.LinearLeLiteral;
import jp.kobe_u.sugar.csp.LinearNeLiteral;
import jp.kobe_u.sugar.csp.LinearSum;
import jp.kobe_u.sugar.csp.Literal;
import jp.kobe_u.sugar.csp.ProductLiteral;
import jp.kobe_u.sugar.expression.Atom;
import jp.kobe_u.sugar.expression.Expression;
import jp.kobe_u.sugar.expression.Sequence;
import jp.kobe_u.sugar.hook.ConverterHook;

/**
 * @author Naoyuki Tamura ([email protected])
 *
 */
public class ComparisonConverter {

    private Converter converter;
    private CSP csp;

    public ComparisonConverter(Converter converter) {
        this.converter = converter;
        csp = converter.csp;
    }

    private void convertConstraint(Expression x) throws SugarException {
        converter.convertConstraint(x);
    }

    private IntegerVariable newIntegerVariable(IntegerDomain d, Expression x)
    throws SugarException {
        return converter.newIntegerVariable(d, x);
    }
    
    private IntegerVariable toIntegerVariable(LinearSum e, Expression x)
    throws SugarException {
        IntegerVariable v;
        if (e.isIntegerVariable()) {
            v = e.getCoef().firstKey();
        } else {
            v = newIntegerVariable(e.getDomain(), x);
            Expression eq = Expression.create(v.getName()).eq(x);
            eq.setComment(v.getName() + " == " + x);
            convertConstraint(eq);
            converter.addEquivalence(v, x);
        }
        return v;
    }
    
    private LinearSum convertInteger(Atom x) throws SugarException {
        return new LinearSum(x.integerValue());
    }
    
    private LinearSum convertString(Atom x) throws SugarException {
        String s = x.stringValue();
        if (csp.getIntegerVariable(s) == null) {
            converter.syntaxError(x);
        }
        IntegerVariable v = csp.getIntegerVariable(s);
        return new LinearSum(v);
    }
    
    private LinearSum convertADD(Sequence seq) throws SugarException {
        LinearSum e = new LinearSum(0);
        for (int i = 1; i < seq.length(); i++) {
            LinearSum ei = convertFormula(seq.get(i));
            e.add(ei);
        }
        return e;
    }
    
    private LinearSum convertSUB(Sequence seq) throws SugarException {
        LinearSum e = null;
        if (seq.length() == 1) {
            converter.syntaxError(seq);
        } else if (seq.length() == 2) {
            e = convertFormula(seq.get(1));
            e.multiply(-1);
        } else {
            e = convertFormula(seq.get(1));
            for (int i = 2; i < seq.length(); i++) {
                LinearSum ei = convertFormula(seq.get(i));
                e.subtract(ei);
            }
        }
        return e;
    }
    
    private LinearSum convertABS(Sequence seq) throws SugarException {
        converter.checkArity(seq, 1);
        Expression x1 = seq.get(1);
        LinearSum e1 = convertFormula(x1);
        IntegerDomain d1 = e1.getDomain();
        if (d1.getLowerBound() >= 0) {
            return e1;
        } else if (d1.getUpperBound() <= 0) {
            e1.multiply(-1);
            return e1;
        }
        IntegerDomain d = d1.abs();
        IntegerVariable v = newIntegerVariable(d, seq);
        Expression x = Expression.create(v.getName());
        Expression eq =
            (x.ge(x1))
            .and(x.ge(x1.neg()))
            .and((x.le(x1)).or(x.le(x1.neg())));
        eq.setComment(v.getName() + " == " + seq);
        convertConstraint(eq);
        converter.addEquivalence(v, seq);
        return new LinearSum(v);
    }
    
    private LinearSum convertMUL(Sequence seq) throws SugarException {
        converter.checkArity(seq, 2);
        Expression x1 = seq.get(1);
        Expression x2 = seq.get(2);
        LinearSum e1 = convertFormula(x1);
        LinearSum e2 = convertFormula(x2);
        IntegerDomain d1 = e1.getDomain();
        IntegerDomain d2 = e2.getDomain();
        if (d1.size() == 1) {
            e2.multiply(d1.getLowerBound());
            return e2;
        } else if (d2.size() == 1) {
            e1.multiply(d2.getLowerBound());
            return e1;
        } else if (d1.size() <= d2.size()) {
            Expression x = null;
            Iterator iter = d1.values();
            while (iter.hasNext()) {
                int a1 = iter.next();
                if (x == null) {
                    x = x2.mul(a1);
                } else {
                    x = (x1.ge(a1)).ifThenElse(x2.mul(a1), x);
                }
            }
            return convertIF((Sequence)x);
        } else {
            return convertMUL((Sequence)x2.mul(x1));
        }
    }
    
    private LinearSum convertDIV(Sequence seq) throws SugarException {
        converter.checkArity(seq, 2);
        Expression x1 = seq.get(1);
        Expression x2 = seq.get(2);
        LinearSum e1 = convertFormula(x1);
        LinearSum e2 = convertFormula(x2);
        IntegerDomain d1 = e1.getDomain();
        IntegerDomain d2 = e2.getDomain();
        IntegerDomain qd = d1.div(d2);
        IntegerDomain rd = d1.mod(d2);
        IntegerVariable qv = newIntegerVariable(qd, seq);
        IntegerVariable rv = newIntegerVariable(rd, x1.mod(x2));
        Expression q = Expression.create(qv.getName());
        Expression r = Expression.create(rv.getName());
        Expression px = x2.mul(q);
        if (d2.size() == 1) {
            int value2 = d2.getLowerBound();
            if (value2 == 1) {
                return e1;
            } else if (value2 == -1) {
                e1.multiply(-1);
                return e1;
            }
            // TODO
            if (value2 <= 0) {
                throw new SugarException("Unsupported " + seq);
            }
            Expression eq =
                (x1.eq(px.add(r)))
                .and((r.ge(Expression.ZERO)).and((x2.abs()).gt(r)));
            eq.setComment(qv.getName() + " == " + seq);
            convertConstraint(eq);
            converter.addEquivalence(qv, seq);
            return new LinearSum(qv);
        }
        // TODO
        if (true) {
            throw new SugarException("Unsupported " + seq);
        }
        IntegerVariable v2 = toIntegerVariable(e2, x2);
        IntegerDomain pd = d1.sub(rd);
        IntegerVariable pv = newIntegerVariable(pd, px);
        Clause clause = new Clause(new ProductLiteral(pv, qv, v2));
        clause.setComment(pv.getName() + " == " + px);
        csp.add(clause);
        Expression p = Expression.create(pv.getName());
        Expression eq =
            (x1.eq(p.add(r)))
            .and((r.ge(Expression.ZERO)).and((x2.abs()).gt(r)));
        eq.setComment(qv.getName() + " == " + seq);
        convertConstraint(eq);
        converter.addEquivalence(qv, seq);
        return new LinearSum(qv);
    }
    
    private LinearSum convertMOD(Sequence seq) throws SugarException {
        converter.checkArity(seq, 2);
        Expression x1 = seq.get(1);
        Expression x2 = seq.get(2);
        LinearSum e1 = convertFormula(x1);
        LinearSum e2 = convertFormula(x2);
        IntegerDomain d1 = e1.getDomain();
        IntegerDomain d2 = e2.getDomain();
        IntegerDomain qd = d1.div(d2);
        IntegerDomain rd = d1.mod(d2);
        IntegerVariable qv = newIntegerVariable(qd, seq);
        IntegerVariable rv = newIntegerVariable(rd, x1.mod(x2));
        Expression q = Expression.create(qv.getName());
        Expression r = Expression.create(rv.getName());
        Expression px = x2.mul(q);
        if (d2.size() == 1) {
            int value2 = d2.getLowerBound();
            // TODO
            if (value2 <= 0) {
                throw new SugarException("Unsupported " + seq);
            }
            Expression eq =
                (x1.eq(px.add(r)))
                .and((r.ge(Expression.ZERO)).and((x2.abs()).gt(r)));
            eq.setComment(qv.getName() + " == " + seq);
            convertConstraint(eq);
            converter.addEquivalence(rv, seq);
            return new LinearSum(rv);
        }
        // TODO
        if (true) {
            throw new SugarException("Unsupported " + seq);
        }
        IntegerVariable v2 = toIntegerVariable(e2, x2);
        IntegerDomain pd = d1.sub(rd);
        IntegerVariable pv = newIntegerVariable(pd, px);
        Clause clause = new Clause(new ProductLiteral(pv, qv, v2));
        clause.setComment(pv.getName() + " == " + px);
        csp.add(clause);
        Expression p = Expression.create(pv.getName());
        Expression eq =
            (x1.eq(p.add(r)))
            .and((r.ge(Expression.ZERO)).and((x2.abs()).gt(r)));
        eq.setComment(rv.getName() + " == " + seq);
        convertConstraint(eq);
        converter.addEquivalence(rv, seq);
        return new LinearSum(rv);
    }
    
    private LinearSum convertPOW(Sequence seq) throws SugarException {
        // TODO pow
        throw new SugarException("Unsupported " + seq);
        // return null;
    }
    
    private LinearSum convertMIN(Sequence seq) throws SugarException {
        converter.checkArity(seq, 2);
        Expression x1 = seq.get(1);
        Expression x2 = seq.get(2);
        LinearSum e1 = convertFormula(x1);
        LinearSum e2 = convertFormula(x2);
        IntegerDomain d1 = e1.getDomain();
        IntegerDomain d2 = e2.getDomain();
        if (d1.getUpperBound() <= d2.getLowerBound()) {
            return e1;
        } else if (d2.getUpperBound() <= d1.getLowerBound()) {
            return e2;
        }
        IntegerDomain d = d1.min(d2);
        IntegerVariable v = newIntegerVariable(d, seq);
        Expression x = Expression.create(v.getName());
        Expression eq =
            (x.le(x1))
            .and(x.le(x2))
            .and((x.ge(x1)).or(x.ge(x2)));
        eq.setComment(v.getName() + " == " + seq);
        convertConstraint(eq);
        converter.addEquivalence(v, seq);
        return new LinearSum(v);
    }
    
    private LinearSum convertMAX(Sequence seq) throws SugarException {
        converter.checkArity(seq, 2);
        Expression x1 = seq.get(1);
        Expression x2 = seq.get(2);
        LinearSum e1 = convertFormula(x1);
        LinearSum e2 = convertFormula(x2);
        IntegerDomain d1 = e1.getDomain();
        IntegerDomain d2 = e2.getDomain();
        if (d1.getUpperBound() <= d2.getLowerBound()) {
            return e2;
        } else if (d2.getUpperBound() <= d1.getLowerBound()) {
            return e1;
        }
        IntegerDomain d = d1.max(d2);
        IntegerVariable v = newIntegerVariable(d, seq);
        Expression x = Expression.create(v.getName());
        Expression eq =
            (x.ge(x1))
            .and(x.ge(x2))
            .and((x.le(x1)).or(x.le(x2)));
        eq.setComment(v.getName() + " == " + seq);
        convertConstraint(eq);
        converter.addEquivalence(v, seq);
        return new LinearSum(v);
    }
    
    private LinearSum convertIF(Sequence seq) throws SugarException {
        converter.checkArity(seq, 3);
        Expression x1 = seq.get(1);
        Expression x2 = seq.get(2);
        Expression x3 = seq.get(3);
        LinearSum e2 = convertFormula(x2);
        LinearSum e3 = convertFormula(x3);
        IntegerDomain d2 = e2.getDomain();
        IntegerDomain d3 = e3.getDomain();
        IntegerDomain d = d2.cup(d3);
        IntegerVariable v = newIntegerVariable(d, seq);
        Expression x = Expression.create(v.getName());
        Expression eq =
            ((x1.not()).or(x.eq(x2)))
            .and(x1.or(x.eq(x3)));
        eq.setComment(v.getName() + " == " + seq);
        convertConstraint(eq);
        converter.addEquivalence(v, seq);
        return new LinearSum(v);
    }
    
    protected LinearSum convertFormula(Expression x) throws SugarException {
        if (Converter.hooks != null) {
            Expression x1;
            do {
                x1 = x;
                for (ConverterHook hook : Converter.hooks) {
                    x = hook.convertFunction(converter, x);
                }
            } while (! x1.equals(x));
        }
        LinearSum e = null;
        IntegerVariable v = converter.getEquivalence(x);
        if (v != null) {
            e = new LinearSum(v);
        } else if (x.isAtom()) {
            if (x.isInteger()) {
                e = convertInteger((Atom)x);
            } else {
                e = convertString((Atom)x);
            }
        } else {
            if (x.isSequence(Expression.ADD)) {
                e = convertADD((Sequence)x);
            } else if (x.isSequence(Expression.NEG) || x.isSequence(Expression.SUB)) {
                e = convertSUB((Sequence)x);
            } else if (x.isSequence(Expression.ABS)) {
                e = convertABS((Sequence)x);
            } else if (x.isSequence(Expression.MUL)) {
                e = convertMUL((Sequence)x);
            } else if (x.isSequence(Expression.DIV)) {
                e = convertDIV((Sequence)x);
            } else if (x.isSequence(Expression.MOD)) {
                e = convertMOD((Sequence)x);
            } else if (x.isSequence(Expression.POW)) {
                e = convertPOW((Sequence)x);
            } else if (x.isSequence(Expression.MIN)) {
                e = convertMIN((Sequence)x);
            } else if (x.isSequence(Expression.MAX)) {
                e = convertMAX((Sequence)x);
            } else if (x.isSequence(Expression.IF)) {
                e = convertIF((Sequence)x);
            } else {
                converter.syntaxError(x);
            }
        }
        return e;
    }

    private LinearSum simplifyLinearLe(LinearSum e) throws SugarException {
        if (e.size() <= 3) {
            return e;
        }
        /*
        if (! e.isDomainLargerThan(MAX_LINEARSUM_SIZE)) {
            return e;
        }
        */
        IntegerVariable var = e.getLargestDomainVariable();
        if (! e.isDomainLargerThanExcept(Converter.MAX_LINEARSUM_SIZE, var)) {
            return e;
        }
        IntegerVariable[] vs = e.getVariablesSorted();
        /*
        LinearLe e0 = new LinearLe(e.getB());
        int domainSize = 1;
        int i = 0;
        for (i = 0; i < vs.length - 2 && domainSize <= MAX_LINEARSUM_SIZE; i++) {
            e0.setA(e.getA(vs[i]), vs[i]);
            domainSize *= vs[i].getDomain().size();
        }
        LinearLe e1 = new LinearLe(0);
        for (int j = i; j < vs.length; j++) {
            e1.setA(e.getA(vs[j]), vs[j]);
        }
        int factor = e1.factor();
        if (factor > 1) {
            e1.divide(factor);
        }
        IntegerVariable v = new IntegerVariable(e1.getDomain());
        v.setComment(v.getName() + " : " + e1);
        csp.add(v);
        Expression x = Expression.create(v.getName());
        Expression ex = e1.toExpression();
        Expression eq = x.eq(ex);
        extra.add(eq);
        e0.setA(factor, v);
        return e0;
        */
        LinearSum e1 = new LinearSum(0);
        for (int i = 2; i < vs.length; i++) {
            e1.setA(e.getA(vs[i]), vs[i]);
        }
        int factor = e1.factor();
        if (factor > 1) {
            e1.divide(factor);
        }
        // v == (a[2]*vs[2] + a[3]*vs[3] + ... + a[n]*vs[n]) / factor
        IntegerVariable v = new IntegerVariable(e1.getDomain());
        v.setComment(v.getName() + " : " + e1);
        csp.add(v);
        Expression x = Expression.create(v.getName());
        Expression ex = e1.toExpression();
        Expression eq = x.eq(ex);
        // eq.setComment(v.getName() + " == " + e1);
        // XXX 
        // convertConstraint(eq);
        converter.addExtra(eq);
        // e0 = b + a[0]*vs[0] + a[1]*vs[1] + factor*v
        LinearSum e0 = new LinearSum(e.getB());
        e0.setA(e.getA(vs[0]), vs[0]);
        e0.setA(e.getA(vs[1]), vs[1]);
        e0.setA(factor, v);
//      System.out.println(e + " ==> " + e0 + " with " + eq);
        return e0;
    }
    
    private LinearSum simplifyLinearExpression(LinearSum e, String cmp, boolean first) throws SugarException {
        if (Converter.ESTIMATE_SATSIZE) {
            // seems bad in general
            if (e.satSizeLE(Converter.MAX_LINEARSUM_SIZE)) {
                return e;
            }
        } else {
            if (e.size() <= 1 || ! e.isDomainLargerThan(Converter.MAX_LINEARSUM_SIZE)) {
            // if (e.size() <= 1 || ! e.isDomainLargerThanExcept(MAX_LINEARSUM_SIZE)) {
                return e;
            }
        }
        int b = e.getB();
        LinearSum[] es = e.split(first ? 3 : Converter.SPLITS);
        e = new LinearSum(b);
        for (int i = 0; i < es.length; i++) {
            LinearSum ei = es[i];
            int factor = ei.factor();
            if (factor > 1) {
                ei.divide(factor);
            }
            // Recursive call is not necessary, but it works better
            ei = simplifyLinearExpression(ei, "eq", false);
            // System.out.println(es[i] + " ==> " + ei);
            if (ei.size() > 1) {
                IntegerVariable v = new IntegerVariable(ei.getDomain());
                v.setComment(v.getName() + " : " + ei);
                csp.add(v);
                Expression x = Expression.create(v.getName());
                Expression ex = ei.toExpression();
                Expression eq;
                if (! Converter.USE_EQ && cmp.equals("ge")) {
                    eq = x.le(ex);
                    eq.setComment(v.getName() + " <= " + ex);
                } else if (! Converter.USE_EQ && cmp.equals("le")) {
                    eq = x.ge(ex);
                    eq.setComment(v.getName() + " >= " + ex);
                } else {
                    eq = x.eq(ex);
                    eq.setComment(v.getName() + " == " + ex);
                }
                convertConstraint(eq);
                ei = new LinearSum(v);
            }
            if (factor > 1) {
                ei.multiply(factor);
            }
            e.add(ei);
        }
        return e;
    }
    
    private LinearSum reduceLinearExpression(LinearSum e, String cmp) throws SugarException {
        if (e.size() <= Converter.MAX_ARITY)
            return e;
        int b = e.getB();
        LinearSum[] es = e.split(Converter.MAX_ARITY);
        e = new LinearSum(b);
        for (int i = 0; i < es.length; i++) {
            LinearSum ei = es[i];
            int factor = ei.factor();
            if (factor > 1) {
                ei.divide(factor);
            }
            ei = reduceLinearExpression(ei, "eq");
            // System.out.println(es[i] + " ==> " + ei);
            if (ei.size() > 1) {
                IntegerVariable v = new IntegerVariable(ei.getDomain());
                v.setComment(v.getName() + " : " + ei);
                csp.add(v);
                Expression x = Expression.create(v.getName());
                Expression ex = ei.toExpression();
                Expression eq;
                if (! Converter.USE_EQ && cmp.equals("ge")) {
                    eq = x.le(ex);
                    eq.setComment(v.getName() + " <= " + ex);
                } else if (! Converter.USE_EQ && cmp.equals("le")) {
                    eq = x.ge(ex);
                    eq.setComment(v.getName() + " >= " + ex);
                } else {
                    eq = x.eq(ex);
                    eq.setComment(v.getName() + " == " + ex);
                }
                convertConstraint(eq);
                ei = new LinearSum(v);
            }
            if (factor > 1) {
                ei.multiply(factor);
            }
            e.add(ei);
        }
        return e;
    }


    private LinearSum reduceArity(LinearSum e, String cmp) throws SugarException {
        if (Converter.REDUCE_ARITY) {
            if (Converter.MAX_ARITY > 0) {
                e = reduceLinearExpression(e, cmp);
            } else if (e.size() > 3 && e.isDomainLargerThanExcept(Converter.MAX_LINEARSUM_SIZE)) {
                e = simplifyLinearExpression(e, cmp, true);
            }
        }
        return e;
    }

    /*
    protected List convertLE(Expression x) throws SugarException {
        LinearSum e = convertFormula(x);
        e.factorize();
        List clauses = new ArrayList();
        IntegerDomain d = e.getDomain();
        if (d.getUpperBound() <= 0) {
            return clauses;
        }
        if (d.getLowerBound() > 0) {
            clauses.add(new Clause());
            return clauses;
        }
        e = reduceArity(e);
        clauses.add(new Clause(new LinearLeLiteral(e)));
        return clauses;
    }
    */

    public List convertComp(Expression x, Expression y, String cmp) throws SugarException {
        LinearSum e = convertFormula(x.sub(y));
        e.factorize();
        e = reduceArity(e, cmp);
        Literal lit = null;
        if (cmp.equals("eq")) {
            lit = new LinearEqLiteral(e);
        } else if (cmp.equals("ne")) {
            lit = new LinearNeLiteral(e);
        } else if (cmp.equals("ge")) {
            lit = new LinearGeLiteral(e);
        } else if (cmp.equals("le")) {
            lit = new LinearLeLiteral(e);
        } else {
            throw new SugarException("Unknown comparison operator in convertComp: " + cmp);
        }
        List clauses = new ArrayList();
        if (lit.isValid()) {
        } else if (lit.isUnsatisfiable()) {
            clauses.add(new Clause());
        } else {
            clauses.add(new Clause(lit));
        }
        return clauses;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy