/******************************************************************************* * Copyright 2014 Felipe Takiyama * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package br.usp.poli.takiyama.acfove; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import br.usp.poli.takiyama.cfove.StdParfactor.StdParfactorBuilder; import br.usp.poli.takiyama.common.Constraint; import br.usp.poli.takiyama.common.Marginal; import br.usp.poli.takiyama.common.Parfactor; import br.usp.poli.takiyama.common.StdMarginal; import br.usp.poli.takiyama.log.FileLogger; import br.usp.poli.takiyama.prv.LogicalVariable; import br.usp.poli.takiyama.prv.Prv; import br.usp.poli.takiyama.prv.RandomVariableSet; import br.usp.poli.takiyama.utils.Sets; public class ACFOVE { private final Marginal input; private Marginal result; private MacroOperation currentOperation; private final static Logger logger = Logger.getLogger(ACFOVE.class.getName()); // change later // private final long timeout; // private final long deadline; // long start, end; // double timeSeconds; /* * I need to create a mechanism to avoid deadlocks between expanding and * counting operations. * This situation happens when expanding/counting the same PRV alternates * as the best operation available. */ /** * Constructor. Initializes AC-FOVE by shattering the specified marginal on * the query and shattering all parfactors. */ public ACFOVE(Marginal parfactors, Level logLevel) { FileLogger.setup(logLevel); // timeout = 100000; // deadline = System.currentTimeMillis() + timeout; logger.info("Starting AC-FOVE..."); this.input = parfactors; logger.info("Input: \n" + input + "\n"); result = input; // start = System.currentTimeMillis(); this.result = performInitialShattering(result); // end = System.currentTimeMillis(); // timeSeconds = (end - start) / 1000.0; // logger.severe("Time to initial shatter: " + timeSeconds + " s\n"); logger.info("Initial shattering and conversion: \n" + result + "\n"); this.currentOperation = new Shatter(result); } /** * Constructor. Initializes AC-FOVE using <code>Level.SEVERE</code> as log * level. */ public ACFOVE(Marginal parfactors) { this(parfactors, Level.OFF); } /** * Shatters the specified marginal on the query (preservable of the * marginal). After that shatters all parfactors in the marginal to * guarantee that for each pair of PRVs in different parfactors, sets of * random variables represented by them are either equal or disjoint. * * @param arg The marginal to shatter. * @return The specified marginal shattered */ private Marginal performInitialShattering(Marginal arg) { Parfactor query = new StdParfactorBuilder() .variables(arg.preservable().prv()) .constraints(arg.preservable().constraints()) .build(); Marginal result = new StdMarginal.StdMarginalBuilder().add(arg).add(query).build(); result = new Shatter(result).run(); return result; } /** * Runs the AC-FOVE algorithm and returns the result. * @return The result of running the AC-FOVE algorithm on the marginal * specified when creating this instance. */ public Parfactor run() { while (thereAreVariablesToEliminate()) { runStep(); resetCurrentOperation(); } evaluateFinalMultiplication(); try { runStep(); } catch (UnsupportedOperationException e) { throw new IllegalStateException(); } logger.info("Result:\n" + result + "\n"); return result.iterator().next(); } /** * Returns <code>true</code> if there are variables to eliminate in the * marginal. All random variables that are not in the query must be * eliminated. */ private boolean thereAreVariablesToEliminate() { return !result.eliminables().isEmpty(); } /** * Returns the result of running one step of the algorithm. A step * consists in choosing a macro operation and executing it. * @return the result of running one step of the algorithm. */ Marginal runStep() { // start = System.currentTimeMillis(); chooseMacroOperation(); // end = System.currentTimeMillis(); // logger.severe("Time to choose operation: " + (end - start) + " ms\n"); // start = System.currentTimeMillis(); executeMacroOperation(); // end = System.currentTimeMillis(); // timeSeconds = (end - start) / 1000.0; // logger.severe("Time to run operation: " + (end - start) + " ms\n"); logger.info("Operation result:\n" + result + "\n\n\n"); return result; } /** * Chooses the macro operation to execute. The chosen operation must have * a smaller cost than the current operation. */ void chooseMacroOperation() { for (Parfactor p : result) { for (Prv prv : p.prvs()) { evaluateFullExpand(p, prv); evaluateGlobalSumOut(prv, p.constraints()); } for (LogicalVariable lv : p.logicalVariables()) { evaluateCountingConvert(p, lv); evaluatePropositionalize(p, lv); } evaluateConversionToStdParfactors(p); } } private void evaluateGlobalSumOut(Prv prv, Set<Constraint> c) { RandomVariableSet eliminables = RandomVariableSet.getInstance(prv, c); MacroOperation candidate = new GlobalSumOut(result, eliminables); compareAndUpdate(candidate); } // used by VariableElimination void evaluateGlobalSumOut(Prv prv) { RandomVariableSet eliminables = RandomVariableSet.getInstance(prv, Sets.<Constraint>getInstance(0)); MacroOperation candidate = new GlobalSumOut(result, eliminables); compareAndUpdate(candidate); } private void evaluateFullExpand(Parfactor p, Prv prv) { MacroOperation candidate = new FullExpand(result, p, prv); compareAndUpdate(candidate); } private void evaluateCountingConvert(Parfactor p, LogicalVariable v) { MacroOperation candidate = new CountingConvert(result, p, v); compareAndUpdate(candidate); } private void evaluatePropositionalize(Parfactor p, LogicalVariable v) { MacroOperation candidate = new Propositionalize(result, p, v); compareAndUpdate(candidate); } private void evaluateFinalMultiplication() { MacroOperation candidate = new FinalMultiplication(result); compareAndUpdate(candidate); } private void evaluateConversionToStdParfactors(Parfactor p) { MacroOperation candidate = new ConvertToStdParfactors(result, p); compareAndUpdate(candidate); } /** * Compares the candidate macro operation with current operation and * updates current if candidate's cost is smaller. * In case of draw, the operation that eliminates more random variables is * chosen. * In case of another draw, current operation is kept. */ private void compareAndUpdate(MacroOperation candidate) { logger.fine("Evaluating candidate " + candidate + " with " + currentOperation + "\n"); int candidateCost = candidate.cost(); int currentCost = currentOperation.cost(); int candidateEliminables = candidate.numberOfRandomVariablesEliminated(); int currentEliminables = currentOperation.numberOfRandomVariablesEliminated(); boolean costIsSmaller = (candidateCost < currentCost); boolean eliminatesMore = (candidateEliminables > currentEliminables); boolean eliminatesTheSame = (candidateEliminables == currentEliminables); logger.fine("Eliminates:" + candidateEliminables + "\n"); logger.fine("Cost:" + candidateCost + "\n"); if (eliminatesMore || (eliminatesTheSame && costIsSmaller)) { logger.fine("Setting " + candidate + " as current operation.\n"); currentOperation = candidate; } else { logger.fine("Keeping " + currentOperation + " as current operation.\n"); } } /** * Executes the current macro operation. * TODO: throw exception when something happens */ private void executeMacroOperation() { logger.info("Running " + currentOperation + "\n"); result = currentOperation.run(); } /** * Resets the current macro operation for the next step of the algorithm. */ private void resetCurrentOperation() { currentOperation = ImpossibleOperation.instance; } Marginal result() { return this.result; } MacroOperation currentOperation() { return this.currentOperation; } }