/******************************************************************************* * 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.cfove; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import br.usp.poli.takiyama.common.AggregationParfactor; import br.usp.poli.takiyama.common.Builder; import br.usp.poli.takiyama.common.ConstantFactor; import br.usp.poli.takiyama.common.Constraint; import br.usp.poli.takiyama.common.Factor; import br.usp.poli.takiyama.common.MultiplicationChecker; import br.usp.poli.takiyama.common.Parfactor; import br.usp.poli.takiyama.common.ParfactorVisitor; import br.usp.poli.takiyama.common.Scanner; import br.usp.poli.takiyama.common.SplitResult; import br.usp.poli.takiyama.common.StdFactor; import br.usp.poli.takiyama.common.Tuple; import br.usp.poli.takiyama.prv.Binding; import br.usp.poli.takiyama.prv.Constant; import br.usp.poli.takiyama.prv.CountingFormula; import br.usp.poli.takiyama.prv.LogicalVariable; import br.usp.poli.takiyama.prv.Population; import br.usp.poli.takiyama.prv.Prv; import br.usp.poli.takiyama.prv.RangeElement; import br.usp.poli.takiyama.prv.StdLogicalVariable; import br.usp.poli.takiyama.prv.StdPrv; import br.usp.poli.takiyama.prv.Substitution; import br.usp.poli.takiyama.prv.Term; import br.usp.poli.takiyama.utils.Lists; import br.usp.poli.takiyama.utils.MathUtils; import br.usp.poli.takiyama.utils.Sets; public final class StdParfactor implements Parfactor { private final Set<Constraint> constraints; private final Factor factor; /* ************************************************************************ * Builders * ************************************************************************/ public static class StdParfactorBuilder implements Builder<StdParfactor> { private Set<Constraint> restrictions; private List<Prv> prvs; private List<BigDecimal> values; public StdParfactorBuilder() { restrictions = new HashSet<Constraint>(); prvs = new ArrayList<Prv>(); values = new ArrayList<BigDecimal>(); } public StdParfactorBuilder(Parfactor p) { this(); constraints(p.constraints()); variables(p.prvs()); values(p.factor().values()); factor(p.factor()); } public StdParfactorBuilder constraints(Constraint ... c) { restrictions.addAll(Arrays.asList(c)); return this; } public StdParfactorBuilder constraints(Set<Constraint> c) { restrictions.addAll(c); return this; } public StdParfactorBuilder variables(Prv ... prv) { prvs.addAll(Arrays.asList(prv)); return this; } public StdParfactorBuilder variables(List<Prv> prv) { prvs.addAll(prv); return this; } public StdParfactorBuilder values(BigDecimal ... v) { values.addAll(Arrays.asList(v)); return this; } public StdParfactorBuilder values(List<BigDecimal> v) { values.addAll(v); return this; } public StdParfactorBuilder values(double ... v) { for (double d : v) { values.add(BigDecimal.valueOf(d)); } return this; } /** * Sets the factor for this builder. Values and PRVs from the * specified factor are added to this builder. * * @param f The factor * @return This builder with the factor updated. */ public StdParfactorBuilder factor(Factor f) { variables(f.variables()); values(f.values()); return this; } /** * Returns the factor defined by PRVs and values in this builder. * <p> * If no values were set, returns a constant factor. * </p> * <p> * If no variables were set, returns an empty factor. * </p> * @return The factor defined by PRVs and values in this builder. * @throws IllegalStateException */ private Factor getFactor() throws IllegalStateException { Factor factor; List<Prv> variables = new ArrayList<Prv>(this.prvs); if (this.values.isEmpty()) { factor = ConstantFactor.getInstance(variables); } else { try { factor = StdFactor.getInstance("", variables, values); } catch (IllegalArgumentException e) { throw new IllegalStateException(variables + "\n" + values); } } return factor; } @Override public StdParfactor build() { // StdParfactor simplified = new StdParfactor(this); // Simplifier simplifier = simplified.new Simplifier(simplified); // // return ((StdParfactor) simplifier.simplify()); // this is causing some problems // StdParfactor current = new StdParfactor(this); // return ((StdParfactor) current.simplifyLogicalVariables()); return new StdParfactor(this); } private StdParfactor buildRaw() { return new StdParfactor(this); } } /** * Encapsulates splitting algorithm. * <p> * Splitting a parfactor "breaks" a parfactor in two. * </p> */ private final class Splitter { private Parfactor splittable; private Substitution substitution; private Splitter(Parfactor splittable, Substitution substitution) { this.splittable = splittable; this.substitution = substitution; } /** * Splits this parfactor on the specified substitution. The result of * splitting StdParfactor g on substitution {X/t} will * have two parfactors: * <li> g[X/t], the parfactor g after applying substitution {X/t}; * <li> g' = ⟨ C U {X ≠ t}, V, F ⟩, the residual * parfactor. */ private SplitResult split() { if (!isSplittable(substitution)) { throw new IllegalArgumentException(splittable + " is not splittable on " + substitution); } Parfactor result = splittable.apply(substitution); Parfactor residue = getResidue(); SplitResult split = SplitResult.getInstance(result, residue); return split; } /** * Returns a Parfactor equal to this one with the specified constraint * added. */ private Parfactor getResidue() { Binding b = substitution.first(); Constraint c = b.toInequalityConstraint(); Set<Constraint> constraints = splittable.constraints(); constraints.add(c); return new StdParfactorBuilder().constraints(constraints) .variables(splittable.prvs()) .values(splittable.factor().values()) .build(); } } /** * Encapsulates counting algorithm. * <p> * Counting a free logical variable eliminates it from the parfactor using * a counting formula. * </p> */ private class Counter { // TODO use aggregation instead of inheritance // The parfactor being modified private Parfactor parfactor; // PRV that contains the logical variable being counted private Prv counted; // The logical variable being counted private LogicalVariable bound; // Subset of constraints that contains the bound logical variable private Set<Constraint> constraintsOnBound; // A counting formula that replaces PRV 'counted' private Prv countingFormula; // Index of 'counted' in the list of PRVs private int countedIndex; // Values from the result private List<BigDecimal> values; // Variables in the result private List<Prv> variables; // Constraints in the result private Set<Constraint> constraints; /* * I use super.factor to navigate through the old factor, and * super.prvs/super.values to set the values for the new factor */ /** * Creates a Counter using the specified parfactor. The 'counted' * parfactor will be built based on the specified parfactor. * * @param p The parfactor on which counting will take place. */ private Counter(Parfactor p) { parfactor = p; counted = StdPrv.getInstance(); bound = StdLogicalVariable.getInstance(); constraintsOnBound = new HashSet<Constraint>(parfactor.constraints().size()); countingFormula = StdPrv.getInstance(); countedIndex = 0; values = parfactor.factor().values(); variables = parfactor.prvs(); constraints = parfactor.constraints(); } /** * Counts the specified free logical variable in this parfactor. * * @param lv The logical variable to count * @return A {@link StdParfactorBuilder} with the result of counting. */ private Parfactor count(LogicalVariable lv) { setBound(lv); partitionOnBound(); setPrvOnOnBound(); setCountingFormulaOnBound(); replaceCountedWithCountingFormula(); setValues(); return new StdParfactorBuilder().constraints(constraints).variables(variables).values(values).build(); } /** * Sets the free logical variable to be bound during count. * @param lv The logical variable to bound */ private void setBound(LogicalVariable lv) { bound = StdLogicalVariable.getInstance(lv); } /** * Splits constraints from this parfactor in two subsets, one that * involves the bound logical variable and another that does not. */ private void partitionOnBound() { for (Constraint c : parfactor.constraints()) { if (c.contains(bound)) { constraintsOnBound.add(c); } } constraints.removeAll(constraintsOnBound); } /** * Searches the PRV in parfactor being processed that contains the * bound logical variable as a parameter. This method assumes that * there is only one PRV satisfying this condition. * * @see StdParfactor#isCountable(LogicalVariable) */ private void setPrvOnOnBound() { counted = parfactor.factor().getVariableHaving(bound); countedIndex = variables.indexOf(counted); } /** * Builds the counting formula that will replace PRV on bound logical * variable in the new parfactor */ private void setCountingFormulaOnBound() { countingFormula = CountingFormula.getInstance(bound, counted, constraintsOnBound); } /** * Replaces the old PRV being counted with its corresponding counting * formula. */ private void replaceCountedWithCountingFormula() { variables.set(countedIndex, countingFormula); } /** * Builds the array that defines the values in the new parfactor. */ private void setValues() { /** * for each tuple t in F' * value = 1 * for each range element e in PRV's range * t' = t with counting formula replaced by e * value = value * F(t)^h(e) * add value to v[] */ values = new ArrayList<BigDecimal>(); Factor newStructure = ConstantFactor.getInstance(variables); for (Tuple<RangeElement> tuple : newStructure) { BigDecimal value = BigDecimal.ONE; for (RangeElement e : counted.range()) { Tuple<RangeElement> old = tuple.set(countedIndex, e); CountingFormula cf = (CountingFormula) countingFormula; int count = cf.getCount(tuple.get(countedIndex), e); value = value.multiply(parfactor.factor().getValue(old).pow(count, MathUtils.CONTEXT), MathUtils.CONTEXT); } values.add(value); } // Erase factor so it does not overwrites constructor //super.factor = null; } } /** * Encapsulates simplification algorithm * <p> * Simplifying a parfactor is the process of replacing all logical variables * constrained to a single individual with this individual. * </p> */ private class Simplifier { private final Parfactor parfactor; private Set<Constraint> unaryConstraints; private Set<Constraint> constraints; private List<Prv> variables; private Simplifier(StdParfactor parfactor) { this.parfactor = parfactor; this.constraints = new HashSet<Constraint>(parfactor.constraints()); this.variables = new ArrayList<Prv>(parfactor.prvs()); this.unaryConstraints = getUnaryConstraints(); } // /* // * This is a quick, temporary, dirt fix ;P // * I need to simplify all parfactors just after creating them, so I // * will call this simplifier in builder's build() method. // */ // private Simplifier(Set<Constraint> constraints, List<Prv> variables, List<BigDecimal> values) { // this.parfactor = new StdParfactorBuilder().constraints(constraints) // .variables(variables).values(values).build(); // this.constraints = this.parfactor.constraints(); // this.variables = this.parfactor.prvs(); // this.unaryConstraints = getUnaryConstraints(); // } /** * Returns the subset of unary constraints from the set of constraints */ private Set<Constraint> getUnaryConstraints() { Set<Constraint> unary = new HashSet<Constraint>(); for (Constraint constraint : constraints) { if (constraint.isUnary()) { unary.add(constraint); } } return unary; } /** * Replaces all logical variable constrained to a single individual * with this individual */ private Parfactor simplify() { simplifyVariablesWithPopulationOne(); LinkedList<LogicalVariable> queue = getVariablesInConstraints(); while (!queue.isEmpty()) { LogicalVariable logicalVariable = queue.poll(); int populationSize = logicalVariable .numberOfIndividualsSatisfying(unaryConstraints); switch (populationSize) { case 0: return StdParfactor.getInstance(); case 1: Substitution sub = getSubstitution(logicalVariable); queue.addAll(variablesInBinaryConstraintsInvolving(logicalVariable)); constraints = Sets.apply(sub, constraints); unaryConstraints = getUnaryConstraints(); variables = Lists.apply(sub, variables); break; default: break; } } Parfactor result = new StdParfactorBuilder().constraints(constraints) .variables(variables).values(parfactor.factor().values()).buildRaw(); return result; } /** * Add logical variables from parfactor's constraints to a queue * >> ugly implementation =( */ private LinkedList<LogicalVariable> getVariablesInConstraints() { Set<LogicalVariable> buffer = new HashSet<LogicalVariable>(); for (Constraint c : parfactor.constraints()) { buffer.addAll(c.logicalVariables()); } return new LinkedList<LogicalVariable>(buffer); } /** * When a logical variable is constrained to a single individual, * builds the substitution that replaces the logical variable by this * individual. */ private Substitution getSubstitution(LogicalVariable lv) { Term loneGuy = lv.individualsSatisfying(unaryConstraints).iterator().next(); Binding bind = Binding.getInstance(lv, loneGuy); return Substitution.getInstance(bind); } /** * Returns the set of logical variables belonging to binary constraints * that involve the specified logical variable. */ private Set<LogicalVariable> variablesInBinaryConstraintsInvolving(LogicalVariable lv) { Set<Constraint> binaryConstraints = new HashSet<Constraint>(constraints); binaryConstraints.removeAll(unaryConstraints); Set<LogicalVariable> otherVariables = new HashSet<LogicalVariable>(); for (Constraint binary : binaryConstraints) { if (binary.contains(lv) && binary.firstTerm().equals(lv)) { otherVariables.add((LogicalVariable) binary.secondTerm()); } else if (binary.contains(lv) && binary.secondTerm().equals(lv)) { otherVariables.add((LogicalVariable) binary.firstTerm()); } } return otherVariables; } private void simplifyVariablesWithPopulationOne() { Parfactor p = new Scanner(parfactor); List<Binding> bindList = new ArrayList<Binding>(p.logicalVariables().size()); for (LogicalVariable v : p.logicalVariables()) { if (v.population().size() == 1) { Constant c = v.population().individualAt(0); Binding bind = Binding.getInstance(v, c); bindList.add(bind); } } Substitution substitution = Substitution.getInstance(bindList); variables = Lists.apply(substitution, variables); } } /* ************************************************************************ * Constructors * ************************************************************************/ /** * @deprecated * Constructor. * @param constraints A set of {@link Constraint}s on logical variables * @param factor A {@link Factor} from the Cartesian product of ranges of * parameterized random variables to the reals. */ private StdParfactor(Set<Constraint> constraints, Factor factor) { this.constraints = new HashSet<Constraint>(constraints); this.factor = StdFactor.getInstance(factor); } /** * Creates an instance of StdParfactor based on a * {@link StdParfactorBuilder}. * * @param builder A Standard Parfactor Builder */ private StdParfactor(StdParfactorBuilder builder) { this.factor = builder.getFactor(); this.constraints = new HashSet<Constraint>(builder.restrictions); } /* ************************************************************************ * Static factories * ************************************************************************/ /** * @deprecated * Returns a constant parfactor, the neutral factor for multiplications. * Any parfactor multiplied by the constant parfactor does not change. * <p> * The constant parfactor contains only one value, 1. There are no * parameterized random variables or constraints in this parfactor. * </p> * * @return The constant parfactor. */ public static Parfactor getInstance() { Set<Constraint> constraints = new HashSet<Constraint>(0); Factor factor = StdFactor.getInstance(); return new StdParfactor(constraints, factor); } /** * @deprecated * Returns a new instance StdParfactor that has the same constraints and * the same factor of the specified Parfactor. * * @param p The parfactor to "copy" */ public static Parfactor getInstance(Parfactor p) { return new StdParfactor(p.constraints(), p.factor()); } /** * @deprecated * Returns an instance of StdParfactor that has the specified constraints * and the specified factor. * * @param constraints A set of constraints * @param factor A factor * @return An instance of StdPArfactor that has the specified constraints * and the specified factor */ public static Parfactor getInstance(Set<Constraint> constraints, Factor factor) { return new StdParfactor(constraints, factor); } /* ************************************************************************ * Getters * ************************************************************************/ @Override public Set<Constraint> constraints() { return new HashSet<Constraint>(constraints); } @Override public Factor factor() { return factor; } @Override public Set<LogicalVariable> logicalVariables() { List<Prv> prvs = factor.variables(); Set<LogicalVariable> logicalVariables = new HashSet<LogicalVariable>(); for (Prv prv : prvs) { logicalVariables.addAll(prv.parameters()); } return logicalVariables; } @Override public List<Prv> prvs() { return factor.variables(); } @Override public int size() throws IllegalStateException { if (!isInNormalForm()) { throw new IllegalStateException("Parfactor not in normal form"); } int size = 1; Set<Constraint> toVisit = new HashSet<Constraint>(constraints); for (LogicalVariable lv : logicalVariables()) { size = size * lv.numberOfIndividualsSatisfying(toVisit); toVisit = remove(toVisit, lv); } return size; } /** * Returns the specified set of constraints with all constraints that * contain the specified term removed. * * @param constraints A set of constraints * @param t The term to search in constraints * @return The specified set of constraints with all constraints that * contain the specified term removed. */ private Set<Constraint> remove(Set<Constraint> constraints, Term t) { Set<Constraint> allConstraints = new HashSet<Constraint>(constraints); for (Constraint c : allConstraints) { if (c.contains(t)) { constraints.remove(c); } } return constraints; } @Override public Parfactor apply(Substitution s) { Set<Constraint> substitutedConstraints = Sets.apply(s, constraints); Factor substitutedFactor = factor.apply(s); return StdParfactor.getInstance(substitutedConstraints, substitutedFactor); } @Override public boolean contains(Prv prv) { return factor.variables().contains(prv); } @Override public boolean isConstant() { boolean hasNoConstraints = constraints.isEmpty(); boolean hasConstantFactor = factor.isConstant(); return hasNoConstraints && hasConstantFactor; } @Override public boolean isCountable(LogicalVariable lv) { return (factor.occurrences(lv) == 1); } /** * Returns <code>true</code> if the specified {@link Prv} can * be expanded on the specified term. * <p> * More specifically, this method checks the following conditions: * <li> This parfactor is in normal form * <li> The specified PRV is a counting formula * #<sub>A:C<sub>A</sub></sub>[f(...A...)] from this parfactor * <li> The specified term does not belong to the excluded set for A on * C<sub>A</sub> * <li> The specified term belongs to the excluded set for Y on constraints * of this parfactor, for each Y in the excluded set for A on C<sub>A</sub>. * </p> * @param cf The PRV to be expanded * @param term The term to expand the counting formula on * @return <code>true</code> if the specified PRV can * be expanded on the specified term, <code>false</code> otherwise */ @Override public boolean isExpandable(Prv cf, Substitution s) { if (s.size() != 1) { return false; } Binding bind = s.asList().get(0); LogicalVariable replaced = bind.firstTerm(); Term t = bind.secondTerm(); // Is counting formula? boolean isCountingFormula = !cf.boundVariable().isEmpty(); // Is it present in this parfactor? boolean belongsHere = factor.variables().contains(cf); // Is this parfactor in normal form? boolean isInNormalForm = isInNormalForm(); // Does t not appear on any constraint from the counting formula? boolean isCountable = !contains(cf.constraints(), t); // For each term Y that appears in constraints from the counting // formula, is there a constraint Y != t in this parfactor? boolean isOrthogonal = isOrthogonal(cf, t); /* * In theory it is possible to expand a counting formula on a logical * variable. For now, I will not allow it to make shattering simpler. * Need to find an example where expanding on a logical variable is * the only and correct thing to do. */ // boolean isLocalLogicalVariable = logicalVariables().contains(t); boolean replacesBound = replaced.equals(cf.boundVariable()); boolean isConstantFromBound = (t.isConstant() ? cf.boundVariable().population().contains((Constant) t) : false); return isCountingFormula && belongsHere && isInNormalForm && isCountable && isOrthogonal && replacesBound && isConstantFromBound; //&& (isLocalLogicalVariable || isConstantFromBound); // disabled for the reason above } /** * Returns <code>true</code> if this parfactor is in normal form. * <p> * A parfactor is in normal form if, for each inequality constraint * (X ≠ Y) ∈ C we have ε<sub>X</sub><sup>C</sup>\{Y} = * ε<sub>Y</sub><sup>C</sup>\{X}. X and Y are logical variables. * </p> * @return <code>true</code> if this parfactor is in normal form, * <code>false</code> otherwise */ private boolean isInNormalForm() { // This algorithm does not look very clever for (Constraint c : constraints) { if (c.firstTerm().isVariable() && c.secondTerm().isVariable()) { LogicalVariable x = (LogicalVariable) c.firstTerm(); LogicalVariable y = (LogicalVariable) c.secondTerm(); Set<Term> ex = x.excludedSet(constraints); Set<Term> ey = y.excludedSet(constraints); ex.remove(y); ey.remove(x); if (!ex.equals(ey)) { return false; } } } return true; } /** * Returns <code>true</code> if the specified term is in at least one of * the constraints from the specified set. * * @param constraints A set of constraints * @param t The term to search in constraints from the set * @return <code>true</code> if the specified term is in at least one of * the constraints from the specified set, <code>false</code> otherwise */ private boolean contains(Set<Constraint> constraints, Term t) { for (Constraint c : constraints) { if (c.contains(t)) { return true; } } return false; } /** * Returns <code>true</code> if the specified term is orthogonal to all * constraints in the specified counting formula. * <p> * A term t is orthogonal to a constraints in a counting formula when, * for each logical variable Y ∈ * ε<sub>A</sub><sup>C<sub>A</sub></sup>, * we have t ∈ ε<sub>Y</sub><sup>C<sub>i</sub></sup>. * </p> * <p> * In other words, for each logical variable Y that appears in constraints * C<sub>A</sub>, checks if the constraint Y ≠ t appears in * C<sub>i</sub> (constraints from this parfactor). * If there is a logical variable Y that does not satisfy it, returns * <code>false</code>. * </p> * @param cf A counting formula #<sub>A:C<sub>A</sub></sub>[f(...,A,...)] * @param t The term to check * @return <code>true</code> if the specified term is orthogonal to all * constraints in the specified counting formula, <code>false</code> * otherwise */ private boolean isOrthogonal(Prv cf, Term t) { for (Term y : cf.boundVariable().excludedSet(cf.constraints())) { if (y.isVariable()) { Set<Term> ey = ((LogicalVariable) y).excludedSet(constraints); if (!ey.contains(t)) { return false; } } } return true; } @Override public boolean isMultipliable(Parfactor other) { /* * Uses a ParfactorVisitor to discover the type of 'other'. * The algorithm to check if parfactors are multipliable is * encapsulated in MultiplicationChecker. */ MultiplicationChecker parfactors = new MultiplicationChecker(); accept(parfactors, other); return parfactors.areMultipliable(); } /** * Returns <code>true</code> if this parfactor can be split on the * specified substitution. * <p> * The following conditions are checked: * <li> The substitution is of the form {X/t}, where X is a logical * variable and t is a term; * <li> X is a logical variable in this parfactor; * <li> t is not in any constraints from this parfactor; * <li> t is a constant such that t ∈ D(x) <b>or</b> t is logical * variable present in this parfactor such that D(t) = D(X). * </p> * * @param s The substitution to split this parfactor on * @return <code>true</code> if this parfactor can be split on the * specified substitution, <code>false</code> otherwise. */ @Override public boolean isSplittable(Substitution s) { boolean isSplittable; if (s.size() != 1) { // Split method only works for 1 binding isSplittable = false; } else { Binding b = s.first(); Term x = b.firstTerm(); Term t = b.secondTerm(); // Can the substitution be applied to logical variables in this parfactor? boolean isApplicable = logicalVariables().contains(x); // Is the replacement absent from constraints in this parfactor? boolean isNotInConstraints = !contains(constraints, t); // Does the substitution make sense? boolean isValidSubstitution = b.isValid(); // If we are replacing for a logical variable, is it present in // this parfactor? boolean isLogicalVariable = (t.isVariable() ? logicalVariables().contains(t) : true); isSplittable = isApplicable && isNotInConstraints && isValidSubstitution && isLogicalVariable; } return isSplittable; } @Override public boolean isEliminable(Prv prv) { Set<LogicalVariable> lvs = Sets.getInstance(0); for (Prv v : factor.variables()) { lvs.addAll(v.parameters()); } return prv.parameters().containsAll(lvs); } @Override public Parfactor count(LogicalVariable lv) throws IllegalArgumentException { if (!isCountable(lv)) { throw new IllegalArgumentException(); } return new Counter(this).count(lv); } // TODO encapsulate expand, sum out and multiplication @Override public Parfactor expand(Prv cf, Term t) { /* * First checks whether the set of constraints in the counting formula * is big enough to limit bound variable population size to 1. * When that happens, expansion converts counting formula * #.A:C[f(...A...)] to f(...t...), where t = D(A):C. */ int cfIndex = factor.variables().indexOf(cf); Population pop = cf.boundVariable().individualsSatisfying(cf.constraints()); if (pop.size() == 1) { Prv expandedPrv = ((CountingFormula) cf).toStdPrv(); List<Prv> prvs = Lists.replace(prvs(), cf, expandedPrv); Parfactor expanded = new StdParfactorBuilder().constraints(constraints()) .variables(prvs).values(factor.values()).build(); return expanded; } // Creates the new set of PRVs List<Prv> vars = getExpandedVariables(cf, t); // Creates a constant factor with the new set of PRVs Factor newStructure = ConstantFactor.getInstance(vars); // Creates an array to store the values of the new parfactor Prv takenOut = vars.get(cfIndex + 1); int newSize = factor.size() * takenOut.range().size(); List<BigDecimal> vals = new ArrayList<BigDecimal>(newSize); // Combines the new histogram with PRV that was taken out to get the // corresponding value in this factor for (Tuple<RangeElement> tuple : newStructure) { Tuple<RangeElement> combined = combine(tuple, cfIndex, cfIndex + 1); vals.add(factor.getValue(combined)); } // Converts the counting formula to StdPrv if possible vars.set(cfIndex, ((CountingFormula)vars.get(cfIndex)).simplify()); // Creates the expanded factor Parfactor expanded = new StdParfactorBuilder().constraints(constraints) .variables(vars).values(vals).build(); return expanded; } /** * Creates the new set of parameterized random variables for * {@link #expand(Prv, Term)}. * <p> * Let c = #_A:CA [f(...,A,...)] and * c' = #_A:(CA U {A != t}) [f(...,A,...)]. * </p> * <p> * Then V' = V \ {c} U {c'} * </p> * * @param cf The counting formula to expand * @param t The term to be taken out from the counting formula * @return the new set of parameterized random variables for * {@link #expand(Prv, Term)}. */ private List<Prv> getExpandedVariables(Prv cf, Term t) { List<Prv> vars = new ArrayList<Prv>(factor.variables()); int cfIndex = vars.indexOf(cf); Prv expanded = ((CountingFormula) cf).remove(t); Prv takenOut = ((CountingFormula) cf).takeOut(t); vars.set(cfIndex, expanded); vars.add(cfIndex + 1, takenOut); return vars; } // tenho que dar um jeito de converter de volta para histograma... /** * Returns the combination of elements at the specified indexes in the * specified tuple. The combined element is put in position <code>i</code> * and position <code>j</code> is removed. * * @see #expand(Prv, Term) * @param t The tuple to manipulate * @param i The index of the counting formula * @param j The index of a 'taken out' PRV * @return The combination of elements at the specified indexes in the * specified tuple. */ private Tuple<RangeElement> combine(Tuple<RangeElement> t, int i, int j) { Tuple<RangeElement> tuple = Tuple.getInstance(t); RangeElement newHistogram = t.get(i).combine(t.get(j)); tuple = tuple.set(i, newHistogram); tuple = tuple.remove(j); return tuple; } @Override public Parfactor multiply(Parfactor other) { return other.multiplicationHelper(this); } @Override public Parfactor multiplicationHelper(Parfactor other) { /* * I am sure that 'other' is a StdParfactor, because this method is * not called by AggregationParfactors.multiply() * I am pretty sure this will not cover all the cases when a new * type of parfactor is invented, but I cannot predict all * possible expansions (if there will be one) ;) * * 'other' is the parfactor that called multiply(), thus to keep * consistency: * other = index i * this = index j */ // Creates intermediate parfactor g = <Ci U Cj, Vi U Vj, Fi x Fj> Set<Constraint> union = Sets.union(other.constraints(), constraints); Factor fixfj = other.factor().multiply(factor); Parfactor g = new StdParfactorBuilder().constraints(union) .variables(fixfj.variables()).values(fixfj.values()).build(); // Correction exponents int giSize = other.size(); int gjSize = size(); int gSize = g.size(); // Creates factor Fi^ri x Fj^rj Factor fi = other.factor().pow(giSize, gSize); Factor fj = factor.pow(gjSize, gSize); Factor fixfjCorrected = fi.multiply(fj); // order is important here // Creates the product parfactor g' = <Ci U Cj, Vi U Vj, Fi^ri x Fj^rj> Parfactor product = new StdParfactorBuilder().constraints(union) .variables(fixfjCorrected.variables()) .values(fixfjCorrected.values()).build(); return product; } /** * Splits this parfactor on the specified substitution. The result of * splitting StdParfactor g on substitution {X/t} will * have two parfactors: * <li> g[X/t], the parfactor g after applying substitution {X/t}; * <li> g' = < C U {X ≠ t}, V, F >, the residual parfactor. */ @Override public SplitResult splitOn(Substitution s) throws IllegalArgumentException { return new Splitter(this, s).split(); } @Override public Parfactor sumOut(Prv prv) { // Creates the intermediate parfactor g = <C,V\{f},F'> Factor sumOut = factor.sumOut(prv); Parfactor g = new StdParfactorBuilder().constraints(constraints) .factor(sumOut).build(); // Correction exponent r = |gi|/|g| int gSize = g.size(); int thisSize = size(); // Creates the eliminated parfactor g' = <C,V\{f},F'^r> Factor corrected = sumOut.pow(thisSize, gSize); Parfactor summedOut = new StdParfactorBuilder().constraints(constraints) .factor(corrected).build(); return summedOut; } @Override public Parfactor simplifyLogicalVariables() { Simplifier parfactor = new Simplifier(this); return parfactor.simplify(); } @Override public void accept(ParfactorVisitor visitor, Parfactor p) { p.accept(visitor, this); } @Override public void accept(ParfactorVisitor visitor, StdParfactor p) { visitor.visit(this, p); } @Override public void accept(ParfactorVisitor visitor, AggregationParfactor p) { visitor.visit(p, this); } /* ************************************************************************ * toString, equals and hashCode * ************************************************************************/ @Override public String toString() { String result = "\nC = " + constraints + ",\n" + factor; return result; } @Override public boolean equals(Object other) { // Tests if both refer to the same object if (this == other) return true; // Tests if the Object is an instance of this class if (!(other instanceof StdParfactor)) return false; // Tests if both have the same attributes StdParfactor targetObject = (StdParfactor) other; return ((constraints == null) ? targetObject.constraints == null : constraints.equals(targetObject.constraints)) && ((factor == null) ? targetObject.factor == null : factor.equals(targetObject.factor)); } @Override public int hashCode() { int result = 17; result = 31 + result + constraints.hashCode(); result = 31 + result + factor.hashCode(); return result; } }