/*******************************************************************************
* 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.common;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
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;
public final class StdMarginal implements Marginal {
private final Distribution parfactors;
private final RandomVariableSet preservable;
/* ************************************************************************
* Builder
* ************************************************************************/
public static final class StdMarginalBuilder implements Builder<StdMarginal> {
private Set<Parfactor> parfactors;
private RandomVariableSet preservable;
/**
* Constructs an empty builder with the specified capacity.
* @param capacity
*/
public StdMarginalBuilder(int capacity) {
parfactors = Sets.getInstance(capacity);
preservable = RandomVariableSet.getInstance();
}
/**
* Constructs an empty marginal. It is equivalent to an empty set.
*/
public StdMarginalBuilder() {
this(0);
}
/**
* Sets the distribution from this marginal to the specified parfactors.
* <p>
* At least one parfactor must be specified.
* Parfactor's order is not preserved.
* </p>
*
* @param first The first (mandatory) parfactor
* @param remaining The remaining parfactors
* @return This builder with the specified parfactors.
*/
public StdMarginalBuilder parfactors(Parfactor first, Parfactor ... remaining) {
parfactors.add(first);
parfactors.addAll(Arrays.asList(remaining));
return this;
}
/**
* Sets the distribution from this marginal to the specified parfactors.
* <p>
* At least one parfactor must be specified, otherwise nothing is done.
* Parfactor's order is not preserved.
* </p>
*
* @param parfactors The set of parfactors that define this marginal
* @return This builder with the specified parfactors.
*/
public StdMarginalBuilder parfactors(Set<Parfactor> parfactors) {
if (parfactors != null && parfactors.size() > 0) {
this.parfactors.addAll(parfactors);
}
return this;
}
/**
* Sets the distribution from this marginal to the parfactors in the
* specified distribution.
* <p>
* At least one parfactor must be in the distribution, otherwise
* nothing is done.
* Parfactor's order is not preserved.
* </p>
*
* @param distribution The distribution that define this marginal
* @return This builder with the specified parfactors.
*/
public StdMarginalBuilder parfactors(Distribution distribution) {
return parfactors(distribution.toSet());
}
/**
* Adds parfactors from the specified marginal to this builder.
* <p>
* The random variable set to be preserved is set to be the same as the
* specified marginal.
* </p>
*
* @param marginal
* @return This builder with parfactors from the specified marginal
* added.
*/
public StdMarginalBuilder add(Marginal marginal) {
parfactors.addAll(marginal.distribution().toSet());
preservable = marginal.preservable();
return this;
}
/**
* Adds the specified parfactor to builder's distribution and returns
* the modified builder.
*
* @param parfactor The parfactor to add.
* @return This builder with the specified parfactor added
*/
public StdMarginalBuilder add(Parfactor parfactor) {
parfactors.add(parfactor);
return this;
}
/**
* Adds the specified random variable set to this builder and returns
* the modified builder.
*
* @param rvSet The random variable set to preserve (that is, the one
* that will not be eliminated)
* @return This builder with the specified random variable set added
*/
public StdMarginalBuilder preservable(RandomVariableSet rvSet) {
preservable = rvSet;
return this;
}
/**
* Replaces the specified old parfactor with the specified new parfactor
* and returns the modified builder.
* <p>
* If the specified old parfactor does not exist, returns this builder
* unmodified.
* </p>
*
* @param oldOne The parfactor to be replaced
* @param newOne The parfactor that replaces the old one
* @return This builder with the old parfactor replaced with the new
* one.
*/
public StdMarginalBuilder replace(Parfactor oldOne, Parfactor newOne) {
if (parfactors.remove(oldOne)) {
parfactors.add(newOne);
}
return this;
}
/**
* Removes the specified parfactor from the set of parfactors in this
* builder. If the parfactor does not exist, returns this builder
* unmodified.
*
* @param removable The parfactor to remove
* @return This builder with the specified parfactor removed
*/
public StdMarginalBuilder remove(Parfactor removable) {
parfactors.remove(removable);
return this;
}
/**
* @return The set of parfactors from this builder as a distribution.
*/
Distribution distribution() {
return StdDistribution.of(parfactors);
}
@Override
public StdMarginal build() {
return new StdMarginal(this);
}
}
/* ************************************************************************
* Constructors
* ************************************************************************/
private StdMarginal(StdMarginalBuilder builder) {
this.parfactors = builder.distribution();
this.preservable = builder.preservable;
}
/* ************************************************************************
* Getters
* ************************************************************************/
@Override
public Set<RandomVariableSet> eliminables() {
Set<RandomVariableSet> eliminables = new HashSet<RandomVariableSet>();
for (Parfactor p : parfactors) {
for (Prv prv : p.prvs()) {
if (!Prvs.areDisjoint(prv, preservable.prv())) {
/*
* If the PRV from the parfactor represents a set of random
* variables that is not disjoint with the set of random
* variables represented by 'preservable', then it cannot
* be eliminated
*/
continue;
} else {
RandomVariableSet s = RandomVariableSet.getInstance(prv, p.constraints());
eliminables.add(s);
}
}
}
return eliminables;
}
@Override
public RandomVariableSet preservable() {
return preservable;
}
@Override
public Distribution distribution() {
return StdDistribution.of(parfactors);
}
@Override
public boolean isEmpty() {
return parfactors.isEmpty();
}
@Override
public int size() {
return parfactors.size();
}
@Override
public Iterator<Parfactor> iterator() {
return parfactors.iterator();
}
/* ************************************************************************
* hashCode, equals and toString
* ************************************************************************/
/**
* Compares objects using their hash code.
*/
private static class HashComparator<T> implements Comparator<T> {
@Override
public int compare(T o1, T o2) {
if (o1.equals(o2)) {
return 0;
}
int hash1 = o1.hashCode();
int hash2 = o2.hashCode();
if (hash1 > hash2) {
return 1;
} else {
return -1;
}
}
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("\nEliminables PRVs:\n");
result.append(Sets.sort(eliminables(), new HashComparator<RandomVariableSet>()));
result.append("\nDistribution:\n");
result.append(Sets.sort(distribution().toSet(), new HashComparator<Parfactor>()));
return result.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((parfactors == null) ? 0 : parfactors.hashCode());
result = prime * result
+ ((preservable == null) ? 0 : preservable.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof StdMarginal)) {
return false;
}
StdMarginal other = (StdMarginal) obj;
if (parfactors == null) {
if (other.parfactors != null) {
return false;
}
} else if (!parfactors.equals(other.parfactors)) {
return false;
}
if (preservable == null) {
if (other.preservable != null) {
return false;
}
} else if (!preservable.equals(other.preservable)) {
return false;
}
return true;
}
}