/* * JaamSim Discrete Event Simulation * Copyright (C) 2013 Ausenco Engineering Canada Inc. * Copyright (C) 2016 JaamSim Software Inc. * * 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 com.jaamsim.ProbabilityDistributions; import java.util.Arrays; import com.jaamsim.datatypes.DoubleVector; import com.jaamsim.input.InputErrorException; import com.jaamsim.input.Keyword; import com.jaamsim.input.Output; import com.jaamsim.input.ValueListInput; import com.jaamsim.rng.MRG1999a; import com.jaamsim.units.DimensionlessUnit; import com.jaamsim.units.Unit; import com.jaamsim.units.UserSpecifiedUnit; /** * DiscreteDistribution is a user-defined probability distribution that selects from a given list of specific values * based on a specified probability for each value. No interpolation is performed between values. * @author Harry King * */ public class DiscreteDistribution extends Distribution { @Keyword(description = "The list of discrete values that can be returned by the distribution. " + "The values can be any positive or negative and can be listed in any order. " + "No interpolation is performed between these values.", exampleList = {"6.2 10.1"}) private final ValueListInput valueListInput; @Keyword(description = "The list of probabilities corresponding to the discrete values in the ValueList. Must sum to 1.0.", exampleList = {"0.3 0.7"}) private final ValueListInput probabilityListInput; private final MRG1999a rng = new MRG1999a(); private int[] sampleCount; // number of times each index has been selected private double[] valueList; private double[] cumProbList; { valueListInput = new ValueListInput( "ValueList", "Key Inputs", null); valueListInput.setUnitType(UserSpecifiedUnit.class); valueListInput.setRequired(true); this.addInput( valueListInput); probabilityListInput = new ValueListInput( "ProbabilityList", "Key Inputs", null); probabilityListInput.setUnitType(DimensionlessUnit.class); probabilityListInput.setValidSum(1.0d, 0.001d); probabilityListInput.setValidRange(0.0d, 1.0d); probabilityListInput.setRequired(true); this.addInput( probabilityListInput); } public DiscreteDistribution() { sampleCount = new int[0]; valueList = new double[0]; cumProbList = new double[0]; } @Override public void validate() { super.validate(); // The number of entries in the ValueList and ProbabilityList inputs must match if( probabilityListInput.getValue().size() != valueListInput.getValue().size() ) { throw new InputErrorException( "The number of entries for ProbabilityList and ValueList must be equal" ); } } @Override public void earlyInit() { super.earlyInit(); rng.setSeedStream(getStreamNumber(), getSubstreamNumber()); int n = probabilityListInput.getValue().size(); sampleCount = new int[n]; // Store the values and cumulative probabilities for binary searching valueList = new double[n]; cumProbList = new double[n]; double total = 0.0d; for (int i=0; i<n; i++) { valueList[i] = valueListInput.getValue().get(i); total += probabilityListInput.getValue().get(i); cumProbList[i] = total; } cumProbList[n-1] = 1.0d; } @Override protected void setUnitType(Class<? extends Unit> specified) { super.setUnitType(specified); valueListInput.setUnitType(specified); } @Override protected double getSample(double simTime) { double rand = rng.nextUniform(); int index = -1; // Binary search the cumulative probabilities int k = Arrays.binarySearch(cumProbList, rand); if (k >= 0) index = k; else index = -k - 1; if (index < 0 || index >= valueList.length) error("Bad index returned from binary search."); sampleCount[index]++; return valueList[index]; } @Override public double getMinValue() { if (probabilityListInput.getValue() == null || valueListInput.getValue() == null) return Double.NaN; double ret = Double.POSITIVE_INFINITY; for( int i = 0; i < probabilityListInput.getValue().size(); i++ ) { if( probabilityListInput.getValue().get(i) > 0.0 ) { if( valueListInput.getValue().get(i) < ret ) { ret = valueListInput.getValue().get(i); } } } return Math.max(ret, super.getMinValue()); } @Override public double getMaxValue() { if (probabilityListInput.getValue() == null || valueListInput.getValue() == null) return Double.NaN; double ret = Double.NEGATIVE_INFINITY; for( int i = 0; i < probabilityListInput.getValue().size(); i++ ) { if( probabilityListInput.getValue().get(i) > 0.0 ) { if( valueListInput.getValue().get(i) > ret ) { ret = valueListInput.getValue().get(i); } } } return Math.min(ret, super.getMaxValue()); } @Override protected double getMean(double simTime) { if (probabilityListInput.getValue() == null || valueListInput.getValue() == null) return Double.NaN; double ret = 0.0; for( int i=0; i<probabilityListInput.getValue().size(); i++) { ret += probabilityListInput.getValue().get(i) * valueListInput.getValue().get(i); } return ret; } @Override protected double getStandardDev(double simTime) { if (probabilityListInput.getValue() == null || valueListInput.getValue() == null) return Double.NaN; double sum = 0.0; for( int i=0; i<probabilityListInput.getValue().size(); i++) { double val = valueListInput.getValue().get(i); sum += probabilityListInput.getValue().get(i) * val * val; } double mean = getMean(simTime); return Math.sqrt( sum - (mean * mean) ); } @Output( name="SampleCount", description="The number of samples selected for each value.") public DoubleVector getSampleCount(double simTime) { DoubleVector ret = new DoubleVector(sampleCount.length); for (int i=0; i<sampleCount.length; i++) { ret.add(sampleCount[i]); } return ret; } }