001package org.clafer.compiler;
002
003import java.util.ArrayList;
004import java.util.HashSet;
005import java.util.List;
006import java.util.Set;
007import org.clafer.ast.AstAbstractClafer;
008import org.clafer.ast.AstClafer;
009import org.clafer.ast.AstConcreteClafer;
010import org.clafer.ast.AstModel;
011import org.clafer.ast.AstRef;
012import org.clafer.ast.AstUtil;
013import org.clafer.ast.analysis.UnsatAnalyzer;
014import org.clafer.ast.compiler.AstCompiler;
015import org.clafer.ast.compiler.AstSolutionMap;
016import org.clafer.choco.constraint.Constraints;
017import org.clafer.collection.Either;
018import org.clafer.collection.Maybe;
019import org.clafer.common.Util;
020import org.clafer.graph.GraphUtil;
021import org.clafer.graph.KeyGraph;
022import org.clafer.graph.Vertex;
023import org.clafer.ir.IrIntConstant;
024import org.clafer.ir.IrIntVar;
025import org.clafer.ir.IrModule;
026import org.clafer.ir.IrSetConstant;
027import org.clafer.ir.IrSetVar;
028import org.clafer.ir.compiler.IrCompiler;
029import org.clafer.ir.compiler.IrSolutionMap;
030import org.clafer.objective.Objective;
031import org.clafer.scope.Scopable;
032import org.clafer.scope.Scope;
033import org.clafer.scope.ScopeBuilder;
034import solver.Solver;
035import solver.constraints.Constraint;
036import solver.constraints.ICF;
037import solver.search.loop.monitors.IMonitorSolution;
038import solver.search.strategy.IntStrategyFactory;
039import solver.search.strategy.SetStrategyFactory;
040import solver.search.strategy.strategy.AbstractStrategy;
041import solver.search.strategy.strategy.StrategiesSequencer;
042import solver.variables.IntVar;
043import solver.variables.SetVar;
044
045/**
046 * Compiles from AST -> Choco
047 *
048 * @author jimmy
049 */
050public class ClaferCompiler {
051
052    private ClaferCompiler() {
053    }
054
055    private static SetVar[] getSetVars(AstModel model, ClaferSolutionMap map) {
056        KeyGraph<AstClafer> dependency = new KeyGraph<>();
057        for (AstAbstractClafer abstractClafer : model.getAbstracts()) {
058            Vertex<AstClafer> node = dependency.getVertex(abstractClafer);
059            for (AstClafer sub : abstractClafer.getSubs()) {
060                node.addNeighbour(dependency.getVertex(sub));
061            }
062            if (abstractClafer.hasRef()) {
063                node.addNeighbour(dependency.getVertex(abstractClafer.getRef().getTargetType()));
064            }
065        }
066        for (AstConcreteClafer concreteClafer : AstUtil.getConcreteClafers(model)) {
067            Vertex<AstClafer> node = dependency.getVertex(concreteClafer);
068            if (concreteClafer.hasParent()) {
069                node.addNeighbour(dependency.getVertex(concreteClafer.getParent()));
070            }
071            if (concreteClafer.hasRef()) {
072                node.addNeighbour(dependency.getVertex(concreteClafer.getRef().getTargetType()));
073            }
074        }
075        List<SetVar> vars = new ArrayList<>();
076        for (Set<AstClafer> component : GraphUtil.computeStronglyConnectedComponents(dependency)) {
077            for (AstClafer clafer : component) {
078                if (clafer instanceof AstConcreteClafer) {
079                    for (IrSetVar setVar : map.getAstSolution().getSiblingVars(clafer)) {
080                        if (!(setVar instanceof IrSetConstant)) {
081                            Either<int[], SetVar> var = map.getIrSolution().getSetVar(setVar);
082                            if (var.isRight()) {
083                                vars.add(var.getRight());
084                            }
085                        }
086                    }
087                }
088            }
089        }
090        return vars.toArray(new SetVar[vars.size()]);
091    }
092
093    private static IntVar[] getIntVars(AstModel model, ClaferSolutionMap map) {
094        List<IntVar> vars = new ArrayList<>();
095        for (AstClafer clafer : AstUtil.getClafers(model)) {
096            if (clafer.hasRef()) {
097                for (IrIntVar intVar : map.getAstSolution().getRefVars(clafer.getRef())) {
098                    if (!(intVar instanceof IrIntConstant)) {
099                        Either<Integer, IntVar> var = map.getIrSolution().getIntVar(intVar);
100                        if (var.isRight()) {
101                            vars.add(var.getRight());
102                        }
103                    }
104                }
105            }
106        }
107        return vars.toArray(new IntVar[vars.size()]);
108    }
109
110    @SafeVarargs
111    private static void set(Solver solver, Maybe<? extends AbstractStrategy>... strategies) {
112        AbstractStrategy<?>[] strats = Maybe.filterJust(strategies);
113        if (strats.length == 0) {
114            solver.set(IntStrategyFactory.random(new IntVar[0], 0));
115        } else {
116            solver.set(new StrategiesSequencer(solver.getEnvironment(), strats));
117        }
118    }
119
120    private static Maybe<? extends AbstractStrategy<SetVar>> setStrategy(SetVar[] vars, ClaferOptions options) {
121        if (vars.length == 0) {
122            return Maybe.nothing();
123        }
124        if (options.isPreferSmallerInstances()) {
125            return Maybe.just(SetStrategyFactory.remove_first(vars));
126        }
127        return Maybe.just(SetStrategyFactory.force_first(vars));
128    }
129
130    private static Maybe<AbstractStrategy<IntVar>> firstFailInDomainMax(IntVar[] vars) {
131        if (vars.length == 0) {
132            return Maybe.nothing();
133        }
134        return Maybe.just(IntStrategyFactory.firstFail_InDomainMax(vars));
135    }
136
137    private static Maybe<AbstractStrategy<IntVar>> firstFailInDomainMin(IntVar[] vars) {
138        if (vars.length == 0) {
139            return Maybe.nothing();
140        }
141        return Maybe.just(IntStrategyFactory.firstFail_InDomainMin(vars));
142    }
143
144    public static ClaferSolver compile(AstModel in, Scopable scope) {
145        return compile(in, scope, ClaferOptions.Default);
146    }
147
148    public static ClaferSolver compile(AstModel in, Scopable scope, ClaferOptions options) {
149        Solver solver = new Solver();
150        IrModule module = new IrModule();
151
152        AstSolutionMap astSolution = AstCompiler.compile(in, scope.toScope(), module,
153                options.isFullSymmetryBreaking());
154        IrSolutionMap irSolution = IrCompiler.compile(module, solver, options.isFullOptimizations());
155        ClaferSolutionMap solution = new ClaferSolutionMap(astSolution, irSolution);
156
157        set(solver,
158                setStrategy(getSetVars(in, solution), options),
159                firstFailInDomainMin(getIntVars(in, solution)));
160        return new ClaferSolver(solver, solution);
161    }
162
163    public static ClaferOptimizer compile(AstModel in, Scopable scope, Objective objective) {
164        return compile(in, scope, objective, ClaferOptions.Default);
165    }
166
167    public static ClaferOptimizer compile(AstModel in, Scopable scope, Objective objective, ClaferOptions options) {
168        Solver solver = new Solver();
169        IrModule module = new IrModule();
170
171        AstSolutionMap astSolution = AstCompiler.compile(
172                in, scope.toScope(), objective, module,
173                options.isFullSymmetryBreaking());
174        IrSolutionMap irSolution = IrCompiler.compile(module, solver, options.isFullOptimizations());
175        ClaferSolutionMap solution = new ClaferSolutionMap(astSolution, irSolution);
176
177        Either<Integer, IntVar> objectiveVar = irSolution.getIntVar(astSolution.getObjectiveVar(objective));
178        IntVar[] objectiveVars = objectiveVar.isLeft() ? new IntVar[0] : new IntVar[]{objectiveVar.getRight()};
179
180        set(solver,
181                setStrategy(getSetVars(in, solution), options),
182                objective.isMaximize()
183                ? firstFailInDomainMax(objectiveVars)
184                : firstFailInDomainMin(objectiveVars),
185                firstFailInDomainMin(getIntVars(in, solution)));
186        return new ClaferOptimizer(solver, solution, objective.isMaximize(), objectiveVar);
187    }
188
189    public static ClaferUnsat compileUnsat(AstModel in, Scopable scope) {
190        return compileUnsat(in, scope.toScope(), ClaferOptions.Default);
191    }
192
193    public static ClaferUnsat compileUnsat(AstModel in, Scopable scope, ClaferOptions options) {
194        Solver solver = new Solver();
195        IrModule module = new IrModule();
196
197        AstSolutionMap astSolution = AstCompiler.compile(in, scope.toScope(), module,
198                Util.cons(new UnsatAnalyzer(), AstCompiler.DefaultAnalyzers),
199                options.isFullSymmetryBreaking());
200        IrSolutionMap irSolution = IrCompiler.compile(module, solver, options.isFullOptimizations());
201        ClaferSolutionMap solution = new ClaferSolutionMap(astSolution, irSolution);
202
203        set(solver,
204                firstFailInDomainMax(Either.filterRight(irSolution.getBoolVars(astSolution.getSoftVars()))),
205                setStrategy(getSetVars(in, solution), options),
206                firstFailInDomainMin(getIntVars(in, solution)));
207        return new ClaferUnsat(solver, solution);
208    }
209
210    public static ClaferSolver compilePartial(AstModel in, ScopeBuilder scope, AstConcreteClafer... concretize) {
211        return compilePartial(in, scope.toScope(), concretize);
212    }
213
214    public static ClaferSolver compilePartial(AstModel in, Scope scope, AstConcreteClafer... concretize) {
215        final Set<AstConcreteClafer> transitiveConcretize = new HashSet<>();
216        for (AstConcreteClafer clafer : concretize) {
217            concretize(clafer, transitiveConcretize);
218        }
219        final ClaferSolver solver = compile(in, scope);
220        final List<IntVar> intVars = new ArrayList<>();
221        final List<SetVar> setVars = new ArrayList<>();
222        for (AstConcreteClafer clafer : transitiveConcretize) {
223            IrSetVar[] siblingVars = solver.getSolutionMap().getAstSolution().getSiblingVars(clafer);
224            for (IrSetVar siblingVar : siblingVars) {
225                Either<int[], SetVar> var = solver.getSolutionMap().getIrSolution().getSetVar(siblingVar);
226                if (var.isRight()) {
227                    setVars.add(var.getRight());
228                }
229            }
230            AstRef ref = AstUtil.getInheritedRef(clafer);
231            if (ref != null) {
232                IrIntVar[] refVars = solver.getSolutionMap().getAstSolution().getRefVars(ref);
233                for (IrIntVar refVar : refVars) {
234                    Either<Integer, IntVar> var = solver.getSolutionMap().getIrSolution().getIntVar(refVar);
235                    if (var.isRight()) {
236                        intVars.add(var.getRight());
237                    }
238                }
239            }
240        }
241        solver.getInternalSolver().getSearchLoop().plugSearchMonitor(new IMonitorSolution() {
242            private static final long serialVersionUID = 1L;
243
244            @Override
245            public void onSolution() {
246                List<Constraint> constraints = new ArrayList<>();
247                for (IntVar var : intVars) {
248                    constraints.add(ICF.arithm(var, "!=", var.getValue()));
249                }
250                for (SetVar var : setVars) {
251                    constraints.add(Constraints.notEqual(var, var.getValue()));
252                }
253                solver.getInternalSolver().postCut(Constraints.or(
254                        constraints.toArray(new Constraint[constraints.size()])));
255            }
256        });
257        return solver;
258    }
259
260    private static void concretize(AstClafer clafer, Set<AstConcreteClafer> concretize) {
261        if (clafer instanceof AstAbstractClafer) {
262            concretize((AstAbstractClafer) clafer, concretize);
263        } else {
264            concretize((AstConcreteClafer) clafer, concretize);
265        }
266    }
267
268    private static void concretize(AstConcreteClafer clafer, Set<AstConcreteClafer> concretize) {
269        if (!AstUtil.isRoot(clafer) && concretize.add(clafer)) {
270            concretize(clafer.getParent(), concretize);
271            concretize(clafer.getSuperClafer(), concretize);
272        }
273    }
274
275    private static void concretize(AstAbstractClafer clafer, Set<AstConcreteClafer> concretize) {
276        if (!AstUtil.isTypeRoot(clafer)) {
277            for (AstClafer sub : clafer.getSubs()) {
278                concretize(sub, concretize);
279            }
280        }
281    }
282}