![JAR search and dependency download from the Maven repository](/logo.png)
com.enterprisemath.math.probability.DiagonalNormalDistributionMixture Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of em-math Show documentation
Show all versions of em-math Show documentation
Advanced mathematical algorithms.
The newest version!
package com.enterprisemath.math.probability;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.enterprisemath.math.algebra.Hypercube;
import com.enterprisemath.math.algebra.Vector;
import com.enterprisemath.utils.DomainUtils;
import com.enterprisemath.utils.ValidationUtils;
/**
* Mixture of the multidimensional normal distributions where all components has diagonal covariance matrix.
* This is the convex combination of normal distributions.
*
* @author radek.hecl
*
*/
public class DiagonalNormalDistributionMixture implements ProbabilityDistribution {
/**
* Builder object.
*/
public static class Builder {
/**
* Weights for the linear combination.
*/
private List weights = new ArrayList();
/**
* Components.
*/
private List components = new ArrayList();
/**
* Adds new component.
*
* @param weight weight of this component, must be [0, 1]
* @param component component
* @return this instance
*/
public Builder addComponent(double weight, DiagonalNormalDistribution component) {
weights.add(weight);
components.add(component);
return this;
}
/**
* Adds new component.
*
* @param weight weight of this component, must be [0, 1]
* @param mi mi parameter of the component
* @param sigma sigma parameter of the component
* @return this instance
*/
public Builder addComponent(double weight, Vector mi, Vector sigma) {
weights.add(weight);
components.add(new DiagonalNormalDistribution.Builder().
setMi(mi).
setSigma(sigma).
build());
return this;
}
/**
* Adds the components with weights from another mixture.
*
* @param mixture mixture to add
* @return this instance
*/
public Builder addMixture(DiagonalNormalDistributionMixture mixture) {
for (int i = 0; i < mixture.getNumComponents(); ++i) {
addComponent(mixture.getWeights().get(i), mixture.getComponents().get(i));
}
return this;
}
/**
* Adds the components with weights from another mixture in weighted manned.
* This means all the weights of the mixture are multiplied by the specified weight.
*
* @param weight weight factor for components
* @param mixture mixture to add
* @return this instance
*/
public Builder addWeightedMixture(double weight, DiagonalNormalDistributionMixture mixture) {
for (int i = 0; i < mixture.getNumComponents(); ++i) {
addComponent(mixture.getWeights().get(i) * weight, mixture.getComponents().get(i));
}
return this;
}
/**
* Builds the result object.
*
* @return created object
*/
public DiagonalNormalDistributionMixture build() {
return new DiagonalNormalDistributionMixture(this);
}
}
/**
* Random generator for new vectors.
*/
private static final SecureRandom random = new SecureRandom();
/**
* Weights for the linear combination.
*/
private List weights;
/**
* Components.
*/
private List components;
/**
* Creates new instance.
*
* @param builder created instance
*/
public DiagonalNormalDistributionMixture(Builder builder) {
weights = DomainUtils.softCopyList(builder.weights);
components = Collections.unmodifiableList(DomainUtils.softCopyList(builder.components));
guardInvariants();
// normalize the weights
double magnitude = 0;
for (double w : weights) {
magnitude += w;
}
for (int i = 0; i < weights.size(); ++i) {
weights.set(i, weights.get(i) / magnitude);
}
weights = Collections.unmodifiableList(weights);
}
/**
* Guards this object to be consistent. Throws exception if this is not the case.
*/
private void guardInvariants() {
ValidationUtils.guardEquals(weights.size(), components.size(), "number of weights and componenets must be same");
ValidationUtils.guardPositiveInt(weights.size(), "at least one component is required");
for (int i = 0; i < weights.size(); ++i) {
ValidationUtils.guardNotNegativeDouble(weights.get(i), "weight cannot be negative");
}
int dim = components.get(0).getDimension();
for (DiagonalNormalDistribution comp : components) {
ValidationUtils.guardEquals(dim, comp.getDimension(), "all components must have same dimension");
}
}
@Override
public double getValue(Vector x) {
double res = 0;
for (int i = 0; i < getNumComponents(); ++i) {
res += weights.get(i) * components.get(i).getValue(x);
}
return res;
}
@Override
public double getLnValue(Vector x) {
return Math.log(getValue(x));
}
@Override
public Vector generateRandom() {
// select the component
double r = random.nextDouble();
double w = 0;
DiagonalNormalDistribution component = components.get(components.size() - 1);
for (int i = 0; i < getNumComponents(); ++i) {
w += weights.get(i);
if (r <= w) {
component = components.get(i);
break;
}
}
// return the random vector from the component
return component.generateRandom();
}
/**
* Returns the number of components.
*
* @return number of components
*/
public int getNumComponents() {
return weights.size();
}
/**
* Returns dimension.
*
* @return dimension
*/
public int getDimension() {
return components.get(0).getDimension();
}
/**
* Returns the list of weights.
*
* @return list of weights
*/
public List getWeights() {
return weights;
}
/**
* Returns the list of the components.
*
* @return list of the components
*/
public List getComponents() {
return components;
}
/**
* Returns hypercube with at least specified probability that random elements
* generated by this distribution will be inside.
*
* @param min minimum probability, must be in interval [0, 1)
* @return interval with at least specified probability that random elements generated by this distribution will be inside
*/
public Hypercube getProbableHypercube(double min) {
Hypercube.Builder resBuilder = new Hypercube.Builder();
for (DiagonalNormalDistribution component : components) {
resBuilder.addSubCube(component.getProbableHypercube(min));
}
return resBuilder.build();
}
/**
* Creates new mixture which contains only the significant components.
* Significant is considered the one which has weight at least same as minWeight.
* The weights of the non significant components are distributed between
* the significant ones.
*
* @param minWeight minimum weight of the component to be included in the new mixture, must be in interval [0, 1]
* @return created mixture of the significant components
*/
public DiagonalNormalDistributionMixture createSignificantComponentMixture(double minWeight) {
DiagonalNormalDistributionMixture.Builder builder = new DiagonalNormalDistributionMixture.Builder();
for (int i = 0; i < getNumComponents(); ++i) {
if (weights.get(i) >= minWeight) {
builder.addComponent(weights.get(i), components.get(i));
}
}
return builder.build();
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy