/**
* Copyright (C) 2006, Laboratorio di Valutazione delle Prestazioni - Politecnico di Milano
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package jmt.gui.exact;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import jmt.analytical.SolverAlgorithm;
import jmt.analytical.SolverMultiClosedAMVA;
import jmt.framework.data.ArrayUtils;
import jmt.gui.common.definitions.CommonModel;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* An object grouping all the data describing a system.
* <br><br>
* WARNING:
* ACCESS TO THIS OBJECT MUST BE IMPLEMENTED IN A TRANSACTION-LIKE SYNCHRONIZED WAY!
*
* @author alyf (Andrea Conti), Stefano Omini, Bertoli Marco
*
* @author Ashanka
* Added modifications regarding the renaming of QueueLength to Customer Number
* @version Date: Aug-2009
*
* @author Ashanka
* Cleaned code by removing unnecesary comments of old code.
* @version Date: Sep-2009
*
* @author Ashanka
* Added a backward compatible clause in loadResultsMatrix to open existing JMVA models correctly.
* If we are looking to open measures of Number of Customers from existing JMVA files then we should look for
* Queue length as this was the previous name of the label.
* @version Date: Sep-2009
*
* @author Kourosh
* Add a new property ReferenceStation which is used to declare the reference station for each closed class. and save and load the files
* @version Date: OCT-2013
*/
public class ExactModel implements ExactConstants {
/* EDITED by Abhimanyu Chugh */
//Set by default to the Exact MVA algorithm
public SolverAlgorithm algorithmType = SolverAlgorithm.EXACT;
//Set by default if user does not enter a tolerance value
public double tolerance = SolverMultiClosedAMVA.DEFAULT_TOLERANCE;
//Names of the algorithms to be compared
private Set<SolverAlgorithm> whatifAlgorithms;
private Map<SolverAlgorithm, Double> whatifAlgorithmsTollerance;
/* END */
//true if the model is closed
private boolean closed;
//true if the model is open
private boolean open;
//true if the model contains load dependent stations
private boolean ld;
//true if visits are set (otherwise they will be all unitary)
private boolean unitaryVisits;
//true if the model has been modified
private boolean changed;
//true if results are available
private boolean hasResults;
//true if the results are valid (no modify has been made in the model after results computation)
private boolean resultsOK;
//description of the model
private String description;
/***********************STATIONS AND CLASSES******************************/
//number of service centers
private int stations;
//number of classes
private int classes;
//total population (computed as the sum of all closed class populations)
private int maxpop;
//class data is class population for closed classes, class arrival rate for open classes
//dim: classData[classes]
private double[] classData;
//station names
//dim: stationNames[stations]
private String[] stationNames;
//station types
//dim: stationTypes[stations]
private int[] stationTypes;
//number of servers of each station
//dim: stationServers[stations]
private int[] stationServers;
//class names
//dim: classNames[classes]
private String[] classNames;
//class types
//dim: classTypes[classes]
private int[] classTypes;
//class refstations
//private double[][] ReferenceStation;
private int[] ReferenceStation;
/***********************SERVICE PARAMETERS**************************/
/**
* visits to the service centers
* dim: visits[stations][classes]
*/
private double[][] visits;
/**
* service times of the service centers
* dim: serviceTimes[stations][classes][p]
* p=maxpop if stationTypes[s]==STATION_LD
* p=1 otherwise
*/
private double[][][] serviceTimes;
/***********************RESULTS******************************/
/* EDITED by Abhimanyu Chugh */
/**
* number of iterations algorithm performed for each (what-if) iteration/execution
* dim: algIterations<Algorithm, [iterations]>
*/
private Map<SolverAlgorithm, int[]> algIterations;
/**
* queue lengths
* dim: queueLen<Algorithm, [stations][classes][iterations]>
*/
private Map<SolverAlgorithm, double[][][]> queueLen;
/**
* throughput
* dim: throughput<Algorithm, [stations][classes][iterations]>
*/
private Map<SolverAlgorithm, double[][][]> throughput;
/**
* residence times
* dim: resTime<Algorithm, [stations][classes][iterations]>
*/
private Map<SolverAlgorithm, double[][][]> resTimes;
/**
* utilization
* dim: util<Algorithm, [stations][classes][iterations]>
*/
private Map<SolverAlgorithm, double[][][]> util;
/* END */
/*****************************************************************/
//parameters for randomization
private static final double MAXRAND = 100;
private static final double MAXRANGE = 10;
/*****************************************************************/
/********************** WHAT-IF ANALYSIS *** Bertoli Marco *******/
/** Number of iterations (1 for normal usage, >1 for what-if analysis) */
private int iterations = 1;
/** Index of class selected for what-if analysis. -1 means all classes */
private int whatIfClass = -1;
/** Index of station selected for what-if analysis. -1 means all stations */
private int whatIfStation = -1;
/**
* Type of what-if analysis
* @see ExactConstants
*/
private String whatIfType;
/** Array with considered values */
private double[] whatIfValues;
/*****************************************************************/
/**
* make an object with default values
*/
public ExactModel() {
setDefaults();
/* EDITED by Abhimanyu Chugh */
/* END */
}
/**
* copy constructor
*/
public ExactModel(ExactModel e) {
closed = e.closed;
open = e.open;
unitaryVisits = e.unitaryVisits;
hasResults = e.hasResults;
resultsOK = e.resultsOK;
changed = e.changed;
stations = e.stations;
classes = e.classes;
maxpop = e.maxpop;
description = e.description;
stationNames = ArrayUtils.copy(e.stationNames);
stationTypes = ArrayUtils.copy(e.stationTypes);
stationServers = ArrayUtils.copy(e.stationServers);
classNames = ArrayUtils.copy(e.classNames);
classTypes = ArrayUtils.copy(e.classTypes);
classData = ArrayUtils.copy(e.classData);
algorithmType = e.algorithmType;
tolerance = e.tolerance;
whatifAlgorithms = EnumSet.copyOf(e.whatifAlgorithms);
whatifAlgorithmsTollerance = new EnumMap<SolverAlgorithm, Double>(e.whatifAlgorithmsTollerance);
visits = ArrayUtils.copy2(e.visits);
serviceTimes = ArrayUtils.copy3(e.serviceTimes);
//ReferenceStation = ArrayUtils.copy2(e.ReferenceStation);
ReferenceStation = ArrayUtils.copy(e.ReferenceStation);
// What-if analysis
iterations = e.iterations;
whatIfClass = e.whatIfClass;
whatIfStation = e.whatIfStation;
whatIfType = e.whatIfType;
whatIfValues = e.whatIfValues;
if (hasResults) {
/* EDITED by Abhimanyu Chugh */
queueLen = new LinkedHashMap<SolverAlgorithm, double[][][]>();
throughput = new LinkedHashMap<SolverAlgorithm, double[][][]>();
resTimes = new LinkedHashMap<SolverAlgorithm, double[][][]>();
util = new LinkedHashMap<SolverAlgorithm, double[][][]>();
algIterations = new LinkedHashMap<SolverAlgorithm, int[]>();
for (SolverAlgorithm alg : e.queueLen.keySet()) {
queueLen.put(alg, ArrayUtils.copy3(e.queueLen.get(alg)));
throughput.put(alg, ArrayUtils.copy3(e.throughput.get(alg)));
resTimes.put(alg, ArrayUtils.copy3(e.resTimes.get(alg)));
util.put(alg, ArrayUtils.copy3(e.util.get(alg)));
if (!alg.isExact()) {
algIterations.put(alg, ArrayUtils.copy(e.algIterations.get(alg)));
}
}
/* END */
}
}
public void setAlgorithmType(SolverAlgorithm algorithm) {
algorithmType = algorithm;
}
public SolverAlgorithm getAlgorithmType() {
return algorithmType;
}
public double getTolerance() {
return tolerance;
}
public void setTolerance (double tol) {
tolerance = tol;
}
/**
* @return the set of algorithm to perform whatif on
*/
public Set<SolverAlgorithm> getWhatifAlgorithms() {
return whatifAlgorithms;
}
/**
* Set algorithm to perform whatif on
* @param algorithm the algorithm
* @param compare true to do whatif, false otherwise
*/
public void setWhatifAlgorithm(SolverAlgorithm algorithm, boolean compare) {
if (compare) {
this.whatifAlgorithms.add(algorithm);
} else {
this.whatifAlgorithms.remove(algorithm);
}
}
/**
* Returns the tolerance for a whatif algorithm
* @param algorithm the algorithm
* @return the tolerance
*/
public double getWhatifAlgorithmTolerance(SolverAlgorithm algorithm) {
Double val = whatifAlgorithmsTollerance.get(algorithm);
if (val != null) {
return val;
} else {
return SolverMultiClosedAMVA.DEFAULT_TOLERANCE;
}
}
/**
* Sets the tolerance for a whatif algorithm
* @param algorithm the algorithm
* @param value the tolerance
*/
public void setWhatifAlgorithmTolerance(SolverAlgorithm algorithm, double value) {
whatifAlgorithmsTollerance.put(algorithm, value);
}
/**
* @return true if AMVA algorithm comparison was requested. false otherwise.
*/
public boolean isWhatifAlgorithms() {
return whatifAlgorithms.size() > 0;
}
/**
* Clears whatif algorithms selection
*/
public void clearWhatifAlgorithms() {
whatifAlgorithms.clear();
}
/**
* Clears all the results
*/
public void discardResults() {
queueLen = null;
throughput = null;
resTimes = null;
util = null;
discardChanges();
}
/**
* Discards all change elements but does not touch results
*/
public void discardChanges() {
hasResults = false;
resultsOK = false;
changed = true;
}
/**
* sets all the result data for this model.
* @throws IllegalArgumentException if any argument is null or not of the correct size
*/
/* EDITED by Abhimanyu Chugh */
public void setResults(SolverAlgorithm alg, int[] algIterations, double[][][] queueLen, double[][][] throughput, double[][][] resTimes, double[][][] util) {
if (queueLen == null || queueLen.length != stations || queueLen[0].length != classes) {
throw new IllegalArgumentException("queueLen must be non null and of size [stations][classes][iterations]");
}
if (throughput == null || throughput.length != stations || throughput[0].length != classes) {
throw new IllegalArgumentException("throughput must be non null and of size [stations][classes][iterations]");
}
if (resTimes == null || resTimes.length != stations || resTimes[0].length != classes) {
throw new IllegalArgumentException("resTimes must be non null and of size [stations][classes][iterations]");
}
if (util == null || util.length != stations || util[0].length != classes) {
throw new IllegalArgumentException("util must be non null and of size [stations][classes][iterations]");
}
// non controlla il numero di classi per tutte le stazioni, ma solo per la prima!!
this.queueLen.put(alg, ArrayUtils.copy3(queueLen));
this.throughput.put(alg, ArrayUtils.copy3(throughput));
this.resTimes.put(alg, ArrayUtils.copy3(resTimes));
this.util.put(alg, ArrayUtils.copy3(util));
if (!alg.isExact()) {
this.algIterations.put(alg, ArrayUtils.copy(algIterations));
}
iterations = queueLen[0][0].length;
/*TODO: achugh
hasResults = true;
resultsOK = true;
changed = true;
*/
}
/* END */
/**
* sets all the result data for this model. This is called when only one iteration is performed.
* @throws IllegalArgumentException if any argument is null or not of the correct size
*/
/* EDITED by Abhimanyu Chugh */
public void setResults(SolverAlgorithm alg, int algIterations, double[][] queueLen, double[][] throughput, double[][] resTimes, double[][] util) {
resetResults();
setResults(alg, algIterations, queueLen, throughput, resTimes, util, 0);
}
/* END */
/**
* Sets ResultsOK flag
* @param value value of ResultsOK flag
*/
public void setResultsOK(boolean value) {
this.resultsOK = value;
}
/**
* sets all the result data for this model. This is called on multiple iterations (what-if analysis)
* @throws IllegalArgumentException if any argument is null or not of the correct size
*/
/* EDITED by Abhimanyu Chugh */
public void setResults(SolverAlgorithm alg, int algIterations, double[][] queueLen, double[][] throughput, double[][] resTimes, double[][] util, int iteration) {
if (queueLen == null || queueLen.length != stations || queueLen[0].length != classes) {
throw new IllegalArgumentException("queueLen must be non null and of size [stations][classes]");
}
if (throughput == null || throughput.length != stations || throughput[0].length != classes) {
throw new IllegalArgumentException("throughput must be non null and of size [stations][classes]");
}
if (resTimes == null || resTimes.length != stations || resTimes[0].length != classes) {
throw new IllegalArgumentException("resTimes must be non null and of size [stations][classes]");
}
if (util == null || util.length != stations || util[0].length != classes) {
throw new IllegalArgumentException("util must be non null and of size [stations][classes]");
}
if (iteration >= iterations) {
throw new IllegalArgumentException("iteration is greater than expected number of iterations");
}
boolean isApprox = !alg.isExact();
if (this.queueLen.get(alg) == null) {
this.queueLen.put(alg, new double[stations][classes][iterations]);
this.throughput.put(alg, new double[stations][classes][iterations]);
this.resTimes.put(alg, new double[stations][classes][iterations]);
this.util.put(alg, new double[stations][classes][iterations]);
if (isApprox) {
this.algIterations.put(alg, new int[iterations]);
}
}
ArrayUtils.copy2to3(queueLen, this.queueLen.get(alg), iteration);
ArrayUtils.copy2to3(throughput, this.throughput.get(alg), iteration);
ArrayUtils.copy2to3(resTimes, this.resTimes.get(alg), iteration);
ArrayUtils.copy2to3(util, this.util.get(alg), iteration);
if (isApprox) {
this.algIterations.get(alg)[iteration] = algIterations;
}
/* TODO: achugh
hasResults = true;
resultsOK = true;
changed = true;
*/
}
public void setResultsBooleans(boolean value) {
hasResults = value;
resultsOK = value;
changed = value;
}
/**
* Resets a particular algorithm's arrays used to store results
*/
public void resetAlgResults(SolverAlgorithm alg) {
if (queueLen.containsKey(alg)) {
queueLen.put(alg, new double[stations][classes][iterations]);
throughput.put(alg, new double[stations][classes][iterations]);
resTimes.put(alg, new double[stations][classes][iterations]);
util.put(alg, new double[stations][classes][iterations]);
if (!alg.isExact()) {
algIterations.put(alg, new int[iterations]);
}
}
hasResults = false;
changed = true;
}
/**
* Resets all algorithms' arrays used to store results
*/
public void resetAlgResults() {
for (SolverAlgorithm alg : queueLen.keySet()) {
queueLen.put(alg, new double[stations][classes][iterations]);
throughput.put(alg, new double[stations][classes][iterations]);
resTimes.put(alg, new double[stations][classes][iterations]);
util.put(alg, new double[stations][classes][iterations]);
if (!alg.isExact()) {
algIterations.put(alg, new int[iterations]);
}
}
hasResults = false;
changed = true;
}
/**
* Resets arrays used to store results
*/
public void resetResults() {
queueLen = new LinkedHashMap<SolverAlgorithm, double[][][]>();
throughput = new LinkedHashMap<SolverAlgorithm, double[][][]>();
resTimes = new LinkedHashMap<SolverAlgorithm, double[][][]>();
util = new LinkedHashMap<SolverAlgorithm, double[][][]>();
algIterations = new LinkedHashMap<SolverAlgorithm, int[]>();
hasResults = false;
changed = true;
}
/* END */
/**
* Initialize the object with defaults:
* 1 closed class, 1 LI station, 0 customers, all visits to one, all service times to zero, no results
*/
public void setDefaults() {
closed = true;
hasResults = false;
resultsOK = false;
stations = 1;
classes = 1;
// perch� � 1 se non ci sono customers?? Lasciare cos�
maxpop = 1;
changed = true;
classData = new double[1];
//NEW
//@author Stefano Omini
classData[0] = 1;
//end NEW
stationNames = new String[1];
stationNames[0] = "Station1";
stationTypes = new int[1];
stationTypes[0] = STATION_LI;
stationServers = new int[1];
stationServers[0] = 1;
classNames = new String[1];
classNames[0] = "Class1";
classTypes = new int[1];
classTypes[0] = CLASS_CLOSED;
visits = new double[1][1];
visits[0][0] = 1.0;
//ReferenceStation = new double[1][2];
//ReferenceStation[0][0]= 0.0;
//ReferenceStation[0][1]= 0.0;
ReferenceStation = new int[1];
ReferenceStation[0]= 0;
serviceTimes = new double[1][1][1];
//NEW
//@author Stefano Omini
serviceTimes[0][0][0] = 0.0;
//end NEW
description = "";
whatifAlgorithms = EnumSet.noneOf(SolverAlgorithm.class);
whatifAlgorithmsTollerance = new EnumMap<SolverAlgorithm, Double>(SolverAlgorithm.class);
}
/**
* Gets the model description
* @return the model description
*/
public String getDescription() {
return description;
}
/**
* Sets the model description
* @param description the model description
* @return true if data was changed, false otherwise
*/
public boolean setDescription(String description) {
if (description.equals(this.description)) {
return false;
}
this.description = description;
changed = true;
return true;
}
/**
* @return true if this object describes a multiclass system
*/
public boolean isMultiClass() {
return (classes > 1);
}
/**
* @return true if this object describes a closed system
*/
public boolean isClosed() {
return closed;
}
/**
* @return true if this object describes an open system
*/
public boolean isOpen() {
return open;
}
/**
* @return true if this object describes a mixed system
*/
public boolean isMixed() {
//mixed = true only if closed = false and open = false
return !(closed || open);
}
/**
* @return true if this object describes a system containing LD stations
*/
public boolean isLd() {
return ld;
}
/**
* @return number of service centers
*/
public int getStations() {
return stations;
}
/**
* @return number of classes
*/
public int getClasses() {
return classes;
}
/**
* @return get the matrix of ReferenceStations
public double[][] getReferenceStation() {
return ReferenceStation;
}
*/
/**
* @return get the matrix of ReferenceStations
*/
public int[] getReferenceStation() {
return ReferenceStation;
}
/**
* @return total population
*/
public int getMaxpop() {
return maxpop;
}
/**
* @return names of the service centers
*/
public String[] getStationNames() {
return stationNames;
}
/**
* @return the number of servers for each station. For delay stations this parameter is unsensed.
*/
public int[] getStationServers() {
return stationServers;
}
/**
* @return true if this model contains multiple server for a station.
*/
public boolean isMultipleServers() {
for (int i = 0; i < stations; i++) {
if (stationServers[i] > 1 && stationTypes[i] != ExactConstants.STATION_DELAY) {
return true;
}
}
return false;
}
/**
* sets the number of servers for each station
* @param classNames the number of servers of each station
* @throws IllegalArgumentException if the array is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setStationServers(int[] stationServers) {
if (stationServers.length != stations) {
throw new IllegalArgumentException("stationServers.length != stations");
}
if (Arrays.equals(this.stationServers, stationServers)) {
return false;
}
this.stationServers = stationServers;
changed = true;
return true;
}
/**
* sets the names of the service centers.
* @param stationNames the names of the service centers
* @throws IllegalArgumentException if the array is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setStationNames(String[] stationNames) {
if (stationNames.length != stations) {
throw new IllegalArgumentException("stationNames.length!=stations");
}
if (!changed) {
if (Arrays.equals(this.stationNames, stationNames)) {
return false;
}
}
this.stationNames = stationNames;
changed = true;
return true;
}
/**
* @return names of the classes
*/
public String[] getClassNames() {
return classNames;
}
/**
* sets the names of the classes.
* @param classNames the names of the classes
* @throws IllegalArgumentException if the array is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setClassNames(String[] classNames) {
if (classNames.length != classes) {
throw new IllegalArgumentException("classNames.length!=classes");
}
if (Arrays.equals(this.classNames, classNames)) {
return false;
}
this.classNames = classNames;
changed = true;
return true;
}
/**
* @return data for the classes.
*/
public double[] getClassData() {
return classData;
}
/**
* sets the data for the classes
* @param classData the data for the classes
* @throws IllegalArgumentException if the array is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setClassData(double[] classData) {
if (classData.length != classes) {
throw new IllegalArgumentException("classData.length!=classes");
}
if (Arrays.equals(this.classData, classData)) {
return false;
}
this.classData = classData;
changed = true;
resultsOK = false;
// make sure 3rd dimension of serviceTimes is ok
resize(stations, classes);
return true;
}
/**
* @return type of the classes
*/
public int[] getClassTypes() {
return classTypes;
}
/**
* sets the type of the classes
* @param classTypes the type of the classes
* @throws IllegalArgumentException if the array is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setClassTypes(int[] classTypes) {
if (classTypes.length != classes) {
throw new IllegalArgumentException("classTypes.length!=classes");
}
if (Arrays.equals(this.classTypes, classTypes)) {
return false;
}
this.classTypes = classTypes;
closed = calcClosed();
open = calcOpen();
changed = true;
resultsOK = false;
return true;
}
/**
* @return type of the stations
*/
public int[] getStationTypes() {
return stationTypes;
}
/**
* sets the type of the stations
* @param stationTypes the type of the stations
* @throws IllegalArgumentException if the array is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setStationTypes(int[] stationTypes) {
if (stationTypes.length != stations) {
throw new IllegalArgumentException("stationTypes.length!=stations");
}
if (Arrays.equals(this.stationTypes, stationTypes)) {
return false;
}
this.stationTypes = stationTypes;
// adjusts serviceTimes size and recalculates flags
resize(stations, classes, true);
changed = true;
resultsOK = false;
return true;
}
/**
* @return the matrix of visits
*/
public double[][] getVisits() {
return visits;
}
/**
* sets the matrix of visits
* @param visits the matrix of visits
* @throws IllegalArgumentException if the matrix is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setVisits(double[][] visits) {
if (visits.length != stations || visits[0].length != classes) {
throw new IllegalArgumentException("incorrect array dimension");
}
if (ArrayUtils.equals2(this.visits, visits)) {
return false;
}
this.visits = visits;
changed = true;
resultsOK = false;
// Checks if visits are all one
calcUnitaryVisits();
return true;
}
/**
* @return the matrix of service times
*/
public double[][][] getServiceTimes() {
return serviceTimes;
}
/**
* sets the matrix of service times
* @param serviceTimes the matrix of service times
* @throws IllegalArgumentException if the matrix is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setServiceTimes(double[][][] serviceTimes) {
if (serviceTimes.length != stations || serviceTimes[0].length != classes) {
throw new IllegalArgumentException("incorrect array dimension");
}
if (ArrayUtils.equals3(this.serviceTimes, serviceTimes)) {
return false;
}
int currSize;
double[][] subST;
//validate sizes
for (int s = 0; s < stations; s++) {
currSize = (stationTypes[s] == STATION_LD ? maxpop : 1);
// if a station is LD but customer number is 0, maxpop = 0
if (currSize == 0) {
currSize = 1;
}
subST = serviceTimes[s];
for (int c = 0; c < classes; c++) {
if (subST[c].length != currSize) {
throw new IllegalArgumentException("Wrong size for station " + stationNames[s]);
}
}
}
this.serviceTimes = serviceTimes;
changed = true;
resultsOK = false;
return true;
}
/**
* sets the matrix of ReferenceStation
* @param ReferenceStation the matrix of ReferenceStation
* @throws IllegalArgumentException if the matrix is not of the correct size
* @return true if data was changed, false otherwise
public boolean setReferenceStation(double[][] ReferenceStation) {
if (ReferenceStation.length != classes ) {
throw new IllegalArgumentException("incorrect array dimension");
}
this.ReferenceStation = ReferenceStation;
changed = true;
resultsOK = false;
return true;
}
*/
/**
* sets the matrix of ReferenceStation
* @param ReferenceStation the matrix of ReferenceStation
* @throws IllegalArgumentException if the matrix is not of the correct size
* @return true if data was changed, false otherwise
*/
public boolean setReferenceStation(int[] ReferenceStation) {
if (ReferenceStation.length != classes ) {
throw new IllegalArgumentException("incorrect array dimension");
}
this.ReferenceStation = ReferenceStation;
//changed = true;
resultsOK = false;
return true;
}
/**
* Resizes the data structures according to specified parameters. Data is preserved as far as possible
* @return true if data was changed, false otherwise
*/
public boolean resize(int stations, int classes, boolean forceResize) {
if (stations <= 0 || classes <= 0) {
throw new IllegalArgumentException("stations and classes must be >0");
}
if (forceResize || this.stations != stations || this.classes != classes) {
//other cases already handled in setXXX methods
discardResults();
this.stations = stations;
this.classes = classes;
stationNames = ArrayUtils.resize(stationNames, stations, null);
stationTypes = ArrayUtils.resize(stationTypes, stations, STATION_LI);
stationServers = ArrayUtils.resize(stationServers, stations, 1);
ld = calcLD();
visits = ArrayUtils.resize2(visits, stations, classes, 1.0);
classNames = ArrayUtils.resize(classNames, classes, null);
classTypes = ArrayUtils.resize(classTypes, classes, CLASS_CLOSED);
closed = calcClosed();
classData = ArrayUtils.resize(classData, classes, 0.0);
maxpop = calcMaxpop();
serviceTimes = ArrayUtils.resize3var(serviceTimes, stations, classes, calcSizes(), 0.0);
// Checks if visits are all one
calcUnitaryVisits();
//ReferenceStation = ArrayUtils.resize2(ReferenceStation, classes, 2, 0.0);
ReferenceStation = ArrayUtils.resize(ReferenceStation, classes, 0);
return true;
} else {
// Check if population was changed.
int newMaxpop = calcMaxpop();
if (newMaxpop != maxpop) {
maxpop = newMaxpop;
serviceTimes = ArrayUtils.resize3var(serviceTimes, stations, classes, calcSizes(), 0.0);
return true;
}
}
return false;
}
/**
* Resizes the data structures according to specified parameters. Data is preserved as far as possible
* @return true if data was changed, false otherwise
*/
public boolean resize(int stations, int classes) {
return resize(stations, classes, false);
}
/* EDITED by Abhimanyu Chugh */
/**
* @return algorithm iterations
*/
public Map<SolverAlgorithm, int[]> getAlgIterations() {
return algIterations;
}
/**
* @return algorithm iterations for the given algorithm
*/
public int[] getAlgIterations(SolverAlgorithm alg) {
if (alg != null) {
return algIterations.get(alg);
}
return null;
}
/**
* @return queue lengths
*/
public Map<SolverAlgorithm, double[][][]> getQueueLen() {
return queueLen;
}
/**
* @return queue lengths for the given algorithm
*/
public double[][][] getQueueLen(SolverAlgorithm alg) {
if (alg != null) {
return queueLen.get(alg);
}
return null;
}
/**
* @return residence times
*/
public Map<SolverAlgorithm, double[][][]> getResTimes() {
return resTimes;
}
/**
* @return residence times for the given algorithm
*/
public double[][][] getResTimes(SolverAlgorithm alg) {
if (alg != null) {
return resTimes.get(alg);
}
return null;
}
/**
* @return throughputs
*/
public Map<SolverAlgorithm, double[][][]> getThroughput() {
return throughput;
}
/**
* @return throughputs for the given algorithm
*/
public double[][][] getThroughput(SolverAlgorithm alg) {
if (alg != null) {
return throughput.get(alg);
}
return null;
}
/**
* @return utilizations
*/
public Map<SolverAlgorithm, double[][][]> getUtilization() {
return util;
}
/**
* @return utilizations for the given algorithm
*/
public double[][][] getUtilization(SolverAlgorithm alg) {
if (alg != null) {
return util.get(alg);
}
return null;
}
/* END */
/**
* Removes all LD stations, converting them into LI stations
*/
public void removeLD() {
for (int i = 0; i < stations; i++) {
if (stationTypes[i] == STATION_LD) {
stationTypes[i] = STATION_LI;
//NEW
//@author Stefano Omini
//clear old LD service times
serviceTimes[i] = new double[classes][1];
for (int c = 0; c < classes; c++) {
serviceTimes[i][c][0] = 0.0;
}
//end NEW
}
}
ld = false;
}
/**
* This method will find if current visits matrix is unitary or not.
* If a value is 0 will check service demand. This is used to show correct
* panel layout upon loading of a model
*/
private void calcUnitaryVisits() {
double epsilon = 1e-14;
for (int i = 0; i < stations; i++) {
for (int j = 0; j < classes; j++) {
if ((Math.abs(visits[i][j]) < epsilon && Math.abs(serviceTimes[i][j][0]) > epsilon)
|| (!(Math.abs(visits[i][j]) < epsilon) && Math.abs(visits[i][j] - 1.0) > epsilon)) {
unitaryVisits = true;
return;
}
}
}
unitaryVisits = false;
}
/**
* @return true if the model contains only closed stations
*/
private boolean calcClosed() {
for (int i = 0; i < classes; i++) {
if (classTypes[i] != CLASS_CLOSED) {
//make sure we stay in a consistent state
removeLD();
//Removes LD as multiclass LD is not supported
return false;
}
}
return true;
}
/**
* @return true if the model contains only open stations
*/
private boolean calcOpen() {
for (int i = 0; i < classes; i++) {
if (classTypes[i] != CLASS_OPEN) {
return false;
}
}
return true;
}
/**
* @return true if the model contains Load Dependent stations
*/
private boolean calcLD() {
for (int i = 0; i < stations; i++) {
if (stationTypes[i] == STATION_LD) {
return true;
}
}
return false;
}
/**
* @return the total population (sum of the customers of all closed class)
*/
private int calcMaxpop() {
/* sum all the closed classes' customers */
int maxpop = 0;
for (int i = 0; i < classes; i++) {
if (classTypes[i] == CLASS_CLOSED) {
maxpop += classData[i];
}
}
return maxpop;
}
/**
* @return the sizes of service times for each station (max pop for LD stations, 1 for LI stations)
*/
private int[] calcSizes() {
int mp = (maxpop > 0 ? maxpop : 1);
int[] sizes = new int[stations];
for (int s = 0; s < stations; s++) {
sizes[s] = (stationTypes[s] == STATION_LD ? mp : 1);
}
return sizes;
}
/**
* Warning: Calling this on large systems *will* result in OutOfMemory errors. You have been warned.
* @return a String representation of the parameters of this object
*/
public String toString() {
StringBuffer s = new StringBuffer();
s.append("stations=").append(stations).append(" classes=").append(classes).append(" pop=").append(maxpop).append(" changed=").append(changed)
.append(" ld=").append(ld).append(" open=").append(open).append(" closed=").append(closed).append(" hasResults=").append(hasResults)
.append(" resultsOK=").append(resultsOK).append("\n").append("stationNames=").append(ArrayUtils.toString(stationNames)).append("\n")
.append("stationTypes=").append(ArrayUtils.toString(stationTypes)).append("\n").append("classNames=").append(
ArrayUtils.toString(classNames)).append("\n").append("classTypes=").append(ArrayUtils.toString(classTypes)).append("\n")
.append("classData=").append(ArrayUtils.toString(classData)).append("\n").append("visits=").append(ArrayUtils.toString2(visits))
.append("\n").append("serviceTimes=").append(ArrayUtils.toString3(serviceTimes)).append("\n");
if (hasResults) {
/* EDITED by Abhimanyu Chugh */
for(SolverAlgorithm alg : queueLen.keySet()) {
double[][][] queueLens = queueLen.get(alg);
double[][][] throughputs = throughput.get(alg);
double[][][] resTime = resTimes.get(alg);
double[][][] utils = util.get(alg);
s.append("algorithm=").append(alg).append("\n").append("number of customers=").append(ArrayUtils.toString3(queueLens))
.append("\n").append("throughput=").append(ArrayUtils.toString3(throughputs)).append("\n").append("resTimes=")
.append(ArrayUtils.toString3(resTime)).append("\n").append("utilization=").append(ArrayUtils.toString3(utils)).append("\n");
}
/* END */
}
return s.toString();
}
/**
* Deletes a class
* @param i the index of the class
*/
public void deleteClass(int i) {
if (classes < 2) {
throw new RuntimeException("System must have at least one class");
}
classes--;
classNames = ArrayUtils.delete(classNames, i);
classTypes = ArrayUtils.delete(classTypes, i);
classData = ArrayUtils.delete(classData, i);
visits = ArrayUtils.delete2_2(visits, i);
serviceTimes = ArrayUtils.delete3_2(serviceTimes, i);
//ReferenceStation = ArrayUtils.delete2_1(ReferenceStation, i);
ReferenceStation = ArrayUtils.delete(ReferenceStation, i);
resize(stations, classes);
//DEK (Federico Granata) 3-10-2003
//it was considering the results valid when a class is cancelled
hasResults = false;
//END
}
/**
* Deletes a station
* @param i the index of the station
*/
public void deleteStation(int i) {
if (stations < 2) {
throw new RuntimeException("System must have at least one station");
}
String[] BstationNames = stationNames; //Backup of stations
stations--;
stationNames = ArrayUtils.delete(stationNames, i);
stationTypes = ArrayUtils.delete(stationTypes, i);
stationServers = ArrayUtils.delete(stationServers, i);
visits = ArrayUtils.delete2_1(visits, i);
serviceTimes = ArrayUtils.delete3_1(serviceTimes, i);
//ReferenceStation = ArrayUtils.delete2_2(ReferenceStation, i);
resize(stations, classes);
//DEK (Federico Granata) 3-10-2003
//it was considering the results valid when a class is cancelled
hasResults = false;
//END
//Delete the selected station in ReferenceStation (Kourosh OCT 2013)
/*
for (int j= 0; j< classes ; j++)
if (ReferenceStation[j][1] == i)
ReferenceStation[j][1] = 0.0;
String temp="";
for (int k= 0 ; k< classes; k++)
{
if (ReferenceStation[k][1] == BstationNames.length)
ReferenceStation[k][1] = stationNames.length;
}
for (int k= 0 ; k< classes; k++)
{
temp = BstationNames[(int) ReferenceStation[k][1]];
ReferenceStation[k][1] = stationNames.length;
for(int j=0;j<stationNames.length;j++)
if (temp.equals(stationNames[j]))
ReferenceStation[k][1] = j;
}
*/
for(int j=0; j< classes; j++)
if(ReferenceStation[j] == i )
ReferenceStation[j] = 0;
}
/**
* @return true if the model has been changed
*/
public boolean isChanged() {
return changed;
}
/**
* resets the changed flag.
* <br>
* WARNING: this enables change checking on parameter setting, which can be quite time-consuming.
*/
public void resetChanged() {
changed = false;
}
/**
* flags the model as changed.
* There is no need to call this, except to disable time-consuming change checking if you're not interested in it
*/
public void setChanged() {
changed = true;
}
/**
* @return true if results are available
*/
public boolean hasResults() {
return hasResults;
}
/**
* @return true if results are valid
*/
public boolean areResultsOK() {
return resultsOK;
}
/**
* Creates a DOM representation of this object
* @return a DOM representation of this object
*/
public Document createDocument() {
Document root;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
root = dbf.newDocumentBuilder().newDocument();
} catch (ParserConfigurationException pce) {
throw new RuntimeException(pce);
}
/* model */
Element modelElement = root.createElement("model");
modelElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
modelElement.setAttribute("xsi:noNamespaceSchemaLocation", "JMTmodel.xsd");
root.appendChild(modelElement);
/* description */
if (!description.equals("")) {
Element descriptionElement = root.createElement("description");
descriptionElement.appendChild(root.createCDATASection(description));
modelElement.appendChild(descriptionElement);
}
/* parameters */
Element parametersElement = root.createElement("parameters");
modelElement.appendChild(parametersElement);
/* classes */
Element classes_element = root.createElement("classes");
parametersElement.appendChild(classes_element);
classes_element.setAttribute("number", Integer.toString(classes));
for (int i = 0; i < classes; i++) {
classes_element.appendChild(makeClassElement(root, i));
}
/* stations */
Element stationsElement = root.createElement("stations");
parametersElement.appendChild(stationsElement);
stationsElement.setAttribute("number", Integer.toString(stations));
for (int i = 0; i < stations; i++) {
stationsElement.appendChild(makeStationElement(root, i));
}
/* EDITED by Abhimanyu Chugh */
/* algorithm parameters */
Element algParamsElement = root.createElement("algParams");
modelElement.appendChild(algParamsElement);
/* algorithm combo box */
Element algType_element = root.createElement("algType");
algParamsElement.appendChild(algType_element);
algType_element.setAttribute("name", algorithmType.toString());
algType_element.setAttribute("tolerance", Double.toString(tolerance));
/* compare algorithms box */
Element compareAlgs_element = root.createElement("compareAlgs");
compareAlgs_element.setAttribute("value", Boolean.toString(isWhatifAlgorithms()));
algParamsElement.appendChild(compareAlgs_element);
if (isWhatifAlgorithms()) {
for (SolverAlgorithm algo : getWhatifAlgorithms()) {
Element alg_element = root.createElement("whatIfAlg");
compareAlgs_element.appendChild(alg_element);
alg_element.setAttribute("name", algo.toString());
alg_element.setAttribute("tolerance", Double.toString(getWhatifAlgorithmTolerance(algo)));
}
}
/* END */
/* What-if Analysis - Bertoli Marco */
if (isWhatIf()) {
Element whatIf = root.createElement("whatIf");
modelElement.appendChild(whatIf);
whatIf.setAttribute("type", whatIfType);
whatIf.setAttribute("values", ArrayUtils.toCSV(whatIfValues));
// Class name
if (whatIfClass >= 0) {
whatIf.setAttribute("className", classNames[whatIfClass]);
}
// Station name
if (whatIfStation >= 0) {
whatIf.setAttribute("stationName", stationNames[whatIfStation]);
}
}
/* classes */
Element ReferenceStation_element = root.createElement("ReferenceStation");
parametersElement.appendChild(ReferenceStation_element);
ReferenceStation_element.setAttribute("number", Integer.toString(classes));
for (int i = 0; i < classes; i++) {
ReferenceStation_element.appendChild(makeReferenceStationElement(root, i));
}
//OLD
//if (hasResults) modelElement.appendChild(appendSolutionElement(root));
//NEW
//@author Stefano Omini
if (hasResults && resultsOK) {
appendSolutionElement(root, modelElement);
//end NEW
}
return root;
}
private Element makeClassElement(Document root, int classNum) {
Element classElement;
if (classTypes[classNum] == CLASS_CLOSED) {
classElement = root.createElement("closedclass");
classElement.setAttribute("population", Integer.toString((int) classData[classNum]));
classElement.setAttribute("name", classNames[classNum]);
} else {
classElement = root.createElement("openclass");
classElement.setAttribute("rate", Double.toString(classData[classNum]));
classElement.setAttribute("name", classNames[classNum]);
}
return classElement;
}
private Element makeReferenceStationElement(Document root, int classNum) {
Element classElement;
String[] MyStationNames;
MyStationNames = stationNames;
MyStationNames = ArrayUtils.resize(MyStationNames, MyStationNames.length+1, null);
MyStationNames[MyStationNames.length-1] = "Arrival Process";
classElement = root.createElement("Class");
classElement.setAttribute("name", classNames[classNum]);
//classElement.setAttribute("refStation", MyStationNames[(int) ReferenceStation[classNum][1]] );
classElement.setAttribute("refStation", MyStationNames[(int) ReferenceStation[classNum]] );
return classElement;
}
private Element makeStationElement(Document root, int stationNum) {
Element station_element;
Node servicetimes_element;
Node visits_element;
switch (this.stationTypes[stationNum]) {
case STATION_LI:
station_element = root.createElement("listation");
station_element.setAttribute("name", this.stationNames[stationNum]);
station_element.setAttribute("servers", String.valueOf(this.stationServers[stationNum]));
/* create the section for service times */
servicetimes_element = station_element.appendChild(root.createElement("servicetimes"));
station_element.appendChild(servicetimes_element);
/* create the section for visits */
visits_element = station_element.appendChild(root.createElement("visits"));
station_element.appendChild(visits_element);
/* for each customer class */
for (int j = 0; j < classes; j++) {
String class_name = this.classNames[j];
/* set service time */
Element st_element = root.createElement("servicetime");
st_element.setAttribute("customerclass", class_name);
st_element.appendChild(root.createTextNode(Double.toString(this.serviceTimes[stationNum][j][0])));
servicetimes_element.appendChild(st_element);
/* set visit */
Element visit_element = root.createElement("visit");
visit_element.setAttribute("customerclass", class_name);
visit_element.appendChild(root.createTextNode(Double.toString(this.visits[stationNum][j])));
visits_element.appendChild(visit_element);
}
break;
case STATION_DELAY:
station_element = root.createElement("delaystation");
station_element.setAttribute("name", this.stationNames[stationNum]);
/* create the section for service times */
servicetimes_element = station_element.appendChild(root.createElement("servicetimes"));
station_element.appendChild(servicetimes_element);
/* create the section for visits */
visits_element = station_element.appendChild(root.createElement("visits"));
station_element.appendChild(visits_element);
/* for each customer class */
for (int j = 0; j < classes; j++) {
String class_name = this.classNames[j];
/* set service time */
Element st_element = root.createElement("servicetime");
st_element.setAttribute("customerclass", class_name);
st_element.appendChild(root.createTextNode(Double.toString(this.serviceTimes[stationNum][j][0])));
servicetimes_element.appendChild(st_element);
/* set visit */
Element visit_element = root.createElement("visit");
visit_element.setAttribute("customerclass", class_name);
visit_element.appendChild(root.createTextNode(Double.toString(this.visits[stationNum][j])));
visits_element.appendChild(visit_element);
}
break;
case STATION_LD:
station_element = root.createElement("ldstation");
station_element.setAttribute("name", this.stationNames[stationNum]);
station_element.setAttribute("servers", String.valueOf(this.stationServers[stationNum]));
/* create the section for service times */
servicetimes_element = station_element.appendChild(root.createElement("servicetimes"));
station_element.appendChild(servicetimes_element);
/* create the section for visits */
visits_element = station_element.appendChild(root.createElement("visits"));
station_element.appendChild(visits_element);
/* for each customer class */
for (int j = 0; j < classes; j++) {
String class_name = this.classNames[j];
/* set service times, one for each population (values are CSV formatted) */
Element st_element = root.createElement("servicetimes");
st_element.setAttribute("customerclass", class_name);
String serv_t = ArrayUtils.toCSV(serviceTimes[stationNum][j]);
st_element.appendChild(root.createTextNode(serv_t));
servicetimes_element.appendChild(st_element);
/* set visit */
Element visit_element = root.createElement("visit");
visit_element.setAttribute("customerclass", class_name);
visit_element.appendChild(root.createTextNode(Double.toString(this.visits[stationNum][j])));
visits_element.appendChild(visit_element);
}
break;
default:
station_element = null;
}//end switch
return station_element;
}
/**
* Appends solution elements to model element
* @param root root element of Document
* @param parentElement model element where solutions have to be appended
* <br>
* Author: Bertoli Marco
*/
private void appendSolutionElement(Document root, Element parentElement) {
for (int k = 0; k < iterations; k++) {
Element result_element = root.createElement("solutions");
result_element.setAttribute("ok", "true");
if (!isWhatIf()) {
result_element.setAttribute("solutionMethod", "analytical");
} else {
result_element.setAttribute("solutionMethod", "analytical whatif");
result_element.setAttribute("iteration", Integer.toString(k));
result_element.setAttribute("iterationValue", Double.toString(whatIfValues[k]));
}
/* EDITED by Abhimanyu Chugh */
result_element.setAttribute("algCount", Integer.toString(queueLen.size()));
for (SolverAlgorithm alg : queueLen.keySet()) {
Element algorithm_element = (Element) result_element.appendChild(root.createElement("algorithm"));
algorithm_element.setAttribute("name", alg.toString());
if (!alg.isExact()) {
algorithm_element.setAttribute("iterations", Integer.toString(algIterations.get(alg)[k]));
}
for (int i = 0; i < stations; i++) {
Element stationresults_element = (Element) algorithm_element.appendChild(root.createElement("stationresults"));
stationresults_element.setAttribute("station", this.stationNames[i]);
for (int j = 0; j < classes; j++) {
Element classesresults_element = (Element) stationresults_element.appendChild(root.createElement("classresults"));
classesresults_element.setAttribute("customerclass", classNames[j]);
Element Q_element = (Element) classesresults_element.appendChild(root.createElement("measure"));
Q_element.setAttribute("measureType", "Number of Customers");
Q_element.setAttribute("successful", "true");
Q_element.setAttribute("meanValue", Double.toString(this.queueLen.get(alg)[i][j][k]));
Element X_element = (Element) classesresults_element.appendChild(root.createElement("measure"));
X_element.setAttribute("measureType", "Throughput");
X_element.setAttribute("successful", "true");
X_element.setAttribute("meanValue", Double.toString(this.throughput.get(alg)[i][j][k]));
Element R_element = (Element) classesresults_element.appendChild(root.createElement("measure"));
R_element.setAttribute("measureType", "Residence time");
R_element.setAttribute("successful", "true");
R_element.setAttribute("meanValue", Double.toString(this.resTimes.get(alg)[i][j][k]));
Element U_element = (Element) classesresults_element.appendChild(root.createElement("measure"));
U_element.setAttribute("measureType", "Utilization");
U_element.setAttribute("successful", "true");
U_element.setAttribute("meanValue", Double.toString(this.util.get(alg)[i][j][k]));
}
}
}
/* END */
parentElement.appendChild(result_element);
}
}
/* Not used
private void appendMatrixCSV(Document root, Element base, double[][] arr, String outer, String inner) {
// forse devo usare questo per trattare anche il caso LD
Element elems, elem;
int n = arr.length;
elems = root.createElement(outer);
base.appendChild(elems);
for (int i = 0; i < n; i++) {
elem = root.createElement(inner);
// separa i diversi elementi dell'array con ";"
elem.appendChild(root.createTextNode(ArrayUtils.toCSV(arr[i])));
elems.appendChild(elem);
}
} */
/**
* load the state of this object from the Document.
* @return true if the operation was successful.
* WARNING: If the operation fails the object is left in an incorrect state and should be discarded.
*/
public boolean loadDocument(Document doc) {
Node classNode = doc.getElementsByTagName("classes").item(0);
Node stationNode = doc.getElementsByTagName("stations").item(0);
Node ReferenceStationNode = doc.getElementsByTagName("ReferenceStation").item(0);
NodeList descList = doc.getElementsByTagName("description");
NodeList solList = doc.getElementsByTagName("solutions");
//load description
if (descList.item(0) != null) {
if (!loadDescription((Element) descList.item(0))) {
//description loading failed!
return false;
}
} else {
description = "";
}
//NEW
//@author Stefano Omini
//load classes
if (classNode != null) {
if (!loadClasses(classNode)) {
//classes loading failed!
return false;
}
}
//load stations
if (stationNode != null) {
if (!loadStations(stationNode)) {
//stations loading failed!
return false;
}
}
//load ReferenceStation
if (ReferenceStationNode != null) {
if (!loadReferenceStation(ReferenceStationNode)) {
//ReferenceStation loading failed!
return false;
}
}
else
{
loadReferenceStationDefault(classNode);
}
//end NEW
/* EDITED by Abhimanyu Chugh */
/* Algorithm parameters */
NodeList algParams = doc.getElementsByTagName("algParams");
if (algParams.getLength() > 0) {
Element algParam = (Element) algParams.item(0);
Element algType = (Element) algParam.getElementsByTagName("algType").item(0);
setAlgorithmType(SolverAlgorithm.fromString(algType.getAttribute("name")));
setTolerance(Double.parseDouble(algType.getAttribute("tolerance")));
Element compareAlgs = (Element) algParam.getElementsByTagName("compareAlgs").item(0);
NodeList whatIfAlgs = compareAlgs.getElementsByTagName("whatIfAlg");
for (int i = 0; i < whatIfAlgs.getLength(); i++) {
Element alg = (Element) whatIfAlgs.item(i);
String name = alg.getAttribute("name");
SolverAlgorithm algo = SolverAlgorithm.fromString(name);
this.setWhatifAlgorithm(algo, true);
this.setWhatifAlgorithmTolerance(algo, Double.parseDouble(alg.getAttribute("tolerance")));
}
} else {
this.whatifAlgorithms = EnumSet.noneOf(SolverAlgorithm.class);
this.whatifAlgorithmsTollerance = new EnumMap<SolverAlgorithm, Double>(SolverAlgorithm.class);
}
/* END */
/* What-if Analysis - Bertoli Marco */
NodeList whatIfs = doc.getElementsByTagName("whatIf");
if (whatIfs.getLength() > 0) {
// What-if analysis was saved
Element whatIf = (Element) whatIfs.item(0);
setWhatIfType(whatIf.getAttribute("type"));
setWhatIfValues(ArrayUtils.fromCSV(whatIf.getAttribute("values")));
// Try to retrive what-if class informations
setWhatIfClass(-1);
String className = whatIf.getAttribute("className");
if (className != null && !className.equals("")) {
for (int i = 0; i < classes; i++) {
if (classNames[i].equals(className)) {
setWhatIfClass(i);
break;
}
}
}
// Try to retrive what-if station informations
setWhatIfStation(-1);
String stationName = whatIf.getAttribute("stationName");
if (stationName != null && !stationName.equals("")) {
for (int i = 0; i < stations; i++) {
if (stationNames[i].equals(stationName)) {
setWhatIfStation(i);
break;
}
}
}
} else {
// What-if analysis was not saved
iterations = 1;
setWhatIfClass(-1);
setWhatIfStation(-1);
}
//load solution
if (solList.getLength() > 0) {
if (!loadSolution(solList)) {
return false;
}
hasResults = true;
} else {
this.resetResults();
}
// compute flags
resize(stations, classes);
changed = false;
return true;
}
public boolean loadDescription(Element desc) {
description = desc.getFirstChild().getNodeValue();
return true;
}
//NEW
//@author Stefano Omini
public boolean loadClasses(Node classNode) {
classes = Integer.parseInt(((Element) classNode).getAttribute("number"));
classNames = new String[classes];
classTypes = new int[classes];
classData = new double[classes];
//ReferenceStation = new double[classes][];
ReferenceStation = new int[classes];
NodeList classList = classNode.getChildNodes();
int classNum = 0;
maxpop = 0;
Node n;
Element current;
closed = true;
open = true;
/* classes */
for (int i = 0; i < classList.getLength(); i++) {
n = classList.item(i);
if (!(n instanceof Element)) {
continue;
}
current = (Element) n;
classNames[classNum] = current.getAttribute("name");
if (current.getTagName().equals("closedclass")) {
classTypes[classNum] = CLASS_CLOSED;
classData[classNum] = Double.parseDouble(current.getAttribute("population"));
//ReferenceStation[classNum][2] = 0;
//for (int j=0; j<stations ; j++)
// if(stationNames[j] == current.getAttribute("refStation"))
//ReferenceStation[classNum][2] = j;
maxpop += (int) classData[classNum];
open = false;
} else {
classTypes[classNum] = CLASS_OPEN;
classData[classNum] = Double.parseDouble(current.getAttribute("rate"));
//ReferenceStation[classNum][2] = 0;
//for (int j=0; j<stations ; j++)
// if(stationNames[j] == current.getAttribute("refStation"))
//ReferenceStation[classNum][2] = j;
closed = false;
}
classNum++;
}
return true;
}
//end NEW
//NEW
//@author Stefano Omini
public boolean loadStations(Node stationNode) {
stations = Integer.parseInt(((Element) stationNode).getAttribute("number"));
stationNames = new String[stations];
stationTypes = new int[stations];
stationServers = new int[stations];
visits = new double[stations][];
serviceTimes = new double[stations][][];
NodeList stationList = stationNode.getChildNodes();
ld = false;
String statType;
NodeList sTimes;
int stationNum = 0;
/* stations */
Node n;
Element current;
for (int i = 0; i < stationList.getLength(); i++) {
n = stationList.item(i);
if (!(n instanceof Element)) {
continue;
}
current = (Element) n;
statType = current.getTagName();
stationNames[stationNum] = current.getAttribute("name");
if (current.hasAttribute("servers")) {
stationServers[stationNum] = Integer.parseInt(current.getAttribute("servers"));
} else {
stationServers[stationNum] = 1;
}
/* make arrays */
visits[stationNum] = new double[classes];
serviceTimes[stationNum] = new double[classes][];
/* station types and service times */
if (statType.equals("ldstation")) {
//LD
ld = true;
if (maxpop == 0) {
System.err.println("LD station with zero customers");
return false;
}
stationTypes[stationNum] = STATION_LD;
/* create arrays */
for (int k = 0; k < classes; k++) {
//serviceTimes[stationNum] = new double[classes][maxpop + 1];
serviceTimes[stationNum] = new double[classes][maxpop];
}
//Element sTimesElem = (Element) current.getElementsByTagName("servicetimes").item(0);
Element sTimesElem = (Element) current.getElementsByTagName("servicetimes").item(0);
sTimes = sTimesElem.getElementsByTagName("servicetimes");
if (sTimes.getLength() != classes) {
System.err.println("Wrong number of service times sets for LD station " + stationNames[stationNum]);
return false;
}
Element visitsElem = (Element) current.getElementsByTagName("visits").item(0);
NodeList visitsNodeList = visitsElem.getElementsByTagName("visit");
for (int k = 0; k < classes; k++) {
String visit = (visitsNodeList.item(k).getFirstChild()).getNodeValue();
visits[stationNum][k] = Double.parseDouble(visit);
//string of LD service times for class k
Element class_st = (Element) sTimes.item(k);
String stimes = class_st.getFirstChild().getNodeValue();
double[] servt_arr = new double[maxpop];
ArrayUtils.fromCSV(servt_arr, stimes);
System.arraycopy(servt_arr, 0, serviceTimes[stationNum][k], 0, maxpop);
}
} else { //LI or delay
if (statType.equals("delaystation")) {
stationTypes[stationNum] = STATION_DELAY;
} else {
stationTypes[stationNum] = STATION_LI;
}
/* create arrays */
sTimes = current.getElementsByTagName("servicetime");
NodeList visitsNodeList = current.getElementsByTagName("visit");
serviceTimes[stationNum] = new double[classes][1];
visits[stationNum] = new double[classes];
for (int k = 0; k < classes; k++) {
Node node = sTimes.item(k).getFirstChild();
String nodeValue = (node).getNodeValue();
serviceTimes[stationNum][k][0] = Double.parseDouble(nodeValue);
visits[stationNum][k] = Double.parseDouble((visitsNodeList.item(k).getFirstChild()).getNodeValue());
}
}
stationNum++;
}
return true;
}
public boolean loadReferenceStation(Node ReferenceStationNode) {
classes = Integer.parseInt(((Element) ReferenceStationNode).getAttribute("number"));
ReferenceStation = new int[classes];
NodeList classList = ReferenceStationNode.getChildNodes();
int classNum = 0;
Node n;
Element current;
for (int i = 0; i < classList.getLength(); i++) {
n = classList.item(i);
if (!(n instanceof Element)) {
continue;
}
current = (Element) n;
for (int j=0; j<stations ; j++)
if(stationNames[j].equals(current.getAttribute("refStation")))
{
ReferenceStation[classNum] = j;
break;
}
else if(current.getAttribute("refStation").equals("Arrival Process")){
ReferenceStation[classNum] = stationNames.length;
}
else{
ReferenceStation[classNum] = 0;
}
classNum++;
}
return true;
}
public boolean loadReferenceStationDefault(Node classNode) {
classes = Integer.parseInt(((Element) classNode).getAttribute("number"));
for(int i=0;i<classes;i++)
{
ReferenceStation[i] = 0;
}
return true;
}
/**
* Load solutions from xml file
* @param sol NodeList of solution elements
* @return true if load was succesful, false otherwise
*/
public boolean loadSolution(NodeList sol) {
resultsOK = true;
/* EDITED by Abhimanyu Chugh */
if (queueLen == null) {
resetResults();
}
/* END */
for (int i = 0; i < sol.getLength(); i++) {
Element solution = (Element) sol.item(i);
String status = solution.getAttribute("ok");
resultsOK = resultsOK && (status.equals("true"));
if (solution.hasAttribute("algCount")) {
/* EDITED by Abhimanyu Chugh */
int algCount = Integer.parseInt(solution.getAttribute("algCount"));
for (int a = 0; a < algCount; a++) {
Element a_alg = (Element) solution.getElementsByTagName("algorithm").item(a);
SolverAlgorithm alg = SolverAlgorithm.fromString(a_alg.getAttribute("name"));
if (alg == null) {
continue;
}
boolean isApprox = !alg.isExact();
if (i == 0) {
queueLen.put(alg, new double[stations][classes][iterations]);
throughput.put(alg, new double[stations][classes][iterations]);
resTimes.put(alg, new double[stations][classes][iterations]);
util.put(alg, new double[stations][classes][iterations]);
if (isApprox) {
algIterations.put(alg, new int[iterations]);
}
}
ArrayUtils.copy2to3(loadResultsMatrix(a_alg, stations, classes, "Number of Customers"), queueLen.get(alg), i);
ArrayUtils.copy2to3(loadResultsMatrix(a_alg, stations, classes, "Throughput"), throughput.get(alg), i);
ArrayUtils.copy2to3(loadResultsMatrix(a_alg, stations, classes, "Residence time"), resTimes.get(alg), i);
ArrayUtils.copy2to3(loadResultsMatrix(a_alg, stations, classes, "Utilization"), util.get(alg), i);
if (isApprox) {
int algIters = Integer.parseInt(a_alg.getAttribute("iterations"));
algIterations.get(alg)[i] = algIters;
}
}
/* END */
} else {
SolverAlgorithm alg = SolverAlgorithm.EXACT;
if (i == 0) {
queueLen.put(alg, new double[stations][classes][iterations]);
throughput.put(alg, new double[stations][classes][iterations]);
resTimes.put(alg, new double[stations][classes][iterations]);
util.put(alg, new double[stations][classes][iterations]);
}
ArrayUtils.copy2to3(loadResultsMatrix(solution, stations, classes, "Number of Customers"), queueLen.get(alg), i);
ArrayUtils.copy2to3(loadResultsMatrix(solution, stations, classes, "Throughput"), throughput.get(alg), i);
ArrayUtils.copy2to3(loadResultsMatrix(solution, stations, classes, "Residence time"), resTimes.get(alg), i);
ArrayUtils.copy2to3(loadResultsMatrix(solution, stations, classes, "Utilization"), util.get(alg), i);
}
}
return true;
}
//NEW
//@author Stefano Omini
public double[][] loadResultsMatrix(Element base, int len1, int len2, String res) {
//matrix of results
double[][] arr = new double[len1][len2];
if (base.getElementsByTagName("stationresults").getLength() != len1) {
return null;
}
for (int i = 0; i < len1; i++) {
Element s_res = (Element) base.getElementsByTagName("stationresults").item(i);
for (int c = 0; c < len2; c++) {
Element n_cls = (Element) s_res.getElementsByTagName("classresults").item(c);
NodeList measure_list = n_cls.getElementsByTagName("measure");
Element measure;
String value = null;
for (int m = 0; m < measure_list.getLength(); m++) {
measure = (Element) measure_list.item(m);
//Below IF clause is added for backward compatibility of the Perf Index : Number of customers
//as previously it was known as Queue Length.
if (res.equalsIgnoreCase("Number of Customers")) {//This is the present name of Label
if (measure.getAttribute("measureType").equalsIgnoreCase("Queue length")) {//Previously known as "Queue length" in old JMVA files.
res = "Queue length";
}
}
if (measure.getAttribute("measureType").equalsIgnoreCase(res)) {
//it's the measure we are searching for
value = measure.getAttribute("meanValue");
break;
}
}
//Element r = (Element) n_cls.getElementsByTagName(res).item(0);
//String value = r.getFirstChild().getNodeValue();
if (value != null) {
arr[i][c] = Double.parseDouble(value);
} else {
arr[i][c] = 0.0;
}
}
}
return arr;
}
//end NEW
//methods for aggregate results retrieval
/* EDITED by Abhimanyu Chugh */
/**Returns per-class aggregate for throughput*/
public double[][] getPerClassX(SolverAlgorithm alg) {
if (throughput == null) {
return null;
} else {
double[][] retVal = new double[classes][iterations];
double[][][] throughput = this.throughput.get(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
//scan columns to get one value per column
for (int i = 0; i < retVal.length; i++) {
//scan cells of each column
for (int j = 0; j < throughput.length; j++) {
//throughput is ratio of specific throughput on specific num of visits
if (visits[j][i] != 0) {
retVal[i][k] = throughput[j][i][k] / visits[j][i];
break;
} else {
//if all visits for a class (why is this included in model???)
//throughput for that class is 0
if (j == throughput.length - 1) {
retVal[i][k] = 0;
}
}
}
}
}
return retVal;
}
}
/**Returns per-station aggregate for throughput*/
public double[][] getPerStationX(SolverAlgorithm alg) {
if (throughput == null) {
return null;
} else {
double[][] retVal = new double[stations][iterations];
double[][][] throughput = this.throughput.get(alg);
for (int i = 0; i < retVal.length; i++) {
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
retVal[i][k] = 0;
for (int j = 0; j < throughput[i].length; j++) {
retVal[i][k] += throughput[i][j][k];
}
}
}
return retVal;
}
}
/**Returns global aggregate for throughput*/
public double[] getGlobalX(SolverAlgorithm alg) {
double[] retVal = new double[iterations];
double[][] aggs = getPerClassX(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
if (throughput == null) {
retVal[k] = Double.NaN;
} else {
if (aggs != null) {
for (double[] agg : aggs) {
retVal[k] += agg[k];
}
} else {
retVal[k] = Double.NaN;
}
}
}
return retVal;
}
/**Returns per-class aggregate for queue lenghts*/
public double[][] getPerClassQ(SolverAlgorithm alg) {
if (queueLen == null) {
return null;
}
double[][] retVal = new double[classes][iterations];
double[][][] queueLen = this.queueLen.get(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
//first scan columns
for (int i = 0; i < retVal.length; i++) {
retVal[i][k] = 0;
//then rows
for (double[][] element : queueLen) {
retVal[i][k] += element[i][k];
}
}
}
return retVal;
}
/**Returns per-station aggregate for queue lenghts*/
public double[][] getPerStationQ(SolverAlgorithm alg) {
if (queueLen == null) {
return null;
}
double[][] retVal = new double[stations][iterations];
double[][][] queueLen = this.queueLen.get(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
for (int i = 0; i < queueLen.length; i++) {
retVal[i][k] = 0;
for (int j = 0; j < queueLen[i].length; j++) {
retVal[i][k] += queueLen[i][j][k];
}
}
}
return retVal;
}
/**Returns global aggregate for queue lenghts*/
public double[] getGlobalQ(SolverAlgorithm alg) {
if (queueLen == null) {
return null;
}
double[] retVal = new double[iterations];
double[][] aggs = getPerClassQ(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
if (aggs != null) {
for (double[] agg : aggs) {
retVal[k] += agg[k];
}
} else {
retVal[k] = Double.NaN;
}
}
return retVal;
}
/**Returns per-class aggregate for residence times*/
public double[][] getPerClassR(SolverAlgorithm alg) {
if (resTimes == null) {
return null;
}
double[][] retVal = new double[classes][iterations];
double[][][] resTimes = this.resTimes.get(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
for (int i = 0; i < retVal.length; i++) {
retVal[i][k] = 0;
for (double[][] resTime : resTimes) {
retVal[i][k] += resTime[i][k];
}
}
}
return retVal;
}
/**Returns per-station aggregate for residence times*/
public double[][] getPerStationR(SolverAlgorithm alg) {
if (resTimes == null) {
return null;
}
double[][] retVal = new double[stations][iterations];
double[][] xClassAggs = getPerClassX(alg);
double[][][] resTimes = this.resTimes.get(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
for (int i = 0; i < retVal.length; i++) {
retVal[i][k] = 0;
double dividend = 0;
for (int j = 0; j < resTimes[i].length; j++) {
if (xClassAggs != null) {
retVal[i][k] += xClassAggs[j][k] * resTimes[i][j][k];
dividend += xClassAggs[j][k];
} else {
return null;
}
}
if (dividend != 0) {
retVal[i][k] /= dividend;
} else {
retVal[i][k] = 0;
}
}
}
return retVal;
}
/**Returns system response time*/
public double[] getGlobalR(SolverAlgorithm alg) {
if (resTimes == null) {
return null;
}
double[] retVal = new double[iterations];
double[][] xClassAggs = getPerClassX(alg);
double[][] aggs = getPerClassR(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
double dividend = 0;
if (aggs != null) {
for (int i = 0; i < aggs.length; i++) {
retVal[k] += xClassAggs[i][k] * aggs[i][k];
dividend += xClassAggs[i][k];
}
if (dividend > 0) {
retVal[k] /= dividend;
} else {
retVal[k] = 0;
}
} else {
retVal[k] = Double.NaN;
}
}
return retVal;
}
/**Returns per-class aggregate for utilization*/
public double[][] getPerClassU(SolverAlgorithm alg) {
if (util == null) {
return null;
} else {
/* Disabled as this measure is unsensed... Returns an array with negatives instead
double[] retVal = new double[classes];
for(int i=0; i<retVal.length; i++){
retVal[i] = 0;
for(int j=0; j<util.length; j++){
retVal[i] += util[j][i];
}
}
return retVal;
*/
double[][] neg = new double[classes][iterations];
for (int k = 0; k < classes; k++) {
Arrays.fill(neg[k], -1.0);
}
return neg;
}
}
public double[][] getPerStationU(SolverAlgorithm alg) {
if (util == null) {
return null;
}
double[][] retVal = new double[stations][iterations];
double[][][] util = this.util.get(alg);
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
for (int i = 0; i < retVal.length; i++) {
retVal[i][k] = 0;
for (int j = 0; j < util[i].length; j++) {
retVal[i][k] += util[i][j][k];
}
}
}
return retVal;
}
public double[] getGlobalU(SolverAlgorithm alg) {
if (util == null) {
return null;
} else {
/* Disabled as this measure is unsensed... Returns an array with negatives instead
double retVal = 0;
double[] aggs = getPerStationU();
if(aggs!=null){
for(int i=0; i<aggs.length; i++) retVal += aggs[i];
return retVal;
}else return Double.NaN;
*/
double[] neg = new double[iterations];
Arrays.fill(neg, -1.0);
return neg;
}
}
//Added by ASHANKA START
//System Power
//Unlike other performance indices we don't have station level
//aggregate and individual
public double[][] getPerClassSP(SolverAlgorithm alg) {
if (resTimes == null) {
return null;
}
double[][] retVal = new double[classes][iterations];
// Scans for every iteration (what if analysis)
for (int k = 0; k < iterations; k++) {
for (int i = 0; i < retVal.length; i++) {
try {
retVal[i][k] = getPerClassX(alg)[i][k] / getPerClassR(alg)[i][k];
} catch (ArithmeticException ae) {
retVal[i][k] = 0;
}
}
}
return retVal;
}
public double[] getGlobalSP(SolverAlgorithm alg) {
if (resTimes == null) {
return null;
}
double[] retVal = new double[iterations];
for (int k = 0; k < iterations; k++) {
try {
retVal[k] = getGlobalX(alg)[k] / getGlobalR(alg)[k];
} catch (ArithmeticException e) {
retVal[k] = 0;
}
}
return retVal;
}
/* END */
//Added by ASHANKA STOP
/**
* This method tells if visits were set or are all unitary (or zero if
* corresponding service time is zero). This is used to show correct panel layout
* upon loading
* @return true iff visits were not set
*/
public boolean areVisitsSet() {
return unitaryVisits;
}
//NEW Federico Dall'Orso
/**Randomizes model's service times and visits.*/
public void randomizeModelData() {
double globRate = globalArrRate();
for (int i = 0; i < serviceTimes.length; i++) {
for (int j = 0; j < serviceTimes[i].length; j++) {
for (int k = 0; k < serviceTimes[i][j].length; k++) {
if (j < classTypes.length) {
if (classTypes[j] == CLASS_CLOSED) {
serviceTimes[i][j][k] = MAXRAND * Math.exp(-Math.random() * MAXRANGE);
} else {
if (globRate != 0) {
serviceTimes[i][j][k] = Math.random() * (0.9) / globRate;
} else {
serviceTimes[i][j][k] = Math.random();
}
}
}
}
}
}
for (int i = 0; i < visits.length; i++) {
for (int j = 0; j < visits[i].length; j++) {
visits[i][j] = 1;
}
}
}
//calculates global arrival rate for open classes
private double globalArrRate() {
double sum = 0;
for (int i = 0; i < classTypes.length && i < classData.length; i++) {
if (classTypes[i] == CLASS_OPEN) {
sum += classData[i];
}
}
return sum;
}
//END
//---- Methods for What-If analysis ---- Bertoli Marco ------------------------------
/**
* Tells if this model includes a what-if analysis
* @return true if this model includes a what-if analysis
*/
public boolean isWhatIf() {
return iterations > 1;
}
/**
* Removes What-if analysis from current model
*/
public void removeWhatIf() {
if (iterations != 1) {
iterations = 1;
changed = true;
}
}
/**
* Sets the array of values used for what-if analysis
* @param values vector with values to be used in iterations of what-if analysis
*/
public boolean setWhatIfValues(double[] values) {
if (whatIfValues == null && values == null) {
return false;
}
if (whatIfValues == null || !Arrays.equals(whatIfValues, values)) {
whatIfValues = values;
if (values != null) {
iterations = values.length;
} else {
iterations = 1;
}
changed = true;
resultsOK = false;
return true;
}
return true;
}
/**
* Sets class used for what-if analysis
* @param classNum ordered number of selected class or -1 for every class
* @return true if data was changed, false otherwise
*/
public boolean setWhatIfClass(int classNum) {
if (whatIfClass != classNum) {
whatIfClass = classNum;
changed = true;
resultsOK = false;
return true;
}
return false;
}
/**
* Sets station used for what-if analysis
* @param stationNum ordered number of selected station or -1 for every station
* @return true if data was changed, false otherwise
*/
public boolean setWhatIfStation(int stationNum) {
if (whatIfStation != stationNum) {
whatIfStation = stationNum;
changed = true;
resultsOK = false;
return true;
}
return false;
}
/**
* Sets type of what-if analysis
* @param type WHAT_IF_ARRIVAL, WHAT_IF_CUSTOMERS, WHAT_IF_MIX, WHAT_IF_DEMANDS
* @see ExactConstants
* @return true if data was changed, false otherwise
*/
public boolean setWhatIfType(String type) {
if (whatIfType == null && type == null) {
return false;
}
if (whatIfType == null || !whatIfType.equalsIgnoreCase(type)) {
whatIfType = type;
changed = true;
resultsOK = false;
return true;
}
return false;
}
/**
* Returns type of what-if analysis
* @return WHAT_IF_ARRIVAL, WHAT_IF_CUSTOMERS, WHAT_IF_MIX, WHAT_IF_DEMANDS
* @see ExactConstants
*/
public String getWhatIfType() {
return whatIfType;
}
/**
* Returns index of station selected for what-if analysis or -1 if every station is selected
* @return index of station selected for what-if analysis or -1 if every station is selected
*/
public int getWhatIfStation() {
return whatIfStation;
}
/**
* Returns index of class selected for what-if analysis or -1 if every class is selected
* @return index of class selected for what-if analysis or -1 if every class is selected
*/
public int getWhatIfClass() {
return whatIfClass;
}
/**
* Returns the array of values used for what-if analysis
* @return the array of values used for what-if analysis
*/
public double[] getWhatIfValues() {
return whatIfValues;
}
/**
* This method is used to generate a suitable vector of values for what-if analysis.
* @param type type of what-if analysis to be performed. (WHAT_IF_ARRIVAL,
* WHAT_IF_CUSTOMERS, WHAT_IF_MIX, WHAT_IF_DEMANDS)
* @param from initial value
* @param to final value
* @param iterations expected number of iterations (will be adjusted to be compliant
* with specified from and to values in WHAT_IF_CUSTOMERS and WHAT_IF_MIX)
* @param ClassRef index of class to be analyzed or -1 for all classes
* @param stationRef index of station to be analyzed (only for WHAT_IF_DEMANDS)
* @return suitable array of iterations to be performed
*/
public double[] generateWhatIfValues(String type, double from, double to, int iterations, int ClassRef, int stationRef) {
double[] ret, tmp;
int n = iterations - 1;
boolean inverted = false; // tells if 'from' and 'to' values were exchanged
// order from and to values
double f, t;
if (from < to) {
f = from;
t = to;
} else if (from > to) {
f = to;
t = from;
inverted = true;
} else {
// In the case of overlapping from and to values, returns a single-number array
return new double[] { from };
}
// Avoid 0 arrival rate
if (type.equals(WHAT_IF_ARRIVAL) && f == 0.0) {
f = 1e-5;
}
// Arrival rate and service Demands: this are really simple.
if (type.equals(WHAT_IF_ARRIVAL) || type.equals(WHAT_IF_DEMANDS)) {
ret = new double[iterations];
for (int i = 0; i <= n; i++) {
ret[i] = (f * (n - i) + i * t) / n;
}
}
// Number of customers: this is complex because only integer values are allowed.
else if (type.equals(WHAT_IF_CUSTOMERS)) {
// Single class
if (ClassRef >= 0) {
tmp = new double[iterations];
int c = 0; // a simple counter to remove duplicates
for (int i = 0; i < iterations; i++) {
tmp[c] = Math.rint((f * (n - i) + i * t) / n);
// Increment counter only if last element was not repeated
if (c < 1 || tmp[c] != tmp[c - 1]) {
c++;
}
}
// Pack target array
ret = new double[c];
System.arraycopy(tmp, 0, ret, 0, c);
}
// Multiclass
else {
// An array that will hold number of customers for each closed class.
int[] customers = new int[classes];
int c = 0; // counter of closed classes
for (int i = 0; i < classes; i++) {
if (classTypes[c] == CLASS_CLOSED) {
customers[c++] = (int) classData[i];
}
}
// Inverse of Highest Common Factor is the minimum percentage allowed.
int hcf = hcf(customers, c);
if (hcf == 0) {
hcf = 1;
}
double step = 1.0 / hcf;
if (f < step) {
f = step;
}
if (t < f) {
return new double[] { f };
}
c = 0; // counter of created steps
tmp = new double[iterations];
for (int i = 0; i < iterations; i++) {
tmp[c] = Math.rint((f / step * (n - i) + i * t / step) / n);
// Increment counter only if last element was not repeated
if (c < 1 || tmp[c] != tmp[c - 1]) {
c++;
}
}
// Now creates results array
ret = new double[c];
for (int i = 0; i < c; i++) {
ret[i] = tmp[i] * step;
}
}
}
// Population mix: this is complex because only integer values are allowed.
else if (type.equals(WHAT_IF_MIX)) {
int cl2 = -1;
// Finds second class
for (int i = 0; i < classes; i++) {
if (classTypes[i] == CLASS_CLOSED && i != ClassRef) {
cl2 = i;
break;
}
}
int N = (int) (classData[ClassRef] + classData[cl2]);
double step = 1.0 / N;
if (f < step) {
f = step;
}
if (t > (N - 1) * step) {
t = (N - 1) * step;
}
if (t < f) {
return new double[] { f };
}
int c = 0; // counter of created steps
tmp = new double[iterations];
for (int i = 0; i < iterations; i++) {
tmp[c] = Math.rint((f / step * (n - i) + i * t / step) / n);
// Increment counter only if last element was not repeated
if (c < 1 || tmp[c] != tmp[c - 1]) {
c++;
}
}
// Now creates results array
ret = new double[c];
for (int i = 0; i < c; i++) {
ret[i] = tmp[i] * step;
}
} else {
ret = null;
}
// Inverts results if from and to values were exchanged
if (inverted && ret != null) {
double[] inv = new double[ret.length];
for (int i = 0; i < ret.length; i++) {
inv[ret.length - 1 - i] = ret[i];
}
ret = inv;
}
return ret;
}
/**
* This method will recalculate whatif analysis values after the initial values were changed. At first
* detects changes, than applies modifications. If what-if analysis is no longer appliable, resets it.
*/
public void recalculateWhatifValues() {
if (whatIfType == null) {
return;
}
HashSet<Integer> closedClasses = new HashSet<Integer>();
HashSet<Integer> openClasses = new HashSet<Integer>();
for (int i = 0; i < classTypes.length; i++) {
if (classTypes[i] == CLASS_OPEN) {
openClasses.add(new Integer(i));
} else if (classTypes[i] == CLASS_CLOSED) {
closedClasses.add(new Integer(i));
}
}
// Checks validity first
if (classTypes.length <= whatIfClass || stationTypes.length <= whatIfStation) {
removeWhatIf();
} else if (WHAT_IF_ARRIVAL.equals(whatIfType)) {
if (openClasses.size() == 0 || (whatIfClass >= 0 && classTypes[whatIfClass] != CLASS_OPEN)) {
removeWhatIf();
}
} else if (WHAT_IF_CUSTOMERS.equals(whatIfType)) {
if (closedClasses.size() == 0 || (whatIfClass >= 0 && classTypes[whatIfClass] != CLASS_CLOSED)) {
removeWhatIf();
}
} else if (WHAT_IF_MIX.equals(whatIfType)) {
if (closedClasses.size() != 2 || (whatIfClass >= 0 && classTypes[whatIfClass] != CLASS_CLOSED)) {
removeWhatIf();
}
} else if (WHAT_IF_DEMANDS.equals(whatIfType)) {
if (whatIfStation >= 0 && stationTypes[whatIfStation] == STATION_LD) {
removeWhatIf();
}
}
// If what-if is still valid, updates initial values
if (whatIfType != null) {
// Check if class data was changed
if (whatIfClass >= 0) {
if ((WHAT_IF_ARRIVAL.equals(whatIfType) || WHAT_IF_CUSTOMERS.equals(whatIfType)) && classData[whatIfClass] != whatIfValues[0]) {
setWhatIfValues(generateWhatIfValues(whatIfType, classData[whatIfClass], whatIfValues[iterations - 1], iterations, whatIfClass,
whatIfStation));
} else if (WHAT_IF_DEMANDS.equals(whatIfType)
&& serviceTimes[whatIfStation][whatIfClass][0] * visits[whatIfStation][whatIfClass] != whatIfValues[0]) {
setWhatIfValues(generateWhatIfValues(whatIfType,
serviceTimes[whatIfStation][whatIfClass][0] * visits[whatIfStation][whatIfClass], whatIfValues[iterations - 1],
iterations, whatIfClass, whatIfStation));
} else if (WHAT_IF_MIX.equals(whatIfType)) {
// Check that no fractionary values are used
int class2 = -1;
for (Integer integer : closedClasses) {
int idx = integer.intValue();
if (idx != whatIfClass) {
class2 = idx;
break;
}
}
for (int i = 0; i < iterations; i++) {
double fClassVal = whatIfValues[i] * classData[whatIfClass];
double sClassVal = (1 - whatIfValues[i]) * classData[class2];
if (Math.abs(fClassVal - Math.rint(fClassVal)) > 1e-8 || Math.abs(sClassVal - Math.rint(sClassVal)) > 1e-8) {
setWhatIfValues(generateWhatIfValues(whatIfType, 0, 1, iterations, whatIfClass, whatIfStation));
break;
}
}
}
}
}
}
/**
* Helper method that finds Highest Common Factor in a given array of integer values.
* @param values array of integer values. MUST have at least 2 elements
* @param len number of elements to be considered in input array (must be at least 2)
* @return found hcf
*/
private static int hcf(int[] values, int len) {
int min, max, tmp;
min = values[0];
for (int i = 1; i < len; i++) {
// Finds minimum value between min (previous hcf) and values[i]
if (values[i] > min) {
max = values[i];
} else {
max = min;
min = values[i];
}
tmp = max % min;
while (tmp > 0) {
max = min;
min = tmp;
tmp = max % min;
}
// At this point 'min' holds the hcf value
}
return min;
}
/**
* This function will check if one or more resources are in saturation. This will
* consider each iteration of what-if analysis if present.
* @return NO_SATURATION if everything is okay, SATURATION if a class saturation is
* detected with specified parameters and SATURATION_WHATIF if a saturation will be caused
* by whatif values.
*/
public int checkSaturation() {
// Checks saturation without what-if analysis
if (checkForSaturation(classData, visits, serviceTimes, stationServers)) {
return SATURATION;
}
if (isWhatIf()) {
double maxValue = whatIfValues[iterations - 1];
// Checks if values are inverted
if (whatIfValues[0] > maxValue) {
maxValue = whatIfValues[0];
}
// What if arrival rates
if (whatIfType.equals(WHAT_IF_ARRIVAL)) {
double[] newClassData = (double[]) classData.clone();
// Change arrival rate of a single class only
if (whatIfClass >= 0) {
newClassData[whatIfClass] = maxValue;
}
// Change arrival rate of all open classes
else {
for (int i = 0; i < classes; i++) {
if (classTypes[i] == ExactConstants.CLASS_OPEN) {
newClassData[i] *= maxValue;
}
}
}
if (checkForSaturation(newClassData, visits, serviceTimes, stationServers)) {
return SATURATION_WHATIF;
}
}
// What if service demands
else if (whatIfType.equals(WHAT_IF_DEMANDS)) {
double[][][] newServiceTimes = (double[][][]) serviceTimes.clone();
double[][] newVisits = (double[][]) visits.clone();
// Change service demands of a LI station for a single (open) class only
if (whatIfClass >= 0 && classTypes[whatIfClass] == CLASS_OPEN) {
newServiceTimes[whatIfStation][whatIfClass][0] = maxValue;
newVisits[whatIfStation][whatIfClass] = 1;
}
// Change service demands of a LI station for all (open) classes
else {
for (int i = 0; i < classes; i++) {
if (classTypes[i] == ExactConstants.CLASS_OPEN) {
newServiceTimes[whatIfStation][i][0] *= maxValue;
}
}
}
if (checkForSaturation(classData, newVisits, newServiceTimes, stationServers)) {
return SATURATION_WHATIF;
}
}
}
return NO_SATURATION;
}
public static final int NO_SATURATION = 0;
public static final int SATURATION = 1;
public static final int SATURATION_WHATIF = 2;
/**
* Checks current model for saturation, given arrival rates for customer classes
* @param classData arrival rates for customer classes
* @param visits number of visits for station
* @param serviceTimes service times for station
* @param stationServers number of servers for each station
* @return true if model will saturate, false otherwise
*/
private boolean checkForSaturation(double[] classData, double[][] visits, double[][][] serviceTimes, int[] stationServers) {
for (int i = 0; i < stations; i++) {
if (stationTypes[i] == STATION_DELAY) {
//delay station: don't check saturation
continue;
}
//utiliz is the aggregate utilization for station j
double utiliz = 0;
for (int j = 0; j < classes; j++) {
//consider only open classes
if (classTypes[j] == CLASS_OPEN) {
utiliz += classData[j] * visits[i][j] * serviceTimes[i][j][0];
}
}
if (utiliz >= stationServers[i]) {
return true;
}
}
//there are no stations in saturation
return false;
}
//-----------------------------------------------------------------------------------
}