/******************************************************************************* * 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.LinkedList; import java.util.List; import java.util.Queue; import br.usp.poli.takiyama.cfove.StdParfactor; import br.usp.poli.takiyama.cfove.StdParfactor.StdParfactorBuilder; import br.usp.poli.takiyama.common.Marginal; import br.usp.poli.takiyama.common.Parfactor; import br.usp.poli.takiyama.common.StdMarginal.StdMarginalBuilder; import br.usp.poli.takiyama.prv.Prv; import br.usp.poli.takiyama.prv.Prvs; import br.usp.poli.takiyama.prv.RandomVariableSet; import br.usp.poli.takiyama.utils.Sets; final class GlobalSumOut implements MacroOperation { // The marginal where elimination will take place private final Marginal marginal; // The set of random variables to eliminate private final RandomVariableSet eliminables; /* * These variables are redundant. If cost is INFINITY, then this operation * is automatically impossible. */ private int cost; private boolean isPossible; private static int infinity = (int) Double.POSITIVE_INFINITY; public GlobalSumOut(Marginal marginal, RandomVariableSet eliminables) { this.marginal = marginal; this.eliminables = eliminables; calculateFeasibility(); } /* * Calculates the feasibility of this operation. This operation is possible * if all parfactors involving the variables being eliminated can be * multiplied and those variables can be summed out from the product. */ private void calculateFeasibility() { setCost(infinity); RandomVariableSet elim = RandomVariableSet.getInstance(eliminables.prv().getCanonicalForm(), eliminables.constraints()); if (Prvs.areDisjoint(elim, marginal.preservable())) { Queue<Parfactor> queue = new LinkedList<Parfactor>(marginal.distribution().toSet()); Parfactor result = new StdParfactorBuilder().build(); for (Parfactor candidate : queue) { if (containsEliminable(candidate)) { if (result.isMultipliable(candidate)) { result = result.multiply(candidate); } else { // contains eliminables but cannot be multiplied: be sure // to shatter before! return; } } } if (result.isEliminable(eliminables)) { int f = result.factor().size(); int v = eliminables.range().size(); setCost(f / v); } } } /* * Updates cost and isPossible. Helps to keep consistency. */ private void setCost(int c) { if (c < infinity) { this.cost = c; this.isPossible = true; } else { this.cost = infinity; this.isPossible = false; } } /** * Returns true if the specified parfactor contains the set of variables * to eliminate. */ private boolean containsEliminable(Parfactor candidate) { /* * In theory, marginal is shattered, so candidate either contains * eliminables or not. */ RandomVariableSet elim = RandomVariableSet.getInstance(eliminables.prv().getCanonicalForm(), Sets.union(eliminables.prv().constraints(), eliminables.constraints())); List<Prv> variables = candidate.prvs(); for (Prv prv : variables) { RandomVariableSet rvs = RandomVariableSet.getInstance(prv.getCanonicalForm(), candidate.constraints()); if (rvs.equals(elim)) { return true; } } return false; } @Override public Marginal run() { if (isPossible) { Parfactor result = new StdParfactorBuilder().build(); StdMarginalBuilder marginalResult = new StdMarginalBuilder(); marginalResult.add(marginal); // Multiplies all parfactors that involve the eliminable PRV for (Parfactor candidate : marginal) { if (containsEliminable(candidate)) { result = result.multiply(candidate); marginalResult.remove(candidate); } } // Sums out the eliminable if possible - actually it should be possible at this point //if (Sets.setOf(eliminables.prv().parameters()).equals(result.logicalVariables())) { try { result = result.sumOut(eliminables.prv()); } catch (IllegalArgumentException e) { result = new StdParfactor.StdParfactorBuilder().build(); } //} // Adds the result to marginal result if not constant (constant // parfactors are irrelevant) if (!result.isConstant()) { marginalResult.add(result); } return marginalResult.build(); } else { return marginal; } } @Override public int cost() { return cost; } @Override public int numberOfRandomVariablesEliminated() { // cost = infinity means this operation is impossible, thus no vars // can be eliminated. if (cost() == infinity) { return 0; } else { return eliminables.prv().groundSetSize(eliminables.constraints()); } } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("GLOBAL-SUM-OUT").append(" ").append(eliminables); return builder.toString(); } }