All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.enterprisemath.math.probability.NormalDistributionMixture Maven / Gradle / Ivy

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.Interval;
import com.enterprisemath.utils.DomainUtils;
import com.enterprisemath.utils.ValidationUtils;

/**
 * Mixture of the 1 dimensional normal distributions.
 * This is the convex combination of normal distributions.
 *
 * @author radek.hecl
 *
 */
public class NormalDistributionMixture 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, NormalDistribution 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, double mi, double sigma) {
            weights.add(weight);
            components.add(new NormalDistribution.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(NormalDistributionMixture 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, NormalDistributionMixture 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 NormalDistributionMixture build() {
            return new NormalDistributionMixture(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 NormalDistributionMixture(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");
        }
    }

    @Override
    public double getValue(Double 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(Double x) {
        return Math.log(getValue(x));
    }

    @Override
    public Double generateRandom() {
        // select the component
        double r = random.nextDouble();
        double w = 0;
        NormalDistribution 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 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 interval 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 Interval getProbableInterval(double min) {
        Interval.Builder resBuilder = new Interval.Builder();
        for (NormalDistribution component : components) {
            resBuilder.addSubInterval(component.getProbableInterval(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 NormalDistributionMixture createSignificantComponentMixture(double minWeight) {
        NormalDistributionMixture.Builder builder = new NormalDistributionMixture.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