/*******************************************************************************
* 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.prv;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import br.usp.poli.takiyama.common.Constraint;
import br.usp.poli.takiyama.common.InequalityConstraint;
import br.usp.poli.takiyama.utils.Sets;
/**
* Represents the set of random variables represented by a parameterized
* random variable and a set of constraints.
* <p>
* For instance, given the parameterized random variable f(A) with
* D(A) = {a1, a2, a3} and the set of constraints C = {A ≠ a1}, the
* random variable set f(A):C equals to {f(a2), f(a3)}.
* </p>
*
* @author Felipe Takiyama
*/
public final class RandomVariableSet implements Prv {
private final Prv prv;
private final Set<Constraint> constraints;
/* ************************************************************************
* Constructors
* ************************************************************************/
/*
* What about building in a standard way?
* For instance, f(x1, B):{B!=x2} is represented as f(A,B):{A!=x2,...,A!=xn,B!=x1}.
* This simplifies comparison between sets.
*/
/**
* Creates a random variable set.
* <p>
* Constraints that do not involve parameters from the specified
* parameterized random variable are ignored.
* </p>
* <p>
* Constraints that involve two logical variables are also discarded.
* </p>
*
* @param prv A {@link Prv}
* @param constraints A set of {@link Constraint}
*/
private RandomVariableSet(Prv prv, Set<Constraint> constraints) {
this.prv = prv;
this.constraints = Sets.getInstance(constraints.size() + prv.constraints().size());
for (Constraint c : constraints) {
if (c.isUnary() && prv.contains(c.logicalVariables().iterator().next())) {
this.constraints.add(c);
}
}
// Enters this block only when PRV is a counting formula.
// I am removing binary constraints: is it a problem? Not sure...
for (Constraint c : prv.constraints()) {
if (c.isUnary()) {
this.constraints.add(c);
}
}
}
/* ************************************************************************
* Static factories
* ************************************************************************/
/**
* Static factory of {@link RandomVariableSet}. Returns an instance of
* RandomVariableSet.
* <p>
* Constraints that do not involve parameters from the specified
* parameterized random variable are not stored in the instance.
* </p>
* <p>
* Constraints that involve two logical variables are also discarded.
* </p>
* <p>
* If the parameterized random variable has no parameters, it returns
* a RandomVariableSet with the specified PRV and an empty set of
* constraints.
* </p>
*
* @param prv A {@link Prv}
* @param constraints A set of {@link Constraint}
* @return An instance of RandomVariableSet with the specified parameters
*/
public static RandomVariableSet getInstance(Prv prv, Set<Constraint> constraints) {
if (prv.parameters().size() == 0) {
return new RandomVariableSet(prv, new HashSet<Constraint>());
}
int maxNumConstraints = 0;
for (LogicalVariable lv : prv.parameters()) {
maxNumConstraints = maxNumConstraints + lv.population().size();
}
if (maxNumConstraints == constraints.size()) {
return RandomVariableSet.getInstance();
} else {
return new RandomVariableSet(prv, constraints);
}
}
/**
* Static factory of {@link RandomVariableSet}. Returns an instance of
* RandomVariableSet that is identical to the specified RandomVariableSet.
*
* @param rvs The RandomVariableSet to copy.
* @return An instance of RandomVariableSet that is identical to the
* specified RandomVariableSet.
*/
public static RandomVariableSet getInstance(RandomVariableSet rvs) {
return new RandomVariableSet(rvs.prv(), rvs.constraints());
}
/**
* Builds an empty instance of {@link RandomVariableSet}.
* <p>
* An empty instance contains an empty PRV (no parameters, nameless functor)
* and an empty set of constraints.
* </p>
*
* @return An empty instance of RandomVariableSet.
*/
public static RandomVariableSet getInstance() {
return new RandomVariableSet(StdPrv.getInstance(), new HashSet<Constraint>(0));
}
/* ************************************************************************
* Getters
* ************************************************************************/
/**
* Returns the set of constraints in this set.
*
* @return The set of constraints in this set.
*/
public Set<Constraint> constraints() {
return new HashSet<Constraint>(constraints);
}
/**
* Returns the prv bound to this set.
*
* @return The prv bound to this set.
*/
public Prv prv() {
return prv;
}
/**
* TODO reimplement
* Returns the complement set of this set.
* <br>
* <b>Attention!</b> This method is inefficient in that it depends on the
* size of the population of each parameter from the PRV.
*
* @return The complement set of this set.
*/
public RandomVariableSet complement() {
HashSet<Constraint> constraints = new HashSet<Constraint>();
for (LogicalVariable lv : prv.parameters()) {
for (Constant c : lv.individualsSatisfying(constraints)) {
constraints.add(InequalityConstraint.getInstance(lv, c));
}
}
return new RandomVariableSet(this.prv, constraints);
}
/**
* Returns the intersection of this set and the specified RandomVariableSet.
*
* @param rvSet A {@link RandomVariableSet}
* @return The intersection of this set and the specified RandomVariableSet.
*/
public RandomVariableSet intersect(RandomVariableSet rvSet) {
if (!this.prv.equals(rvSet.prv)) {
return RandomVariableSet.getInstance();
} else {
HashSet<Constraint> constraints =
new HashSet<Constraint>(this.constraints);
constraints.addAll(rvSet.constraints);
return new RandomVariableSet(this.prv, constraints);
}
}
/**
* Returns the difference between this set and the specified set.
*
* @param rvSet A {@link RandomVariableSet}
* @return The difference between this set and the specified set.
*/
public RandomVariableSet minus(RandomVariableSet rvSet) {
if (!this.prv.equals(rvSet.prv)) {
return this;
} else {
if (this.constraints.equals(rvSet.constraints)) {
return RandomVariableSet.getInstance();
} else {
RandomVariableSet r = rvSet.complement();
HashSet<Constraint> constraints =
new HashSet<Constraint>(r.constraints);
constraints.addAll(this.constraints);
return new RandomVariableSet(r.prv, constraints);
}
}
}
/**
* Returns the union of this set and the specified set.
*
* @param rvSet A {@link RandomVariableSet}
* @return The union of this set and the specified set.
*/
public Set<RandomVariableSet> union(RandomVariableSet rvSet) {
HashSet<RandomVariableSet> r = new HashSet<RandomVariableSet>(2);
if (!this.prv.equals(rvSet.prv)) {
r.add(this);
r.add(rvSet);
} else {
HashSet<Constraint> constraints = new HashSet<Constraint>();
if (this.constraints.equals(rvSet.constraints)) {
constraints.addAll(this.constraints);
} else {
constraints.addAll(this.constraints);
constraints.retainAll(rvSet.constraints);
}
r.add(new RandomVariableSet(this.prv, constraints));
}
return r;
}
/**
* Returns <code>true</code> if this set is empty, <code>false</code>
* otherwise.
* <p>
* An empty set can be created by adding all possible constraints
* involving all parameters of the associated parameterized random variable.
* </p>
* @return <code>true</code> if this set is empty, <code>false</code>
* otherwise.
*/
public boolean isEmpty() {
return prv.parameters().isEmpty() && constraints.isEmpty();
}
/**
* Returns true if this set contains the specified parameterized random
* variable.
* @param prv
* @return
*/
// public boolean contains(ParameterizedRandomVariable prv) {
// // TODO all unification process, again?
// Substitution mgu = null;
// try {
// mgu = this.prv.getMgu(prv);
// } catch (IllegalArgumentException e) {
// // firstVariable and secondVariable represent disjoint sets
// return false;
// }
// return false;
// }
// Calling from PRV instead
// /**
// * Returns true if this set represents the same set of random variables
// * from the specified counting formula.
// * @param cf The counting formula to compare
// * @return True if this set represents the same set of random variables
// * from the specified counting formula.
// */
// public boolean isEquivalent(Prv prv) {
// return (this.prv.name().equals(prv.name())
// && this.constraints.equals(prv.constraints()));
// }
@Override
public boolean isEquivalentTo(RandomVariableSet s) {
return this.equals(s);
}
@Override
public String name() {
return prv.name();
}
@Override
public List<LogicalVariable> parameters() {
return prv.parameters();
}
@Override
public List<Term> terms() {
return prv.terms();
}
@Override
public List<RangeElement> range() {
return prv.range();
}
@Override
public LogicalVariable boundVariable() {
return prv.boundVariable();
}
@Override
public int groundSetSize(Set<Constraint> constraints) {
return prv.groundSetSize(Sets.union(this.constraints, constraints));
}
@Override
public boolean contains(Term t) {
return prv.contains(t);
}
@Override
public Prv apply(Substitution s) {
return RandomVariableSet.getInstance(prv.apply(s), Sets.apply(s, constraints));
}
/**
* Throws a {@link UnsupportedOperationException}.
*/
@Override
public Prv rename(String name) {
throw new UnsupportedOperationException();
}
/**
* Throws a {@link UnsupportedOperationException}.
*/
@Override
public BigDecimal getSumOutCorrection(RangeElement e) {
throw new UnsupportedOperationException();
}
@Override
public Prv getCanonicalForm() {
// Prv canonicalPrv = prv.getCanonicalForm();
// Set<Constraint> allConstraints = Sets.union(constraints, prv.constraints());
// return RandomVariableSet.getInstance(canonicalPrv, allConstraints);
return this.prv.getCanonicalForm();
}
/* ************************************************************************
* hashCode, equals and toString
* ************************************************************************/
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof RandomVariableSet))
return false;
RandomVariableSet o = (RandomVariableSet) other;
return ((this.prv == null)
? (o.prv == null)
: (this.prv.equals(o.prv)))
&& ((this.constraints == null)
? (o.constraints == null)
: (this.constraints.equals(o.constraints)));
}
@Override
public String toString() {
StringBuilder result = new StringBuilder(this.prv.toString());
result.append(":").append(this.constraints);
return result.toString();
}
@Override
public int hashCode() {
int result = 17;
result = 31 + result + constraints.hashCode();
result = 31 + result + prv.hashCode();
return result;
}
}