/******************************************************************************* * 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.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.cfove.StdParfactor; import br.usp.poli.takiyama.cfove.StdParfactor.StdParfactorBuilder; 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.Distribution; import br.usp.poli.takiyama.common.Factor; import br.usp.poli.takiyama.common.InequalityConstraint; 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.SplitResult; import br.usp.poli.takiyama.common.StdDistribution; import br.usp.poli.takiyama.common.StdFactor; import br.usp.poli.takiyama.common.Tuple; import br.usp.poli.takiyama.common.VisitableParfactor; 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.Operator; 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 class AggParfactor implements AggregationParfactor, VisitableParfactor { private final Prv parent; private final Prv child; private final Factor factor; private final Operator<? extends RangeElement> operator; private final Set<Constraint> constraintsNotOnExtra; private final Set<Constraint> constraintsOnExtra; private final LogicalVariable extraVar; /* * List of context parameterized random variables. * Context variables describe dependency in aggregation between * parent and child. * On standard aggregation parfactors, this list is empty. */ private final List<Prv> context; /* ************************************************************************ * Builders * ************************************************************************/ /** * Builder for {@link AggParfactor}. * <p> * There are two ways of specifying the factor associated with this * aggregation parfactor: either pass a factor that was previously built * using {@link #factor(Factor)} or pass the values that constitute the * factor using {@link #values()}. These operations overwrite changes * made by previous calls. * </p> */ public static class AggParfactorBuilder implements Builder<AggParfactor> { // mandatory parameters private final Prv p; private Prv c; // not final to allow renaming private final Operator<? extends RangeElement> op; private final LogicalVariable lv; // optional parameters private List<BigDecimal> values; private Set<Constraint> constraintsOnExtra; private Set<Constraint> constraintsNotOnExtra; private List<Prv> ctxt; public AggParfactorBuilder(Prv p, Prv c, Operator<? extends RangeElement> op) throws IllegalArgumentException { this.p = p; this.c = c; this.op = op; this.values = new ArrayList<BigDecimal>(); this.constraintsNotOnExtra = new HashSet<Constraint>(0); this.constraintsOnExtra = new HashSet<Constraint>(0); this.lv = setExtra(); this.ctxt = new ArrayList<Prv>(0); } public AggParfactorBuilder(AggregationParfactor ap) { this.p = ap.parent(); this.c = ap.child(); this.op = ap.operator(); this.values = ap.factor().values(); this.constraintsNotOnExtra = ap.constraintsNotOnExtra(); this.constraintsOnExtra = ap.constraintsOnExtra(); this.lv = StdLogicalVariable.getInstance(ap.extraVariable()); this.ctxt = ap.context(); } /** * Puts in <code>lv</code> the logical variable that is present in * the parent PRV and not in child PRV. * * @return The extra logical variable in parent PRV * @throws IllegalArgumentException If the parent PRV has a number * of extra variables that is different from 1. */ private LogicalVariable setExtra() throws IllegalArgumentException { List<LogicalVariable> pVars = p.parameters(); List<LogicalVariable> cVars = c.parameters(); List<LogicalVariable> diff = Lists.difference(pVars, cVars); if (diff.size() == 1) { return StdLogicalVariable.getInstance(diff.get(0)); } else { throw new IllegalArgumentException(); } } /** * Adds the specified constraint to this builder. * @param c The constraint to add * @return This builder with the constraint added */ public AggParfactorBuilder constraint(Constraint c) { if (c.contains(lv)) { constraintsOnExtra.add(c); } else { constraintsNotOnExtra.add(c); } return this; } /** * Adds the specified constraints to this builder. * @param c The constraints to add * @return This builder with the constraints added */ public AggParfactorBuilder constraints(Constraint ... c) { for (Constraint cons : c) { constraint(cons); } return this; } /** * Adds the specified constraints to this builder. * @param c The constraints to add * @return This builder with the constraints added */ public AggParfactorBuilder constraints(Set<Constraint> c) { for (Constraint cons : c) { constraint(cons); } return this; } /** * Sets the factor for this builder. * * @param f The factor * @return This builder with the factor updated. * @throws IllegalArgumentException If the specified factor is not * consistent with this builder. * @see #isConsistent(Factor) */ public AggParfactorBuilder factor(Factor f) throws IllegalArgumentException { if (isConsistent(f)) { values = f.values(); return this; } else if (needsReordering(f)) { Factor reference = ConstantFactor.getInstance(Lists.union(Lists.listOf(p), ctxt)); values = f.reorder(reference).values(); return this; } else { throw new IllegalArgumentException(); } } /** * Returns <code>true</code> if the specified factor is consistent * with this builder, that is, prvs from the factor are the parent + * context PRVs, and the parent is the first element of the list. * * @param f The factor to evaluate */ private boolean isConsistent(Factor f) { List<Prv> varsFromBuilder = Lists.union(Lists.listOf(p), ctxt); return varsFromBuilder.equals(f.variables()); } private boolean needsReordering(Factor f) { List<Prv> varsFromBuilder = Lists.union(Lists.listOf(p), ctxt); List<Prv> varsFromFactor = f.variables(); boolean sameElements = Lists.sameElements(varsFromBuilder, varsFromFactor); boolean differentOrder = !varsFromBuilder.equals(varsFromFactor); return sameElements && differentOrder; } /** * Sets factor values for this builder. * * @param v A list of doubles * @return This builder updated with the specified values */ public AggParfactorBuilder values(double ... v) { values.clear(); for (double d : v) { values.add(BigDecimal.valueOf(d)); } return this; } /** * Sets factor values for this builder * * @param v A list of {@link BigDecimal} * @return This builder updated with the specified values */ public AggParfactorBuilder values(List<BigDecimal> v) { values = Lists.listOf(v); return this; } /** * Sets the child PRV. Used only internally. * * @param c The child PRV * @return This builder with the new child PRV */ AggParfactorBuilder child(Prv c) { this.c = c; return this; } /** * Sets the list of context PRVs. * @param contextVars A list of context PRVs * @return This builder with the list of context PRVs. */ public AggParfactorBuilder context(List<Prv> contextVars) { ctxt = Lists.listOf(contextVars); return this; } /** * Sets the list of context PRVs. * @param contextVars A list of context PRVs * @return This builder with the list of context PRVs. */ public AggParfactorBuilder context(Prv ... contextVars) { ctxt = Lists.listOf(Arrays.asList(contextVars)); return this; } @Override public AggParfactor build() { // Cannot simplify logical variables here because in aggregation // parfactors this operation may result in StdParfactor return new AggParfactor(this); } /** * Returns the factor defined by PRVs and values in this builder. * <p> * If no values were set, returns a constant factor on parent + context * PRVs, otherwise tries to build the factor on parent + context PRVs * using the values given by {@link #values()}. * </p> * @return The factor defined by PRVs and values in this builder. * @throws IllegalStateException */ private Factor getFactor() throws IllegalStateException { Factor factor; List<Prv> variables = Lists.listOf(p); variables.addAll(ctxt); if (values.isEmpty()) { factor = ConstantFactor.getInstance(variables); } else { try { factor = StdFactor.getInstance("", variables, values); } catch (IllegalArgumentException e) { throw new IllegalStateException(); } } return factor; } } /* ************************************************************************ * Auxiliary classes * ************************************************************************/ /** * This class encapsulates the algorithm to verify if this parfactor is * splittable on a specified substitution. */ private class Split { private final SubstitutionType substitution; private Split(LogicalVariable replaced, Term replacement) { // I think there should a better way to do this if (replaced.equals(extraVar)) { if (replacement.isConstant()) { Constant c = (Constant) replacement; substitution = new ExtraConstant(c); } else { LogicalVariable x = (LogicalVariable) replacement; substitution = new ExtraVariable(x); } } else { if (replacement.equals(extraVar)) { substitution = new VariableExtra(replaced); } else if (replacement.isConstant()) { Constant c = (Constant) replacement; substitution = new VariableConstant(replaced, c); } else { LogicalVariable x = (LogicalVariable) replacement; substitution = new VariableVariable(replaced, x); } } } /** * Returns <code>true</code> if this parfactor can be split in this * substitution. * * @return <code>true</code> if this parfactor can be split in this * substitution, <code>false</code> otherwise. */ private boolean isValid() { Constraint c = substitution.toInequalityConstraint(); boolean isNotInConstraints = !constraints().contains(c); boolean isValid = substitution.isValid(); return isNotInConstraints && isValid; } /** * Represents substitutions of the type A/t, where A is the extra * logical variable from the parent's PRV and t is a constant. */ private class ExtraConstant implements SubstitutionType { private final Constant constant; private ExtraConstant(Constant c) { this.constant = c; } @Override public boolean isValid() { return extraVar.population().contains(constant); } @Override public Constraint toInequalityConstraint() { Term a = StdLogicalVariable.getInstance(extraVar); Term t = Constant.getInstance(constant); return InequalityConstraint.getInstance(a, t); } } /** * Represents substitutions of the type A/X, where A is the extra * logical variable from the parent's PRV and X is a logical variable. */ private class ExtraVariable implements SubstitutionType { private final LogicalVariable var; private ExtraVariable(LogicalVariable x) { this.var = x; } @Override public boolean isValid() { List<LogicalVariable> param = parent.parameters(); param.remove(extraVar); return param.contains(var); } @Override public Constraint toInequalityConstraint() { Term a = StdLogicalVariable.getInstance(extraVar); Term x = StdLogicalVariable.getInstance(var); return InequalityConstraint.getInstance(a, x); } } /** * Represents substitutions of the type X/A, where A is the extra * logical variable from the parent's PRV and X is a logical variable. */ private class VariableExtra implements SubstitutionType { private final LogicalVariable var; private VariableExtra(LogicalVariable x) { this.var = x; } @Override public boolean isValid() { List<LogicalVariable> param = parent.parameters(); param.remove(extraVar); return param.contains(var); } @Override public Constraint toInequalityConstraint() { Term a = StdLogicalVariable.getInstance(extraVar); Term x = StdLogicalVariable.getInstance(var); return InequalityConstraint.getInstance(x, a); } } /** * Represents substitutions of the type X/t, where X is a logical * variable different from parent PRV's extra variable and * t is a constant. */ private class VariableConstant implements SubstitutionType { private final LogicalVariable var; private final Constant constant; private VariableConstant(LogicalVariable x, Constant c) { this.var = x; this.constant = c; } @Override public boolean isValid() { return child.parameters().contains(var) && var.population().contains(constant); } @Override public Constraint toInequalityConstraint() { Term x = StdLogicalVariable.getInstance(var); Term t = Constant.getInstance(constant); return InequalityConstraint.getInstance(x, t); } } /** * Represents substitutions of the type X/Y, where X is a logical * variable different from parent PRV's extra variable and * Y is a logical variable in the same condition as X. */ private class VariableVariable implements SubstitutionType { private final LogicalVariable x; private final LogicalVariable y; private VariableVariable(LogicalVariable x, LogicalVariable y) { this.x = x; this.y = y; } @Override public boolean isValid() { return child.parameters().contains(x) && child.parameters().contains(y); } @Override public Constraint toInequalityConstraint() { Term x1 = StdLogicalVariable.getInstance(x); Term y1 = StdLogicalVariable.getInstance(y); return InequalityConstraint.getInstance(x1, y1); } } } /** * This class represents possible types of substitutions. There are five: * A/c, A/X, X/A, X/c, X/Y, where A is the extra logical variable in * parent PRV, X and Y are logical variables different from A and c is * a constant. */ private interface SubstitutionType { /** * Returns <code>true</code> if this parfactor can be split in this * substitution. * * @return <code>true</code> if this parfactor can be split in this * substitution, <code>false</code> otherwise. */ boolean isValid(); /** * Returns a {@link InequalityConstraint} based on terms from this * substitution. * * @return a {@link InequalityConstraint} based on terms from this * substitution. */ Constraint toInequalityConstraint(); } /** * This class encapsulates the splitting algorithm */ private class Splitter { private final SplitterType splitter; private Splitter(AggregationParfactor agg, Substitution s) { if (s.has(extraVar)) { splitter = new SplitterInvolvingExtra(agg, s); } else { splitter = new SplitterWithoutExtra(agg, s); } } private SplitResult split() { return splitter.split(); } private class SplitterInvolvingExtra implements SplitterType { private Substitution substitution; private AggregationParfactor parfactorToSplit; private Prv auxChild; private SplitterInvolvingExtra(AggregationParfactor agg, Substitution s) { substitution = s; parfactorToSplit = agg; setAuxChild(); } private void setAuxChild() { Prv child = parfactorToSplit.child(); auxChild = child.rename(child.name() + "'"); } @Override public SplitResult split() { return SplitResult.getInstance(result(), residue()); } private Parfactor residue() { Constraint c = substitution.first().toInequalityConstraint(); return new AggParfactorBuilder(parfactorToSplit).constraint(c) .child(auxChild).build() .simplifyLogicalVariables(); } private Parfactor result() { Set<Constraint> constraints = Sets.apply(substitution, parfactorToSplit.constraints()); List<Prv> prvs = setPrvs(); List<BigDecimal> values = setValues(prvs); return new StdParfactorBuilder().constraints(constraints) .variables(prvs).values(values).build() .simplifyLogicalVariables(); } private List<Prv> setPrvs() { List<Prv> vars = Lists.listOf(parfactorToSplit.context()); vars.add(0, parfactorToSplit.parent().apply(substitution)); vars.add(auxChild); vars.add(parfactorToSplit.child()); return vars; } private List<BigDecimal> setValues(List<Prv> prvs) { List<BigDecimal> values = new ArrayList<BigDecimal>(); Factor newStructure = StdFactor.getInstance(prvs); for (Tuple<? extends RangeElement> tuple : newStructure) { RangeElement p = tuple.get(0); RangeElement cAux = tuple.get(tuple.size() - 2); RangeElement c = tuple.get(tuple.size() - 1); if (apply(operator, p, cAux).equals(c)) { values.add(correctedValue(p)); } else { values.add(BigDecimal.ZERO); } } return values; } private BigDecimal correctedValue(RangeElement pVal) { Tuple<RangeElement> t = Tuple.getInstance(pVal); BigDecimal base = parfactorToSplit.factor().getValue(t); Prv p = replaceExtra(); int rp = p.groundSetSize(constraintsNotOnExtra); int rc = parfactorToSplit.child().groundSetSize(constraintsNotOnExtra); return MathUtils.pow(base, rp, rc); } private Prv replaceExtra() { Binding b = Binding.getInstance(extraVar, extraVar.population().individualAt(0)); Substitution s = Substitution.getInstance(b); Prv p = parfactorToSplit.parent().apply(s); return p; } } private class SplitterWithoutExtra implements SplitterType { private Substitution substitution; private AggregationParfactor parfactorToSplit; private SplitterWithoutExtra(AggregationParfactor agg, Substitution s) { substitution = s; parfactorToSplit = agg; } @Override public SplitResult split() { return SplitResult.getInstance(result(), residue()); } private Parfactor residue() { Constraint c = substitution.first().toInequalityConstraint(); return new AggParfactorBuilder(parfactorToSplit).constraint(c).build() .simplifyLogicalVariables(); } private Parfactor result() { return parfactorToSplit.apply(substitution) .simplifyLogicalVariables(); } } } /** * This class represents possible types of splits. There two of them: * split involving parent's extra logical variable and split not involving * the extra variable. * */ private interface SplitterType { public SplitResult split(); } /** * This class encapsulates multiplication algorithm. */ private class Multiplier { private final Parfactor operand; private final AggregationParfactor multiplicand; private Multiplier(AggregationParfactor thiz, Parfactor other) { multiplicand = thiz; operand = other; } private Parfactor multiply() { Factor f = multiplicand.factor().multiply(operand.factor()); Parfactor product = new AggParfactorBuilder(multiplicand).factor(f).build(); return product; } } /** * This class encapsulates sum out algorithm. * <p> * The conversion can be made only if the set of all constraints in * this aggregation parfactor is in the normal form. * </p> * <p> * The conversion results in two parfactors, one of them involving a * counting formula. This method returns a list of the resulting parfactors * in the following order: the first does involves counting formulas and * the second does not. * </p> */ private class Eliminator { private final AggregationParfactor parfactor; private final StdParfactorBuilder builder; private Eliminator(AggregationParfactor ag) { this.parfactor = ag; this.builder = new StdParfactorBuilder(); } private Parfactor eliminate() { Set<Constraint> constraints = parfactor.constraints(); Factor factor = setFactor(); Parfactor result = builder.constraints(constraints) .factor(factor).build(); if (childHasParameterNotInParent()) { LogicalVariable extraParameter = getExtraParameterFromChild(); result = result.count(extraParameter); } return result; } private Factor setFactor() { Factor current = getBase(); int domainSize = parfactor.extraVariable() .numberOfIndividualsSatisfying(parfactor.constraintsOnExtra()); String binSize = Integer.toBinaryString(domainSize); for (int k = 1; k < binSize.length(); k++) { Factor previous = StdFactor.getInstance(current); for (Tuple<RangeElement> x : previous) { BigDecimal sum; if (binSize.charAt(k) == '0') { sum = getDoubleComposition(previous, x); } else { sum = getTripleComposition(previous, x); } Tuple<RangeElement> xTuple = Tuple.getInstance(x); current = current.set(xTuple, sum); } } return current; } private BigDecimal getDoubleComposition(Factor factor, Tuple<RangeElement> x) { int childIndex = 0; RangeElement childValue = x.get(childIndex); BigDecimal sum = BigDecimal.ZERO; List<RangeElement> childRange = parfactor.child().range(); for (RangeElement y : childRange) { Tuple<RangeElement> yTuple = x.set(childIndex, y); for (RangeElement z : childRange) { Tuple<RangeElement> zTuple = x.set(childIndex, z); if (apply(parfactor.operator(), y, z).equals(childValue)) { sum = sum.add(factor.getValue(yTuple).multiply(factor.getValue(zTuple), MathUtils.CONTEXT), MathUtils.CONTEXT); } } } return sum; } private BigDecimal getTripleComposition(Factor factor, Tuple<RangeElement> x) { int childIndex = 0; RangeElement childValue = x.get(childIndex); BigDecimal sum = BigDecimal.ZERO; List<RangeElement> childRange = parfactor.child().range(); for (RangeElement y : childRange) { Tuple<RangeElement> yTuple = x.set(childIndex, y); for (RangeElement z : childRange) { Tuple<RangeElement> zTuple = x.set(childIndex, z); for (RangeElement w : childRange) { Tuple<RangeElement> wTuple = x.set(childIndex, w); if (apply(parfactor.operator(), w, y, z).equals(childValue)) { BigDecimal fw = parfactor.factor().getValue(wTuple); BigDecimal fy = factor.getValue(yTuple); BigDecimal fz = factor.getValue(zTuple); sum = sum.add(fw.multiply(fy, MathUtils.CONTEXT).multiply(fz, MathUtils.CONTEXT), MathUtils.CONTEXT); } } } } return sum; } /** * Builds the base factor F0 */ private Factor getBase() { List<Prv> prvs = new ArrayList<Prv>(); prvs.add(parfactor.child()); prvs.addAll(parfactor.context()); Factor tempBase = ConstantFactor.getInstance(prvs); int size = parfactor.child().range().size(); List<BigDecimal> vals = new ArrayList<BigDecimal>(size); for (Tuple<RangeElement> tuple : tempBase) { RangeElement childValue = tuple.get(0); if (parfactor.parent().range().contains(childValue)) { vals.add(parfactor.factor().getValue(tuple)); } else { vals.add(BigDecimal.ZERO); } } return StdFactor.getInstance("", prvs, vals); } /** * Returns <code>true</code> if the child PRV has exactly one extra * parameter that is not present in parameters from parent PRV. * * @return <code>true</code> if |param(c) \ param(p)| = 1, * <code>false</code> otherwise. */ private boolean childHasParameterNotInParent() { List<LogicalVariable> parentParam = parfactor.parent().parameters(); List<LogicalVariable> childParam = parfactor.child().parameters(); List<LogicalVariable> difference = Lists.difference(childParam, parentParam); return (difference.size() == 1); } /** * Returns the logical variable in the child PRV that is not present in * the parent PRV. * * @return The logical variable in the child PRV that is not present in * the parent PRV. */ private LogicalVariable getExtraParameterFromChild() { List<LogicalVariable> difference = Lists.difference( parfactor.child().parameters(), parfactor.parent().parameters()); return difference.get(0); } } /** * This class encapsulates the algorithm to convert aggregation parfactors * into standard parfactors. */ private class Converter { private final AggregationParfactor ap; /** * Creates an instance of converter. * @param ap The aggregation parfactor to convert to standard * parfactors. */ private Converter(AggregationParfactor ap) { this.ap = ap; } /** * Returns a list of standard parfactors whose product is * equivalent to this aggregation parfactor. */ private Distribution convert() { Parfactor parent = getParfactorOnParent(); Parfactor child = getParfactorOnChild(); Distribution dist = StdDistribution.of(parent, child); return dist; } /** * Returns the parfactor involving the parent PRV. */ private Parfactor getParfactorOnParent() { List<Prv> vars = ap.context(); vars.add(0, ap.parent()); Parfactor parent = new StdParfactorBuilder() .constraints(ap.constraints())//.variables(vars) .factor(ap.factor()).build(); return parent; } /** * Returns the parfactor involving a counting formula on the parent * and the extra logical variable. */ private Parfactor getParfactorOnChild() { List<Prv> vars = getPrvsForParfactorOnChild(); Factor structure = ConstantFactor.getInstance(vars); List<BigDecimal> vals = new ArrayList<BigDecimal>(); for (Tuple<RangeElement> tuple : structure) { RangeElement histogram = tuple.get(0); RangeElement condensedHistogram = histogram.apply(ap.operator()); RangeElement childValue = tuple.get(1); if (condensedHistogram.equals(childValue)) { vals.add(BigDecimal.ONE); } else { vals.add(BigDecimal.ZERO); } } Parfactor child = new StdParfactorBuilder() .constraints(ap.constraintsNotOnExtra()) .variables(vars).values(vals).build(); return child; } /** * Counts the extra variable from parent's PRV */ private Prv countParent() { return CountingFormula.getInstance(ap.extraVariable(), ap.parent(), ap.constraintsOnExtra()); } /** * Builds the list of PRVs for the parfactor on child PRV. The order * is counted, child, context variables. */ private List<Prv> getPrvsForParfactorOnChild() { Prv counted = countParent(); List<Prv> vars = new ArrayList<Prv>(ap.context().size() + 2); vars.add(counted); vars.add(ap.child()); vars.addAll(ap.context()); return vars; } } // TODO if you like spaghetti. here it is private class Simplifier { private Parfactor simplified; private Set<Constraint> unaryConstraints; private LogicalVariable extraVariable; private Simplifier(AggregationParfactor parfactor) { this.unaryConstraints = new HashSet<Constraint>(); setSimplified(parfactor); this.extraVariable = parfactor.extraVariable(); } /** * Sets the simplified parfactor with the specified parfactor, updating * the set of unary constraints. */ private void setSimplified(Parfactor parfactor) throws IllegalArgumentException { if (parfactor == null) { throw new IllegalArgumentException(); } simplified = parfactor; unaryConstraints.clear(); for (Constraint constraint : simplified.constraints()) { if (constraint.isUnary()) { unaryConstraints.add(constraint); } } } /** * Replaces all logical variable constrained to a single individual * with this individual */ private Parfactor simplify() { 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); if (logicalVariable.equals(extraVariable)) { return simplifyAggregation(sub).simplifyLogicalVariables(); } else { queue.addAll(variablesInBinaryConstraintsInvolving(logicalVariable)); setSimplified(simplified.apply(sub)); } break; default: break; } } // stupid for (LogicalVariable lv : logicalVariables()) { int populationSize = lv.numberOfIndividualsSatisfying(unaryConstraints); switch (populationSize) { case 0: return StdParfactor.getInstance(); case 1: Substitution sub = getSubstitution(lv); if (lv.equals(extraVariable)) { return simplifyAggregation(sub).simplifyLogicalVariables(); } else { setSimplified(simplified.apply(sub)); } break; default: break; } } return simplified; } /** * Add logical variables from constraints from parfactor to a queue */ private LinkedList<LogicalVariable> getVariablesInConstraints() { Set<LogicalVariable> buffer = new HashSet<LogicalVariable>(); for (Constraint c : simplified.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>(simplified.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; } /// buiuuuuuuuu private Parfactor simplifyAggregation(Substitution sub) { /* * When the extra variable is constrained to a single individual * there is no aggregation needed. */ List<Prv> parentChild = Lists.listOf(parent(), child()); Factor newStructure = ConstantFactor.getInstance(parentChild); List<BigDecimal> values = new ArrayList<BigDecimal>(); for (Tuple<RangeElement> tuple : newStructure) { if (tuple.get(0).equals(tuple.get(1))) { values.add(BigDecimal.ONE); } else { values.add(BigDecimal.ZERO); } } AggregationParfactor ap = (AggregationParfactor) simplified; Parfactor result = new StdParfactorBuilder() .constraints(ap.constraintsNotOnExtra()) .variables(parentChild).values(values).build(); return result; // // casting aggregation parfactor // AggregationParfactor ap = (AggregationParfactor) simplified; // // // list with parent, child and context variables // List<Prv> parentChildContext = Lists.union(ap.prvs(), ap.context()); // parentChildContext = Lists.apply(sub, parentChildContext); // // // new factor structure after simplification // Factor newStructure = ConstantFactor.getInstance(parentChildContext); // // // lets fill the array of factor values // List<BigDecimal> values = new ArrayList<BigDecimal>(); // for (Tuple<RangeElement> tuple : newStructure) { // if (tuple.get(0).equals(tuple.get(1))) { // // correction factors // Prv parent = parentChildContext.get(0); // int rp = parent.groundSetSize(ap.constraintsNotOnExtra()); // Prv child = parentChildContext.get(1); // int rc = child.groundSetSize(ap.constraintsNotOnExtra()); // // gets the value from old factor by removing the child // BigDecimal value = ap.factor().getValue(tuple.remove(1)); // BigDecimal correctedValue = MathUtils.pow(value, rp, rc); // values.add(correctedValue); // } else { // values.add(BigDecimal.ZERO); // } // } // Parfactor result = new StdParfactorBuilder() // .constraints(ap.constraintsNotOnExtra()) // .variables(parentChildContext).values(values).build(); // return result; } } /* ************************************************************************ * Constructors * ************************************************************************/ /** * Creates an AggParfactor using the specified builder. * * @param builder A {@link AggParfactorBuilder} */ private AggParfactor(AggParfactorBuilder builder) { this.parent = builder.p; this.child = builder.c; this.context = builder.ctxt; this.factor = builder.getFactor(); this.operator = builder.op; this.constraintsNotOnExtra = builder.constraintsNotOnExtra; this.constraintsOnExtra = builder.constraintsOnExtra; this.extraVar = builder.lv; } /* ************************************************************************ * Getters * ************************************************************************/ @Override public Set<Constraint> constraints() { return Sets.union(constraintsNotOnExtra, constraintsOnExtra); } @Override public Set<Constraint> constraintsOnExtra() { return new HashSet<Constraint>(constraintsOnExtra); } @Override public Set<Constraint> constraintsNotOnExtra() { return new HashSet<Constraint>(constraintsNotOnExtra); } @Override public Factor factor() { return factor; } @Override public Set<LogicalVariable> logicalVariables() { List<LogicalVariable> variables = Lists.union(child.parameters(), parent.parameters()); return new HashSet<LogicalVariable>(variables); } /** * Returns a list containing the parent PRV, the child PRV and context * PRVs, in this order. The order of context PRVs will be same as defined * when constructing this parfactor. */ @Override public List<Prv> prvs() { List<Prv> prvs = new ArrayList<Prv>(context.size() + 2); prvs.add(parent); prvs.add(child); prvs.addAll(context); return prvs; } @Override public Prv parent() { return StdPrv.getInstance(parent); } @Override public Prv child() { return StdPrv.getInstance(child); } @Override public List<Prv> context() { return Lists.listOf(context); } @Override public Operator<? extends RangeElement> operator() { return operator; } @Override public LogicalVariable extraVariable() { return StdLogicalVariable.getInstance(extraVar); } @Override public int size() { Set<LogicalVariable> vars = logicalVariables(); vars.remove(extraVar); int size = 1; for (LogicalVariable lv : vars) { size = size * lv.numberOfIndividualsSatisfying(constraintsNotOnExtra); } return size; } @Override public Parfactor apply(Substitution s) { Set<Constraint> substitutedConstraints = Sets.apply(s, constraints());//applyToConstraints(s); Factor substitutedFactor = factor.apply(s); Prv substitutedParent = parent.apply(s); Prv substututedChild = child.apply(s); List<Prv> substitutedContext = Lists.apply(s, context); return new AggParfactorBuilder(substitutedParent, substututedChild, operator).constraints(substitutedConstraints) .context(substitutedContext).factor(substitutedFactor).build(); } @Override public boolean contains(Prv prv) { return (prv.equals(parent) || prv.equals(child) || context.contains(prv)); } @Override public boolean isConstant() { boolean hasNoConstraints = constraints().isEmpty(); boolean hasConstantFactor = factor.isConstant(); return hasNoConstraints && hasConstantFactor; } /** * Returns <code>false</code>. * <p> * A logical variable cannot be eliminated from * {@link AggregationParfactor}s. * </p> */ @Override public boolean isCountable(LogicalVariable lv) { return false; } /** * Returns <code>false</code>. * <p> * {@link AggregationParfactor}s do not contain {@link CountingFormula}s. * </p> */ @Override public boolean isExpandable(Prv cf, Substitution s) { return false; } @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(); } @Override public boolean isSplittable(Substitution s) { boolean isSplittable = false; if (s.size() != 1) { isSplittable = false; } else { Binding bind = s.first(); Split split = new Split(bind.firstTerm(), bind.secondTerm()); isSplittable = split.isValid(); } return isSplittable; } @Override public boolean isEliminable(Prv prv) { /* * There is one condition not checked here: no other parfactor from the * set has PRVs representing random variables from ground(p). */ // param(p(...A...)) \ {A} Set<LogicalVariable> parentParameters = new HashSet<LogicalVariable>(parent.parameters()); parentParameters.remove(extraVar); // param(c(...E...)) \ {E} Set<LogicalVariable> childParameters = new HashSet<LogicalVariable>(child.parameters()); List<LogicalVariable> extraVariablesInChild = child.parameters(); extraVariablesInChild.removeAll(parent.parameters()); if (extraVariablesInChild.size() == 1) { LogicalVariable e = extraVariablesInChild.get(0); childParameters.remove(e); } boolean oneExtraForEach = childParameters.equals(parentParameters); return isInNormalForm() && oneExtraForEach; } /** * 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() { 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; } /** * Throws {@link UnsupportedOperationException}. */ @Override public Parfactor count(LogicalVariable lv) { throw new UnsupportedOperationException("Aggregation Parfactors are not countable"); } /** * Throws {@link UnsupportedOperationException}. */ @Override public Parfactor expand(Prv cf, Term t) { throw new UnsupportedOperationException("Aggregation Parfactors are not expandable"); } @Override public Parfactor multiply(Parfactor other) { // TODO check if multiplication is valid Multiplier result = new Multiplier(this, other); return result.multiply(); } @Override public Parfactor multiplicationHelper(Parfactor other) { /* * I know that 'other' is a StdParfactor, because * AggregationParfactor.multiply() does not call this helper method. */ return multiply(other); } @Override public SplitResult splitOn(Substitution s) throws IllegalArgumentException { Splitter result = new Splitter(this, s); return result.split(); } @Override public Parfactor sumOut(Prv prv) { // TODO check if prv is parent Eliminator eliminator = new Eliminator(this); Parfactor result = eliminator.eliminate(); return result; } @Override public Distribution toStdParfactors() { Converter converter = new Converter(this); Distribution result = converter.convert(); return result; } @Override public Parfactor simplifyLogicalVariables() { Simplifier parfactor = new Simplifier(this); return parfactor.simplify(); } @Override public void accept(ParfactorVisitor visitor, Parfactor p) { /* * As p has an unknown type, it is necessary to call its accept() * method. JVM will infer its runtime type and call the appropriate * method, which is one of the methods with the signature below. */ p.accept(visitor, this); } @Override public void accept(ParfactorVisitor visitor, StdParfactor p) { /* * I know this parfactor is an AggregationParfactor, and that p is a * StdParfactor, thus types for visit() are defined. */ visitor.visit(this, p); } @Override public void accept(ParfactorVisitor visitor, AggregationParfactor p) { /* * I know this parfactor is an AggregationParfactor, and that p is a * AggregationParfactor, thus types for visit() are defined. */ visitor.visit(this, p); } /* * Life savior: * http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ207 */ public <T extends RangeElement> T apply(Operator<T> op, RangeElement e1, RangeElement e2) { T t1 = op.getTypeArgument().cast(e1); T t2 = op.getTypeArgument().cast(e2); return op.applyOn(t1, t2); } public <T extends RangeElement> T apply(Operator<T> op, RangeElement e1, RangeElement e2, RangeElement e3) { T t1 = op.getTypeArgument().cast(e1); T t2 = op.getTypeArgument().cast(e2); T t3 = op.getTypeArgument().cast(e3); return op.applyOn(t1, t2, t3); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((child == null) ? 0 : child.hashCode()); result = prime * result + ((constraintsNotOnExtra == null) ? 0 : constraintsNotOnExtra .hashCode()); result = prime * result + ((constraintsOnExtra == null) ? 0 : constraintsOnExtra .hashCode()); result = prime * result + ((extraVar == null) ? 0 : extraVar.hashCode()); result = prime * result + ((factor == null) ? 0 : factor.hashCode()); result = prime * result + ((operator == null) ? 0 : operator.hashCode()); result = prime * result + ((parent == null) ? 0 : parent.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof AggParfactor)) { return false; } AggParfactor other = (AggParfactor) obj; if (child == null) { if (other.child != null) { return false; } } else if (!child.equals(other.child)) { return false; } if (constraintsNotOnExtra == null) { if (other.constraintsNotOnExtra != null) { return false; } } else if (!constraintsNotOnExtra.equals(other.constraintsNotOnExtra)) { return false; } if (constraintsOnExtra == null) { if (other.constraintsOnExtra != null) { return false; } } else if (!constraintsOnExtra.equals(other.constraintsOnExtra)) { return false; } if (extraVar == null) { if (other.extraVar != null) { return false; } } else if (!extraVar.equals(other.extraVar)) { return false; } if (factor == null) { if (other.factor != null) { return false; } } else if (!factor.equals(other.factor)) { return false; } if (operator == null) { if (other.operator != null) { return false; } } else if (!operator.equals(other.operator)) { return false; } if (parent == null) { if (other.parent != null) { return false; } } else if (!parent.equals(other.parent)) { return false; } return true; } @Override public String toString() { return "\np = " + parent + ", c = " + child + ", V = " + context + ", C_A = " + constraintsOnExtra + ", C = " + constraintsNotOnExtra + "\n" + factor; } }