// $Id: Process.pizza,v 1.16 1999/05/24 15:08:00 gcla Exp $ // file : Process.pizza // description : Build up a collection of SPA routines // project : PEPAroni (mjta) // author : Graham Clark // Time-stamp : <15 May 99 14:04:48 gcla> // Peparoni Stochastic Process Algebra (PEPA) Discrete Event Simulator // Copyright (C) 1999 Graham Clark // 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; see the file COPYING. If not, write to // the Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. // ---------------------------------------------------------------------- // // Remember, you can't subtype on mutable collections so just as // Object[] !<= String[], I can't expect // Set <= Set // Just consider adding an item using a function Set put(...) // TODO: // o Override set to produce a decent implementation, with union, inter etc // o This would be even better because // (a) I shouldn't allow tau in an action set // (b) I could have my own toString function // o Actions should be factored out in the parser so that they may be strings // but they also may have to start with a lowercase, say // o Remove public from all classes except those that need it! They're all // package local after all // o Support for silent actions etc // o Make T available as well as infty in parsing // x Can I have the empty cooperation set? Or || even? // o since competeDerivatives() is written, derivatives() should really use it // x check that p ->a p works - i.e. derivs back to oneself. // x discrepancy in syntax - I need || to work as <> in the Java version // x check whether I get a memory leak or not... // o check whether memory leak is still present // o fix bug where an activity is marked WAITING, then another // finishes, enabling a passive which would allow the WAITING to // proceed; but it doesn't. Ah, but when should it happen? Does it // happen immediately? // Recall why I have a CSet. It's because an ordinary set doesn't use // the equals method for comparing entries in a set. My CSet does use // equals(). // Commentary: // // An Activity contains an ActivitySim. An ActivitySim is the thing // that gets scheduled, and so on. The ActivitySim has a pointer back // to the owning activity. Peparoni.me holds the global state of the // simulation process, so the sims don't need any kind of reference // back to it. package pepa.process; import java.io.StringReader; import java.io.FileReader; import java.io.Reader; import java.io.IOException; import java.util.Vector; import java.util.Random; import java_cup.runtime.*; import pizza.util.Set; import pizza.lang.*; import pizza.lang.List.*; import pizza.util.*; import pizza.util.Option.*; import pepa.process.Rate.*; import pepa.process.PepaProcess.*; import eduni.simjava.*; //import simjavaextensions.*; import gnu.getopt.*; // Merge with ActionInt? public interface EqType { public boolean equals(A a); } public interface EqAndHash extends EqType { public int hashCode(); } public interface ActionInt extends EqAndHash { public String typeName(); //public boolean equals(Action a); } public interface ProcessInt> extends EqAndHash { public Set> derivatives(); public Set derivativeSet(); public CSet actionSet(); public Set derivativesBy(Action a); public Set derivativesBy(CSet as); } // Class definitions public class Either { case Inl(A elem); case Inr(B elem); } public class Rate { case Spec(Double elem); case Unspec(Double elem); public Double specValue() { switch (this) { case Unspec(Double w): throw (new Error("Tried to get concrete value of unspecified rate " +toString())); case Spec(Double r): return r; } } public Rate min(Rate r) { switch(this) { case Spec(Double r1): switch(r) { case Spec(Double r2): if ((double)r1 < (double)r2) { return this; } else { return r; } case Unspec(_): return this; } case Unspec(Double r1): switch(r) { case Spec(_): return r; case Unspec(Double r2): if ((double)r1 < (double)r2) { return this; } else { return r; } } } } public Rate plus(Rate r) { switch(this) { case Spec(Double r1): switch(r) { case Spec(Double r2): return Rate.Spec((Double)((double)r1 + (double)r2)); case Unspec(_): throw (new Error("Tried to add an unspecified and specified rate")); } case Unspec(Double ur1): switch(r) { case Spec(_): throw (new Error("Tried to add an unspecified and specified rate")); case Unspec(Double ur2): return Rate.Unspec((Double)((double)ur1 + (double)ur2)); } } } public Rate minus(Rate r) { switch(this) { case Spec(Double r1): switch(r) { case Spec(Double r2): return Rate.Spec((Double)((double)r1 - (double)r2)); case Unspec(_): throw (new Error("Tried to subtract with an unspecified and specified rate")); } case Unspec(Double r1): switch(r) { case Spec(_): throw (new Error("Tried to subtract with an unspecified and specified rate")); case Unspec(Double r2): return Rate.Unspec((Double)((double)r1 - (double)r2)); } } } public Rate div(Rate r) { switch(this) { case Spec(Double r1): switch(r) { case Spec(Double r2): return Rate.Spec((Double)((double)r1 / (double)r2)); case Unspec(_): throw (new Error("Tried to divide a specified by an unspecified rate")); } case Unspec(Double r1): switch(r) { case Spec(Double r2): return Rate.Unspec((Double)((double)r1 / (double)r2)); //throw (new Error("Tried to divide an unspecified and specified rate")); case Unspec(Double r2): return Rate.Spec((Double)((double)r1 / (double)r2)); } } } public Rate times(Rate r) { switch(this) { case Spec(Double r1): switch(r) { case Spec(Double r2): return Rate.Spec((Double)((double)r1 * (double)r2)); case Unspec(Double r2): return Rate.Unspec((Double)((double)r1 * (double)r2)); //throw (new Error("Tried to multiply an unspecified and specified rate")); } case Unspec(Double r1): switch(r) { case Spec(Double r2): return Rate.Unspec((Double)((double)r1 * (double)r2)); //throw (new Error("Tried to multiply an unspecified and specified rate")); case Unspec(_): throw (new Error("Tried to multiply two unspecified rates")); //return Rate.Unspec((Double)((double)r1 * (double)r2)); } } } public boolean equals(Rate r) { switch(this) { case Unspec(Double w): switch (r) { case Unspec(Double wa): return w.equals(wa); case _: return false; } case Spec(Double w): switch (r) { case Spec(Double ra): return w.equals(ra); case _: return false; } } } public boolean greaterThan(Rate r) { switch(this) { case Unspec(Double w): switch (r) { case Unspec(Double ra): return ((double)w > (double)ra); case _: return true; } case Spec(Double w): switch (r) { case Spec(Double ra): return ((double)w > (double)ra); case _: return false; } } } } public class Dist { public static int NONE = 0; public static int EXPONENTIAL = 1; public static int NORMAL = 2; public static int UNIFORM = 3; // used to seed each new instantiation of an activity public static int uniq = 0; private static Random uniqgen = new Random((long)84524); // exponential by default public int disttype = EXPONENTIAL; // unfortunately SimJava doesn't have an abstract class for these, // so this'll have to do private Object distribution; // For everything other than an exponential distribution, this // should be specified, and is a runtime error if it isn't (parse // time to be precise). This isn't strictly necessary as it will // be used to initialise the distribution object, but handy to // have around for the passive case private Rate rate; public static void reseed() { uniq = uniqgen.nextInt(); } Dist(Object distribution, int disttype, Rate rate) { super(); this.distribution = distribution; this.disttype = disttype; this.rate = rate; } // Assume exponential Dist(Rate rate) { switch (rate) { case Unspec(Double w): this.distribution = null; this.disttype = NONE; this.rate = rate; break; case Spec(Double r): Sim_negexp_obj ne = new Sim_negexp_obj("RV("+((Double)r).toString()+")", 1/(double)r, uniq); reseed(); this.distribution = ne; this.disttype = disttype; this.rate = rate; break; } } public Rate rate() { return this.rate; } public Object dist() { return this.distribution; } public double sample() { if (disttype == NONE) { throw (new Error("Attempt to sample from null distribution!")); } if (disttype == EXPONENTIAL) { return ((Sim_negexp_obj)(this.distribution)).sample(); } if (disttype == NORMAL) { return ((Sim_normal_obj)(this.distribution)).sample(); } if (disttype == UNIFORM) { return ((Sim_uniform_obj)(this.distribution)).sample(); } // should never get here, but compiler doesn't spot it return -1.0; } public boolean equals(Dist d2) { return ((this.disttype == d2.disttype) && (this.rate().equals(d2.rate()))); } } // public class ActionFactory { // private static Hashtable, Action> allactions = // new Hashtable(); // public static Action getAction(Option name) { // if (allactions.containsKey(name)) { // return allactions.get(name); // } else { // Action a = new Action(name); // allactions.put(name, a); // return a; // } // } // // public ActionFactory() { // // super(); // // } // } public class Action implements ActionInt { private String name = "Unnamed"; private String silentStr = "tau"; private boolean issilent = false; Action(Option name) { switch (name) { case None: this.issilent = true; break; case Some(String s): this.issilent = false; this.name = s; break; } } Action(String name) { this(Some(name)); } Action() { this(None); } public boolean equals(Action a) { return ((a.isSilent() && this.isSilent()) || (a.typeName().equals(this.typeName()))); } public String typeName() { return this.name; } public boolean isSilent() { return this.issilent; } public String toString() { if (this.isSilent()) { return silentStr; } else { return this.typeName(); } } public int hashCode() { return this.name.hashCode(); } } public class Activity extends Action { // None represents unspecified //private Option rate = None; private static Double defaultweight = (Double)1.0; private Dist dist; //private Rate rate = Unspec(defaultweight); // keep note of how far down the clock has run etc public ActivitySim as = null; private String rateUnspecStr = "T"; // First opt means silent or not // Second opt means T or not Activity(Option name, Dist dist) { super(name); this.dist = dist; // horrible^10 kludge! gcla later if (Peparoni.buildingModel) { Debug.println(5, " %%%% making a new activitysim"); as = new ActivitySim(this); } } // inl is a real rate, inr is a weighting given to an infty Activity(Option name, Rate rate) { // assumes exponential this(name, new Dist(rate)); } Activity(String name, Dist dist) { this(Some(name), dist); } Activity(Dist dist) { this(None, dist); } Activity(String name, Rate rate) { this(Some(name), rate); } Activity(Rate rate) { this(None, rate); } Activity(Option name, Double rate) { this(name, Spec(rate)); } Activity(Option name) { this(name, Unspec(defaultweight)); } Activity(String name, double rate) { this(name, (Double)rate); } Activity(double rate) { this((Double)rate); } Activity(String name, int rate) { this(name, (Integer)rate); } Activity(int rate) { this((Integer)rate); } Activity(String name, Integer rate) { this(name, rate.doubleValue()); } Activity(Integer rate) { this(rate.doubleValue()); } Activity(String name, Double rate) { this(Some(name), Spec(rate)); } Activity(String name) { this(Some(name), Unspec(defaultweight)); } Activity(Double rate) { this(None, Spec(rate)); } Activity(Action a) { this(a.isSilent() ? None : Some(a.typeName())); } Activity(Action a, Double rate) { this(a.isSilent() ? None : Some(a.typeName()), rate); } Activity(Action a, Rate rate) { this(a.isSilent() ? None : Some(a.typeName()), rate); } public Rate rate() { return this.dist.rate(); } public Object dist() { // dist represents a dist object. distribution represents a negexp, // normal, etc return this.dist; } public boolean equals(Activity a) { if (super.equals((Action)a)) { return (this.dist().equals(a.dist())); } else { return false; } } // return (super.equals(a) && // (this.rate.hasValue() ? // (a.rate.value() == this.rate.value()) : // (! a.rate.hasValue()))); // } public boolean rateUnspec() { switch (this.rate()) { case Unspec(_): return true; case _: return false; } } public String rateToString() { switch (this.rate()) { case Unspec(Double w): return (w + "*" + rateUnspecStr); case Spec(Double r): return r.toString(); } } public Activity cooperatingWith(Activity a, PepaProcess p1, PepaProcess p2) { Rate rE = p1.apparentRate(this); Rate rF = p2.apparentRate(a); Rate r1 = this.rate().div(rE); Rate r2 = a.rate().div(rF); Rate rf = r1.times(r2.times(rE.min(rF))); return new Activity(this, rf); } public String toString() { String act = super.toString(); String rate = rateToString(); return "(" + act + "," + rate + ")"; } } // Processes. I wanted to make sure that all the following conditions // held true: // Process extends AnyProcess // AnyProcess is abstract // AnyProcess implements the Process Interface. public abstract class Process implements ProcessInt { protected static Hashtable definitions = new Hashtable(); public static Process def(String pn) { return definitions.get(pn); } // can't have static abstract! //public abstract static Process def(Process p); protected static void addDef(String pn, Process p) { definitions.put(pn, p); } public abstract boolean equals(Process p); public int hashCode() { return super.hashCode(); } public void prettyPrintln() { this.prettyPrint(); System.out.println(""); } public void prettyPrint() { System.out.print(this); } public abstract Set> derivatives(); public Set derivativeSet() { Set> s = this.derivatives(); Set ps = new Set(); Enumeration> e = s.elements(); while (e.hasMoreElements()) { ps.put((e.nextElement()).snd); } return ps; } public CSet actionSet() { Set> s = this.derivatives(); CSet as = new CSet(); Enumeration> e = s.elements(); while (e.hasMoreElements()) { as.put(e.nextElement().fst); } return as; } public Set derivativesBy(Action a) { CSet as = new CSet(); as.put(a); return this.derivativesBy(as); } public Set derivativesBy(CSet as) { Set> s = this.derivatives(); Set ps = new Set(); Enumeration> e = s.elements(); while (e.hasMoreElements()) { Pair d = e.nextElement(); // Note we test equality of references - therefore, actions // should be immutable (else a != a) if (as.contains(d.fst)) { ps.put(d.snd); } } return ps; } } public class PepaProcess extends Process { public case Var(String pn); public case Prefix(Activity a, PepaProcess p); public case Sum(PepaProcess p1, PepaProcess p2); public case Coop(PepaProcess p1, CSet l, PepaProcess p2); public case Hide(PepaProcess p, CSet l); // A bit of a hack having this here really - but it's useful to // have a simulation object accessible for each pepaprocess - so // when a process is built on top of others, we can recursively // hunt down through the structure, to deactivate competing // process's simulation objects. I should extend PepaProcess, but // I can't be bothered! //public PepaProcessSim sim; public int simstate = UNSCHEDULED; // Left unspecified at first - we store this so that unspecified // rates with weightings can be taken account of. public Activity simact; // if the rate is passive - we'll store the Rate value, because we // may have a weight factor too. But this flag may be useful. public static int UNSCHEDULED = 0; // if the rate is passive - we'll store the Rate value, because we // may have a weight factor too. But this flag may be useful. public static int PASSIVE = 1; // Means that this process has completed its simulation time, but // must wait for a partner (cooperator) to finish too public static int WAITSYNC = 2; // Means its running down its clock public static int RUNNING = 3; // Means its waiting for a derivative to be made public static int MUSTFIRE = 4; // Hopefully I can revise these later - but this means the clock // has just run out. public static int MAYFIRE = 5; // Return a PepaProcess, the result of firing what I can for this public CSet finishedActivities() { switch (this) { case Var(String n1): { return ((PepaProcess)def(this)).finishedActivities(); } case Prefix(Activity a, PepaProcess q1): { CSet ta = new CSet(); if (this.eligibleFire(a)) { ta.put((Action)a); } return ta; } case Sum(PepaProcess q1, PepaProcess q2): { CSet q1fa = q1.finishedActivities(); CSet q2fa = q2.finishedActivities(); Enumeration e = q1fa.elements(); while (e.hasMoreElements()) { Action a = e.nextElement(); q2fa.put(a); } return q2fa; } case Hide(PepaProcess q1, CSet m): { CSet q1fa = q1.finishedActivities(); CSet ta = new CSet(); ta.clear(); Enumeration e = q1fa.elements(); while (e.hasMoreElements()) { Activity a = (Activity)e.nextElement(); Activity ac = new Activity(None, a.rate()); ta.put(ac); } return ta; } case Coop(PepaProcess q1, CSet m, PepaProcess q2): { CSet q1fa = q1.finishedActivities(); CSet q2fa = q2.finishedActivities(); CSet ta = new CSet(); ta.clear(); Enumeration e1 = q1fa.elements(); while (e1.hasMoreElements()) { Activity a = (Activity)e1.nextElement(); if (! m.contains((Action)a)) { ta.put(a); } } Enumeration e2 = q2fa.elements(); while (e2.hasMoreElements()) { Activity a = (Activity)e2.nextElement(); if (! m.contains((Action)a)) { ta.put(a); } } Enumeration e3 = q1fa.elements(); while (e3.hasMoreElements()) { Activity a = (Activity)e3.nextElement(); if (m.contains((Action)a) && q2fa.contains((Action)a)) { ta.put(a); } } return ta; } } } public CSet allEnabled() { switch (this) { case Var(String n1): { return ((PepaProcess)def(this)).allEnabled(); } case Prefix(Activity a, PepaProcess q1): { CSet ta = new CSet(); ta.put((Action)a); return ta; } case Sum(PepaProcess q1, PepaProcess q2): { CSet q1fa = q1.allEnabled(); CSet q2fa = q2.allEnabled(); Enumeration e = q1fa.elements(); while (e.hasMoreElements()) { Action a = e.nextElement(); q2fa.put(a); } return q2fa; } case Hide(PepaProcess q1, CSet m): { return q1.allEnabled(); } case Coop(PepaProcess q1, CSet m, PepaProcess q2): { CSet q1fa = q1.allEnabled(); CSet q2fa = q2.allEnabled(); Enumeration e = q1fa.elements(); while (e.hasMoreElements()) { Action a = e.nextElement(); q2fa.put(a); } return q2fa; } } } // Return true if some activity within the structure of this // process has a terminated clock and is able to fire (not waiting // to sync, say) public boolean canFire(Action ac) { switch (this) { case Var(String n1): { return ((PepaProcess)def(this)).canFire(ac); } case Prefix(Activity a, PepaProcess q1): { // if the prefix's clock has run out if ((simstate == MAYFIRE) && ((Action)a).equals(ac)) { return true; } else { return false; } } case Sum(PepaProcess q1, PepaProcess q2): { return (q1.canFire(ac) || q2.canFire(ac)); } case Hide(PepaProcess q1, CSet m): { return ((! m.contains(ac)) && q1.canFire(ac)); } case Coop(PepaProcess q1, CSet m, PepaProcess q2): { if (m.isEmpty()) { return (q1.canFire(ac) || q2.canFire(ac)); } else { // m is not empty CSet q1f = q1.finishedActivities(); CSet q2f = q2.finishedActivities(); // check sync activities first Enumeration q1ae = q1f.elements(); while (q1ae.hasMoreElements()) { Action a = q1ae.nextElement(); if (((Action)ac).equals(a) && q2f.contains(a) && m.contains(a)) { // we can fire this activity return true; } } // check free activities next Enumeration q1ae2 = q1f.elements(); while (q1ae2.hasMoreElements()) { Action a = q1ae2.nextElement(); if (((Action)ac).equals(a) && (! m.contains(a))) { // we can fire this activity return true; } } Enumeration q2ae2 = q2f.elements(); while (q2ae2.hasMoreElements()) { Action a = q2ae2.nextElement(); if (((Action)ac).equals(a) && (! m.contains(a))) { // we can fire this activity return true; } } return false; } } } } // Return true if some activity within the structure of this // process has a terminated clock and is able to fire (not waiting // to sync, say) public boolean canFire() { switch (this) { case Var(String n1): { return ((PepaProcess)def(this)).canFire(); } case Prefix(Activity a, PepaProcess q1): { // if the prefix's clock has run out if (simstate == MAYFIRE) { return true; } else { return false; } } case Sum(PepaProcess q1, PepaProcess q2): { return (q1.canFire() || q2.canFire()); } case Hide(PepaProcess q1, CSet m): { return (q1.canFire()); } case Coop(PepaProcess q1, CSet m, PepaProcess q2): { if (m.isEmpty()) { return (q1.canFire() || q2.canFire()); } else { // m is not empty CSet q1f = q1.finishedActivities(); CSet q2f = q2.finishedActivities(); // check sync activities first Enumeration q1ae = q1f.elements(); while (q1ae.hasMoreElements()) { Action a = q1ae.nextElement(); if (q2f.contains(a) && m.contains(a)) { // we can fire this activity return true; } } // check free activities next Enumeration q1ae2 = q1f.elements(); while (q1ae2.hasMoreElements()) { Action a = q1ae2.nextElement(); if (! m.contains(a)) { // we can fire this activity return true; } } Enumeration q2ae2 = q2f.elements(); while (q2ae2.hasMoreElements()) { Action a = q2ae2.nextElement(); if (! m.contains(a)) { // we can fire this activity return true; } } return false; } } } } // Return true if some activity within the structure of this // process doesn't have a terminated clock (includes passive) public boolean eligibleFire(Action ac) { switch (this) { case Var(String n1): { return ((PepaProcess)def(this)).eligibleFire(ac); } case Prefix(Activity a, PepaProcess q1): { // if the prefix's clock has run out, and the activities // are equal AS ACTIONS, then it's fine if ((simstate != RUNNING) && ((Action)a).equals(ac)) { return true; } else { return false; } } case Sum(PepaProcess q1, PepaProcess q2): { return (q1.eligibleFire(ac) || q2.eligibleFire(ac)); } case Hide(PepaProcess q1, CSet m): { return ((! m.contains(ac)) && q1.eligibleFire(ac)); } case Coop(PepaProcess q1, CSet m, PepaProcess q2): { if (m.isEmpty()) { return (q1.eligibleFire(ac) || q2.eligibleFire(ac)); } else { // m is not empty CSet q1f = q1.finishedActivities(); CSet q2f = q2.finishedActivities(); // check sync activities first Enumeration q1ae = q1f.elements(); while (q1ae.hasMoreElements()) { Action a = q1ae.nextElement(); if (((Action)ac).equals(a) && q2f.contains(a) && m.contains(a)) { // we can fire this activity return true; } } // check free activities next Enumeration q1ae2 = q1f.elements(); while (q1ae2.hasMoreElements()) { Action a = q1ae2.nextElement(); if (((Action)ac).equals(a) && (! m.contains(a))) { // we can fire this activity return true; } } Enumeration q2ae2 = q2f.elements(); while (q2ae2.hasMoreElements()) { Action a = q2ae2.nextElement(); if (((Action)ac).equals(a) && (! m.contains(a))) { // we can fire this activity return true; } } return false; } } } } // Return true if some activity within the structure of this // process does not have a terminated clock public boolean eligibleFire() { switch (this) { case Var(String n1): { return ((PepaProcess)def(this)).eligibleFire(); } case Prefix(Activity a, PepaProcess q1): { // if the prefix's clock has run out if (simstate != RUNNING) { return true; } else { return false; } } case Sum(PepaProcess q1, PepaProcess q2): { return (q1.eligibleFire() || q2.eligibleFire()); } case Hide(PepaProcess q1, CSet m): { return (q1.eligibleFire()); } case Coop(PepaProcess q1, CSet m, PepaProcess q2): { if (m.isEmpty()) { return (q1.eligibleFire() || q2.eligibleFire()); } else { // m is not empty CSet q1f = q1.finishedActivities(); CSet q2f = q2.finishedActivities(); // check sync activities first Enumeration q1ae = q1f.elements(); while (q1ae.hasMoreElements()) { Action a = q1ae.nextElement(); if (q2f.contains(a) && m.contains(a)) { // we can fire this activity return true; } } // check free activities next Enumeration q1ae2 = q1f.elements(); while (q1ae2.hasMoreElements()) { Action a = q1ae2.nextElement(); if (! m.contains(a)) { // we can fire this activity return true; } } Enumeration q2ae2 = q2f.elements(); while (q2ae2.hasMoreElements()) { Action a = q2ae2.nextElement(); if (! m.contains(a)) { // we can fire this activity return true; } } return false; } } } } // This function walks through a PepaProcess and cancels any simulation // objects that it finds within the structure public void cancelSims(CSet enabled) { switch (this) { case Var(String n1): { ((PepaProcess)def(this)).cancelSims(enabled); break; } case Prefix(Activity a, PepaProcess q1): { // is this sufficient to get all active PepaProcessSims? boolean gotit = false; Enumeration e = enabled.elements(); while (e.hasMoreElements()) { Activity ea = (Activity)e.nextElement(); // deliberate ==! We want the same object if (a == ea) { gotit = true; } } if ((! gotit) && (a.as.status == ActivitySim.RUNNING)) { Sim_event ev = null; //int num = a.as.sim_cancel(Sim_system.SIM_ANY, ev); Debug.println(5, " **loser activity, cancelling " +((Activity)a).toString()); a.as.status = UNSCHEDULED; int id = a.as.get_id(); // interrupt the sim_hold a.as.sim_schedule(id, 0.0, -1); } break; } case Sum(PepaProcess q1, PepaProcess q2): { q1.cancelSims(enabled); q2.cancelSims(enabled); break; } case Hide(PepaProcess q1, CSet m): { q1.cancelSims(enabled); break; } case Coop(PepaProcess q1, CSet m, PepaProcess q2): { q1.cancelSims(enabled); q2.cancelSims(enabled); break; } } } // This function takes a pepaprocess, and computes the next // derivative. Note that there may be more than one - consider the // following processes // (a,2) ( (a,3T) + (a,2T) ) // the passive activities won't have clocks set, and so when the (a,2) // finishes, there'll always be two to choose from // (a,2) ( (a,8) + (a,9) ) // the rules for PEPA dictate that the synchronisation must go ahead // at either 2.(8/17) or 2.(9/17) depending on a probabilistic choice // over the rates of the second process. This is time-independent due // to the nature of the exponentials. What should we do in the general // case? Well, we should decide that we always wait for the slowest, // but how do we calculate this at a mathematical level? With PEPA, // the slowest is always the one with the lower rate, even though in // practise, this might not always finish first. These two ideas are // not equivalent because of the exponential, because by the rules, // min(2,3) should be 2. However, surely the 3 could contribute to // being slower some of the time. So I conclude it's a rule imposed // from above. It'd be nice to give the whole thing a race // interpretation, cause then I'd code the whole general thing as a // race too. But what happens in this case? // (b,3).(a,2) ( (a,3) + (a,4) ) // it's a coop, so do I set both sides off straight away? Then when // the b's finished, the RHS will have completed some of its race. Do // I then discard this used lifetimes by assuming the (a,2) will // enforce the lifetime of the cooperation? This one isn't a problem, // cause we can assume the RHS doesn't start to do anything until the // left hand a becomes enabled. So we can guarantee, and we should, // that when a cooperation is active, all activities start at the same // point. // Not true. Consider this process // (a,4) ( (b,1).(a,2) <> (b,2).(a,3) ) // each b will finish at different times. Say (b,1) finishes // first. Then (a,2) is enabled (2 is less than 4). Then (b,2) might // finish. Now we should enable (a,4), since 4 < 2+3 - and then choose // probabilistically. Is this right? Part of (a,2)'s clock will have // run down. Perhaps I should keep (a,2)'s clock value, make a draw // for (a,3), and when the (a,4) finishes, make a prediction based on // the clock draws...? // Further question - can this result in oscillation? I suppose the // dominant factor in any cooperation could change from side to side // as more actions looking like the b above finish on either // side. What should be the rule? So when a new one is enabled, we // should count up the apparent rates on both sides of the // cooperation. Perhaps this should be current // 29/01/99 What if we just use a race interpretation, and that // cooperating means the slowest decides? // the passive activities won't have clocks set, and so when the (a,2) // finishes, there'll always be two to choose from public Set>, Process>> simulationDerivatives() { Set>, Process>> derivs = new Set(); switch(this) { case Prefix(Activity a, PepaProcess p): Set>, Process>> d = new Set(); Set all = new Set(); all.put((Action)a); d.put(new Pair(new Pair((Action)a, all), (Process)p)); derivs = d; break; case Sum(PepaProcess p1, PepaProcess p2): Set>, Process>> d1 = p1.simulationDerivatives(); Enumeration>, Process>> e2 = p2.simulationDerivatives().elements(); while (e2.hasMoreElements()) { d1.put(e2.nextElement()); } derivs = d1; break; case Coop(PepaProcess p1, CSet l, PepaProcess p2): Set>, Process>> d = new Set(); Enumeration>, Process>> e1 = p1.simulationDerivatives().elements(); while (e1.hasMoreElements()) { Pair>, Process> ap = e1.nextElement(); if (! l.contains(ap.fst.fst)) { d.put(new Pair(ap.fst, (Process)(Coop((PepaProcess)ap.snd, l, p2)))); } } Enumeration>, Process>> e2 = p2.simulationDerivatives().elements(); while (e2.hasMoreElements()) { Pair>, Process> ap = e2.nextElement(); if (! l.contains(ap.fst.fst)) { d.put(new Pair(ap.fst, (Process)(Coop(p1, l, (PepaProcess)ap.snd)))); } } // Work out derivatives due to cooperation e1 = p1.simulationDerivatives().elements(); while (e1.hasMoreElements()) { Pair>, Process> ap1 = e1.nextElement(); if (l.contains(ap1.fst.fst)) { e2 = p2.simulationDerivatives().elements(); while (e2.hasMoreElements()) { Pair>, Process> ap2 = e2.nextElement(); if (ap1.fst.fst.equals(ap2.fst.fst)) { if ((((Activity)ap1.fst.fst).rateUnspec()) && (((Activity)ap2.fst.fst).rateUnspec())) { throw (new Error(" SIMERROR : tried a cooperation between two passives ("+((Activity)ap1.fst.fst).toString()+","+((Activity)ap2.fst.fst).toString()+")")); } Activity afast, aslow; Debug.println(5, " examining "+((Activity)ap1.fst.fst).toString()); Rate rap1 = p1.apparentRate(ap1.fst.fst); Debug.println(5, " examining "+((Activity)ap2.fst.fst).toString()); Rate rap2 = p2.apparentRate(ap2.fst.fst); if (rap2.greaterThan(rap1)) { afast = ((Activity)ap2.fst.fst); aslow = ((Activity)ap1.fst.fst); } else { afast = ((Activity)ap1.fst.fst); aslow = ((Activity)ap2.fst.fst); } Set origs = new Set(); Enumeration o1 = ap1.fst.snd.elements(); while (o1.hasMoreElements()) { origs.put(o1.nextElement()); } Enumeration o2 = ap2.fst.snd.elements(); while (o2.hasMoreElements()) { Action o2a = o2.nextElement(); if (! origs.contains(o2a)) { origs.put(o2a); } } // if (aslow.as.status == ActivitySim.RUNDOWN) { Activity jointact = ((Activity)ap1.fst.fst).cooperatingWith((Activity)ap2.fst.fst,p1,p2); jointact.as = aslow.as; d.put(new Pair(new Pair((Action)jointact,origs), (Process)(Coop((PepaProcess)ap1.snd, l, (PepaProcess)ap2.snd)))); // } } } } } derivs = d; break; case Hide(PepaProcess p, CSet l): Set>, Process>> d = new Set(); Enumeration>, Process>> e = p.simulationDerivatives().elements(); while (e.hasMoreElements()) { Pair>, Process> pr = e.nextElement(); Pair> x = pr.fst; Activity a = (Activity)x.fst; Set orig = x.snd; Process np = pr.snd; if (! l.contains(new Action(a.typeName()))) { d.put(new Pair(new Pair((Action)a, orig), (Process)Hide((PepaProcess)np, l))); } else { // Make a new activity with the name hidden Activity ac = new Activity(None, a.rate()); // copy the clock ac.as = a.as; d.put(new Pair(new Pair((Action)ac, orig), (Process)Hide((PepaProcess)np, l))); } } derivs = d; break; case Var(String pn): derivs = ((PepaProcess)PepaProcess.def(pn)).simulationDerivatives(); break; } return derivs; } // Recurse through the process structure, initialising any PPS // objects that are null at the moment. For the initial process, // this'll set off every PPS object // rule is that an unscheduled means the clock isn't started // a running means the clock is still running // a passive means there's no clock, but it can fire // a mayfire means the clock's run out and it may fire public void scheduleSims() { switch (this) { case Var(String n1): { ((PepaProcess)def(this)).scheduleSims(); break; } case Prefix(Activity a, PepaProcess q1): { // Only schedule a new simulation object if there isn't // one running. Debug.println(5, a.toString()+" is an activity with status "+((Integer)a.as.status).toString()); if (a.as.status == a.as.UNSCHEDULED) { switch (a.rate()) { case Unspec(Double w): break; case Spec(Double r): //a.as.status = a.as.PRIMED; // it might actually be able to start; but we will // call another function after this one, to check // through the structure of the process, with a // cooperation set, to set to PRIMED those ones // that can actually start //a.as.status = a.as.CANTSTART; a.as.status = a.as.PRIMED; break; } } break; } case Sum(PepaProcess q1, PepaProcess q2): { //Debug.println(5, " {sched for sum "+this.toString()+"}"); q1.scheduleSims(); q2.scheduleSims(); break; } case Coop(PepaProcess q1, CSet m, PepaProcess q2): { //Debug.println(5, " {sched for coop "+this.toString()+"}"); q1.scheduleSims(); q2.scheduleSims(); break; } case Hide(PepaProcess q1, CSet m): { //Debug.println(5, " {sched for hide "+this.toString()+"}"); q1.scheduleSims(); break; } } } // public void primeSims(CSet coop) { // switch (this) { // case Var(String n1): { // ((PepaProcess)def(this)).scheduleSims(); // break; // } // case Prefix(Activity a, PepaProcess q1): { // // Only schedule a new simulation object if there isn't // // one running. // Debug.println(5, a.toString()+" is an activity with status "+((Integer)a.as.status).toString()); // if (a.as.status == a.as.UNSCHEDULED) { // switch (a.rate()) { // case Unspec(Double w): // break; // case Spec(Double r): // //a.as.status = a.as.PRIMED; // // it might actually be able to start; but we will // // call another function after this one, to check // // through the structure of the process, with a // // cooperation set, to set to PRIMED those ones // // that can actually start // a.as.status = a.as.CANTSTART; // break; // } // } // break; // } // case Sum(PepaProcess q1, PepaProcess q2): { // //Debug.println(5, " {sched for sum "+this.toString()+"}"); // q1.scheduleSims(); // q2.scheduleSims(); // break; // } // case Coop(PepaProcess q1, CSet m, PepaProcess q2): { // //Debug.println(5, " {sched for coop "+this.toString()+"}"); // q1.scheduleSims(); // q2.scheduleSims(); // break; // } // case Hide(PepaProcess q1, CSet m): { // //Debug.println(5, " {sched for hide "+this.toString()+"}"); // q1.scheduleSims(); // break; // } // } // } // public boolean couldEnable(Action a) { // switch (this) { // case Var(String n1): { // return ((PepaProcess)def(this)).couldEnable(a); // } // case Prefix(Activity a2, PepaProcess q1): { // return ((Action)a2.equals(a)); // } // case Sum(PepaProcess q1, PepaProcess q2): { // return (q1.couldEnable(a) || q2.couldEnable(a)); // } // case Coop(PepaProcess q1, CSet m, PepaProcess q2): { // if (m.contains(a)) { // q1.scheduleSims(); // q2.scheduleSims(); // break; // } // case Hide(PepaProcess q1, CSet m): { // //Debug.println(5, " {sched for hide "+this.toString()+"}"); // q1.scheduleSims(); // break; // } // } // } // this isn't actually a process as far as we're concerned // public case Def(String pn, PepaProcess p); public static Process def(Process p) { switch ((PepaProcess)p) { case Var(String pn): return PepaProcess.def(pn); case _: throw (new Error("Argument was not a process variable!")); } } // A very strong equality - we want named identity! // Why do I need to provide this equals function? Presumably because // of the tight interface matching done by the Pizza compiler on // interfaces and subtypes public boolean equals(Process p) { return this.equals((PepaProcess)p); } public boolean equals(PepaProcess p) { switch (this) { case Var(String n1): switch (p) { case Var(String n2): return n1.equals(n2); case _: return false; } case Prefix(Activity a, PepaProcess q1): switch (p) { case Prefix(Activity b, PepaProcess q2): return (a.equals(b) && q1.equals(q2)); case _: return false; } case Sum(PepaProcess q1, PepaProcess q2): switch (p) { case Sum(PepaProcess p1, PepaProcess p2): return (q1.equals(p1) && q2.equals(p2)); case _: return false; } case Coop(PepaProcess q1, CSet m, PepaProcess q2): switch (p) { case Coop(PepaProcess p1, CSet l, PepaProcess p2): return (q1.equals(p1) && q2.equals(p2) && l.equals(m)); case _: return false; } case Hide(PepaProcess q1, CSet m): switch (p) { case Hide(PepaProcess p1, CSet l): return (q1.equals(p1) && l.equals(m)); case _: return false; } } } public String toString(boolean guarded) { switch(this) { case Prefix(Activity a, PepaProcess p): return Prefix(a, p).toString(); case Var(String pn): return this.toString(); case _: if (guarded) { return "(" + this + ")"; } else { return this.toString(); } } } public String toString() { switch(this) { case Prefix(Activity a, PepaProcess p): // we've printed out an activity, so sums should be bracketed return a + "." + p.toString(true); case Sum(PepaProcess p1, PepaProcess p2): return p1 + " + " + p2; case Coop(PepaProcess p1, CSet l, PepaProcess p2): String sep = (l.isEmpty()) ? " || " : (" <" + l.toString() + "> "); return p1 + sep + p2; case Hide(PepaProcess p, CSet l): return p + "/" + l; case Var(String pn): return pn; } } // Return a set of pairs representing the one step derivatives and // the actions required to get there public Set> derivatives() { Set> derivs = new Set(); switch(this) { case Prefix(Activity a, PepaProcess p): Set> d = new Set(); d.put(new Pair((Action)a, (Process)p)); derivs = d; break; case Sum(PepaProcess p1, PepaProcess p2): Set> d1 = p1.derivatives(); Enumeration> e2 = p2.derivatives().elements(); while (e2.hasMoreElements()) { d1.put(e2.nextElement()); } derivs = d1; break; case Coop(PepaProcess p1, CSet l, PepaProcess p2): Set> d = new Set(); // Enumeration le = l.elements(); // herehere - do cooperation properly! Enumeration> e1 = p1.derivatives().elements(); while (e1.hasMoreElements()) { Pair ap = e1.nextElement(); if (! l.contains(ap.fst)) { d.put(new Pair(ap.fst, (Process)(Coop((PepaProcess)ap.snd, l, p2)))); Debug.println(5, "adding 1"); } } Enumeration> e2 = p2.derivatives().elements(); while (e2.hasMoreElements()) { Pair ap = e2.nextElement(); if (! l.contains(ap.fst)) { d.put(new Pair(ap.fst, (Process)(Coop(p1, l, (PepaProcess)ap.snd)))); Debug.println(5, "adding 2"); } } // Work out derivatives due to cooperation e1 = p1.derivatives().elements(); Debug.println(5, "here1"); while (e1.hasMoreElements()) { Pair ap1 = e1.nextElement(); Debug.println(5, "here2"); if (l.contains(ap1.fst)) { e2 = p2.derivatives().elements(); Debug.println(5, "here3"); while (e2.hasMoreElements()) { Pair ap2 = e2.nextElement(); Debug.println(5, "ap1.fst is " + ap1.fst); Debug.println(5, "ap2.fst is " + ap2.fst); Debug.println(5, "equality is " + (ap1.fst.equals(ap2.fst))); if (ap1.fst.equals(ap2.fst)) { Activity jointact = ((Activity)ap1.fst).cooperatingWith((Activity)ap2.fst,p1,p2); // (PepaProcess)ap1.snd, // (PepaProcess)ap2.snd); d.put(new Pair((Action)jointact, (Process)(Coop((PepaProcess)ap1.snd, l, (PepaProcess)ap2.snd)))); Debug.println(5, "adding 3"); } } } } derivs = d; break; case Hide(PepaProcess p, CSet l): Set> d = new Set(); Enumeration> e = p.derivatives().elements(); while (e.hasMoreElements()) { Pair pr = e.nextElement(); Activity a = (Activity)pr.fst; Process np = pr.snd; if (! l.contains(new Action(a.typeName()))) { d.put(new Pair((Action)a, (Process)Hide((PepaProcess)np, l))); } else { // Make a new activity with the name hidden Activity ac = new Activity(None, a.rate()); d.put(new Pair((Action)ac, (Process)Hide((PepaProcess)np, l))); } } derivs = d; break; case Var(String pn): derivs = PepaProcess.def(pn).derivatives(); break; } return derivs; } // Returns a set of sets of competing derivatives, and first, the // activities leading there public Set>> competingDerivatives() { Set>> comps = new Set(); switch(this) { case Prefix(Activity a, PepaProcess p): Set> d = new Set(); d.put(new Pair((Action)a, (Process)p)); comps.put(d); break; case Sum(PepaProcess p1, PepaProcess p2): Set>> res = new Set(); Set>> p1c = p1.competingDerivatives(); Enumeration>> ep1c = p1c.elements(); while (ep1c.hasMoreElements()) { Set> ires = new Set(); Set> p1ci = ep1c.nextElement(); Enumeration> ep1ci = p1ci.elements(); while (ep1ci.hasMoreElements()) { ires.put(ep1ci.nextElement()); } Enumeration>> ep2c = p2.competingDerivatives().elements(); while (ep2c.hasMoreElements()) { Enumeration> ep2ci = ep2c.nextElement().elements(); while (ep2ci.hasMoreElements()) { ires.put(ep2ci.nextElement()); } } res.put(ires); } comps = res; break; case Hide(PepaProcess p, CSet l): Set>> c = new Set(); Enumeration>> es = p.competingDerivatives().elements(); while (es.hasMoreElements()) { Set> d = new Set(); Set> s = es.nextElement(); Enumeration> e = s.elements(); while (e.hasMoreElements()) { Pair pr = e.nextElement(); Activity a = (Activity)pr.fst; Process np = pr.snd; if (! l.contains(new Action(a.typeName()))) { d.put(new Pair((Action)a, (Process)Hide((PepaProcess)np, l))); } else { // Make a new activity with the name hidden Activity ac = new Activity(None, a.rate()); d.put(new Pair((Action)ac, (Process)Hide((PepaProcess)np, l))); } } c.put(d); } comps = c; break; case Var(String pn): comps = ((PepaProcess)PepaProcess.def(pn)).competingDerivatives(); break; case Coop(PepaProcess p1, CSet l, PepaProcess p2): Set>> c = new Set(); // Enumeration le = l.elements(); // herehere - do cooperation properly! Enumeration>> es1 = p1.competingDerivatives().elements(); while (es1.hasMoreElements()) { Set> d = new Set(); Enumeration> e1 = es1.nextElement().elements(); while (e1.hasMoreElements()) { Pair ap = e1.nextElement(); if (! l.contains(ap.fst)) { d.put(new Pair(ap.fst, (Process)(Coop((PepaProcess)ap.snd, l, p2)))); Debug.println(5, "adding 1"); } else { // we need to hunt through p2's derivatives for partners Enumeration>> es2 = p2.competingDerivatives().elements(); while (es2.hasMoreElements()) { Enumeration> e2 = es2.nextElement().elements(); while (e2.hasMoreElements()) { Pair aq = e2.nextElement(); if (ap.fst.equals(aq.fst)) { Activity jointact = ((Activity)ap.fst).cooperatingWith((Activity)aq.fst,p1,p2); d.put(new Pair((Action)jointact, (Process) (Coop((PepaProcess)ap.snd, l, (PepaProcess)aq.snd)))); Debug.println(5, "adding comp coop"); } } } } } c.put(d); } Enumeration>> et2 = p2.competingDerivatives().elements(); while (et2.hasMoreElements()) { Set> d = new Set(); Enumeration> e2 = et2.nextElement().elements(); while (e2.hasMoreElements()) { Pair aq = e2.nextElement(); if (! l.contains(aq.fst)) { d.put(new Pair(aq.fst, (Process)(Coop(p1, l, (PepaProcess)aq.snd)))); Debug.println(5, "adding 1"); } else { // we need to hunt through p2's derivatives for partners Enumeration>> et1 = p1.competingDerivatives().elements(); while (et1.hasMoreElements()) { Enumeration> e1 = et1.nextElement().elements(); while (e1.hasMoreElements()) { Pair ap = e1.nextElement(); if (aq.fst.equals(ap.fst)) { Activity jointact = ((Activity)ap.fst).cooperatingWith((Activity)aq.fst,p1,p2); d.put(new Pair((Action)jointact, (Process) (Coop((PepaProcess)ap.snd, l, (PepaProcess)aq.snd)))); Debug.println(5, "adding comp coop"); } } } } } c.put(d); } comps = c; break; } return comps; } public Rate apparentRate(Action a) { switch(this) { case Prefix(Activity b, PepaProcess p): if (a.equals(b)) { //Debug.println(5, "returning " + b.rate()); return b.rate(); } else { //Debug.println(5, "returning 0"); return Rate.Spec((Double)0.0); } case Sum(PepaProcess p1, PepaProcess p2): Rate r1 = p1.apparentRate(a); Rate r2 = p2.apparentRate(a); switch (r1) { // should ouytput a warning, saying what r equals case Spec(Double sr1): if ((double)sr1 < 0.000001) { switch (r2) { case Unspec(Double ur2): r1 = new Unspec(sr1); break; case Spec(Double ur2): break; } } break; case Unspec(Double ur1): break; } switch (r2) { case Spec(Double sr2): if ((double)sr2 < 0.000001) { switch (r1) { case Unspec(Double ur1): r2 = new Unspec(sr2); break; case Spec(Double sr1): break; } } break; case Unspec(Double ur2): break; } return r1.plus(r2); //return p1.apparentRate(a).plus(p2.apparentRate(a)); case Hide(PepaProcess p, CSet l): if (l.contains(a)) { return p.apparentRate(a); } else { return Rate.Spec((Double)0.0); } case Coop(PepaProcess p1, CSet l, PepaProcess p2): Rate r1 = p1.apparentRate(a); Rate r2 = p2.apparentRate(a); switch (r1) { // should ouytput a warning, saying what r equals case Spec(Double sr1): if ((double)sr1 < 0.000001) { switch (r2) { case Unspec(Double ur2): r1 = new Unspec(sr1); break; case Spec(Double ur2): break; } } break; case Unspec(Double ur1): break; } switch (r2) { case Spec(Double sr2): if ((double)sr2 < 0.000001) { switch (r1) { case Unspec(Double ur1): r2 = new Unspec(sr2); break; case Spec(Double sr1): break; } } break; case Unspec(Double ur2): break; } // if (l.contains(a)) { // return p1.apparentRate(a).min(p2.apparentRate(a)); // } else { // return p1.apparentRate(a).plus(p2.apparentRate(a)); // } if (l.contains(a)) { return r1.min(r2); } else { return r1.plus(r2); } case Var(String pn): return ((PepaProcess)PepaProcess.def(this)).apparentRate(a); } } } // Discrete event simulation objects // The idea here is that an activity can be extended to be the thing // that is the clock - this is conceptually cleaner. public class ActivitySim extends Sim_entity { // if the rate is passive - we'll store the Rate value, because we // may have a weight factor too. But this flag may be useful. public static int UNSCHEDULED = 0; // Means its running down its clock public static int RUNNING = 1; // Means its running down its clock public static int RUNDOWN = 2; // Means the clock hasn't started yet, but will when body() is called // set by scheduleSims() public static int PRIMED = 3; // Means this activity has been chosen to generate a derivative public static int CHOSEN = 4; // Means this activity is waiting for another to complete public static int WAITING = 5; // Means this activity must wait for others to cooperate with public static int CANTSTART = 6; // Means its not meant to represent a clock - maybe just a // temporary activity // public static int TEMP = 10; public int status; // pointer back to activity holding this sim private Activity a; private static Set allacts = new Set(); public ActivitySim(Activity a) { super(a.toString()); status = UNSCHEDULED; this.a = a; allacts.put(this); //Sim_port go = new Sim_port(a.toString()); //add_port(go); } // send an event to each activity to wake up // only send an event if the activitysim isn't running public void wakeup() { Enumeration e = allacts.elements(); while (e.hasMoreElements()) { ActivitySim asim = e.nextElement(); if ((asim.status != RUNNING) && (asim.status != RUNDOWN)) { int id = asim.get_id(); // wake up call //Debug.println(5, "sending wakeup to "+asim.a.toString()); sim_schedule(id, 0.0, 0); } } } // two functions for simulationDerivative() public static boolean isRunning(Activity a) { return (a.as.status == a.as.RUNNING); } public static boolean isRundown(Activity a) { return (a.as.status == a.as.RUNDOWN); } public static boolean isAvailable(Activity a) { if (a.as.status != a.as.UNSCHEDULED) { return true; } switch (a.rate()) { case Unspec(Double w): return true; case Spec(Double r): return false; } } public void deSchedule(Set acts) { Enumeration e = acts.elements(); while (e.hasMoreElements()) { Activity a = (Activity)e.nextElement(); a.as.status = UNSCHEDULED; } } public Pair> chooseDerivative(PepaProcess me, Set>,Process>> ds, ActivitySim clock) { // Ensure there are no passives { Enumeration>, Process>> e = ds.elements(); while (e.hasMoreElements()) { Pair>, Process> pr = e.nextElement(); Activity a = (Activity)pr.fst.fst; Process p = pr.snd; if (a.rateUnspec()) { throw (new Error(" SIMERROR : derivative chosen by passive activity "+a.toString()+","+p.toString())); } } } // Ensure all activities are of the same type // what about when a choice is offered? Duh // { // Action common = null; // Enumeration> e = ds.elements(); // while (e.hasMoreElements()) { // Pair pr = e.nextElement(); // Activity a = (Activity)pr.fst; // Process p = pr.snd; // if (common == null) { // common = (Action)a; // } // if (common != (Action)a) { // throw (new Error(" SIMERROR : two different action types enabled at the same time "+a.toString()+","+common.toString()+","+p.toString())); // } // } // } // at this point, we'll have a set of activity x derivatives, // but note that some of them will represent choices whose // clocks haven't run down yet. However, all cooperative // activities maintain the clocks of the slower activity. So // if we get an activity at the top level, it is guaranteed to // point to the required clock. If that clock is rundown, we // can take the activity Set>,Process>> fds = new Set(); { Enumeration>, Process>> e = ds.elements(); while (e.hasMoreElements()) { Pair>, Process> pr = e.nextElement(); Activity a = (Activity)pr.fst.fst; // if it's just rundown now, or if it's been waiting // around for a while. Note it can't be passive, cause // that would imply the partner was passive too (since // you can't have a faster active rate), and two // passives can't give rise to a transition if ((a.as.status == ActivitySim.RUNDOWN) || (a.as.status == ActivitySim.WAITING)) { fds.put(pr); } } } // Sum the rates for a probabilistic choice Rate total = Spec((Double)0.0); { Enumeration>, Process>> e = fds.elements(); while (e.hasMoreElements()) { Pair>, Process> pr = e.nextElement(); Activity a = (Activity)pr.fst.fst; total = total.plus(a.rate()); } } Double t = total.specValue(); Sim_uniform_obj un = new Sim_uniform_obj("probchoice", 0.0, (double)t, Dist.uniq); // hack gcla later Dist.reseed(); //Dist d = new Dist(un, Dist.UNIFORM, total); double samp = un.sample(); Debug.println(5, " sampled a "+((Double)samp).toString()+" up to "+((Double)t).toString()); // walk along set PepaProcess ans = null; Set ansacts = new Set(); Rate running = Spec((Double)0.0); { Enumeration>, Process>> e = fds.elements(); boolean done = false; while ((e.hasMoreElements()) && (! done)) { Pair>, Process> pr = e.nextElement(); Activity a = (Activity)pr.fst.fst; Process p = pr.snd; Debug.println(5, " activity is "+a.toString()); running = running.plus(a.rate()); if ((double)running.specValue() > samp) { done = true; Debug.println(5, " picked that one"); ans = (PepaProcess)p; ansacts = pr.fst.snd; } } } // remember, an activity could fire, yet have to wait for its // partner in the cooperation - this might not be an error // if (ans == null) { // throw (new Error(" SIMERROR : Couldn't choose derivative!")); // } if (ans == null) { clock.status = WAITING; return new Pair((Process)me, null); } else { return new Pair((Process)ans, ansacts); } } public void body() { while (true) { //Debug.println(5, "I'm checking my status "+a.toString()); if (status == PRIMED) { //Debug.println(5, "I woke up, am about to hold"+a.toString()); double h = ((Dist)a.dist()).sample(); status = RUNNING; Sim_event dummy = new Sim_event(); sim_hold_for(h, dummy); if (status == RUNNING) { // I won // When I get here, the clock has just fired. Debug.println(10, " event fired - I am activity "+a.toString() +" after "+((Double)h).toString()); status = RUNDOWN; // Calculate the next state of the process CSet empty = new CSet(); Set>,Process>> ds = Peparoni.me.simulationDerivatives(); // apply some sanity checks here PepaProcess oldme = Peparoni.me; // including this is a hack, but it lets me set // the status to WAITING if no derivative is // chosen Pair> res = chooseDerivative(Peparoni.me, ds, this); Peparoni.me = (PepaProcess)res.fst; Debug.println(10, " the simderiv will be " +Peparoni.me.toString()); // set all to unscheduled if (res.snd != null) { deSchedule(res.snd); } // Gather together all activities in the new process which // are enabled CSet allacts = Peparoni.me.allEnabled(); // Now any enabled in here not in allacts have to // be cancelled oldme.cancelSims(allacts); if (status == RUNDOWN) { status = UNSCHEDULED; } Peparoni.me.scheduleSims(); // they should all be set to cantstart right // now. Let's now go through the process // structure, and set to PRIMED those that aren't // bound by a cooperation set //CSet emptyas = new CSet(); //Peparoni.me.primeSims(emptyas); sim_hold(0.0); wakeup(); } } //Debug.println(5, "I'm waiting to be woken "+a.toString()); sim_wait(null); //Debug.println(5, "I've been woken "+a.toString()); } } } // Class for producing debugging information, and so on. public class Debug { private static boolean debug = false; // convention is that 1 is lowest, others are higher private static int level = 10; // note this doesn't turn debugging on (or off)! public static void level(int lvl) { level = lvl; } public static void debugOn() { debug = true; } public static void debugOff() { debug = false; } public static void println(String s) { if (debug) { System.out.println(s); } } public static void println(int l, String s) { if (l >= level) { println(s); } } } public class Peparoni { public static String progname = "peparoni"; public static String version = "0.1"; // for storing variable name translations protected static Hashtable vardefs = new Hashtable(); //protected static Hashtable, Action> allactions = // the process to be simulated public static PepaProcess me; // set to true while the model is being parsed public static boolean buildingModel; public static Double def(String vn) { return vardefs.get(vn); } public static void addDef(String vn, Double v) { vardefs.put(vn, v); } public static void main(String[] args) { boolean cont = true; Debug.debugOff(); // Use GNU getopt for argument passing int c; String pepafilename = null; String arg; StringBuffer optval = new StringBuffer(); LongOpt[] longopts = new LongOpt[2]; longopts[0] = new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'); longopts[1] = new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'd'); Getopt g = new Getopt(progname, args, "-hd::", longopts); g.setOpterr(true); while (cont && ((c = g.getopt()) != -1)) { switch (c) { case 1: pepafilename = g.getOptarg(); break; case 'd': arg = g.getOptarg(); if ((arg == null) || (! arg.equals("off"))) { Debug.debugOn(); } if (arg != null) { if (arg.equals("low")) { Debug.level(10); } // include lots of debugging information if (arg.equals("high")) { Debug.level(5); } // all debug info if (arg.equals("all")) { Debug.level(0); } } break; case 'h': usage(); cont = false; break; default: System.out.println("getopt() returned " + c); break; } } if (cont && (pepafilename != null)) { try { PepaLexer.preInit(new FileReader(pepafilename)); } catch (java.io.FileNotFoundException e) { throw (new Error("File " + pepafilename + " not found!")); } PepaParser parser = new PepaParser(); try { Sim_system.initialise(); buildingModel = true; Symbol result = parser.parse(); PepaProcess pr = (PepaProcess)result.value; buildingModel = false; //Debug.println(5, "Process was " + PepaProcess.def(pr)); Debug.println(5, "Process was " + pr.toString()); Debug.println(5, "init"); // Debug.println(5, "making new pepaprocesssim"); Debug.println(5, "process is "+pr.toString()); // Recurse through the process structure, and set going any // simulation objects that aren't started. me means // that the PPS objects that are created will see this // process as the one being simulated (there are no nodes in // the process tree above this one). It's static. me = pr; me.scheduleSims(); Sim_system.run(); Debug.println(5, "running"); } catch (Exception e) { System.out.println("exception in first parse..."); } } } public static void computeSteadyState(PepaProcess p) { System.out.println("Beginning steady state analysis..."); //Graph> lmg = //p.labelledMultiGraph(); } public static void usage() { String u; u = "Usage: "+progname+" [OPTION]... FILE\n"; u += "Simulate PEPA model in FILE.\n"; u += "\n"; u += " -h, --help\t\t\tdisplay this help and exit\n"; u += " -d, --debug[=on|off]\t\tset debugging information on/off\n"; u += "\n"; u += "Please report bugs to \n"; System.out.println(u); } public static void testProcesses() { //StringReader s = new StringReader("Source = (incell, lambda).Source;"); //StringReader s = new StringReader("# Source = (incell, 14).Source;"); //Yylex lexer = new Yylex(s); Activity a1 = new Activity("a"); Activity a2 = new Activity("b", 1.2); Activity a3 = new Activity(2.7); Action a5 = new Action("ac"); Action a6 = new Action(None); Action a7 = new Action(); Action a8 = new Action("a"); Action a9 = new Action("b"); System.out.println(a1.toString()); System.out.println(a2.toString()); System.out.println(a3.toString()); System.out.println(a5.toString()); System.out.println(a6.toString()); System.out.println(a7.toString()); CSet as = new CSet(); as.put(a8); as.put(a9); PepaProcess p1 = Var("P"); PepaProcess p2 = Sum(Var("P"), Var("Q")); PepaProcess p3 = Prefix(a2, p2); PepaProcess p4 = Hide(p3, as); System.out.println(p1.toString()); System.out.println(p2.toString()); System.out.println(p3.toString()); System.out.println(p4.toString()); //PepaLexer.preInit(new StringReader("# Source = (incell, 14).Source; Source")); PepaProcess pr = null; PepaParser parser = null; PepaLexer.preInit(new StringReader("# P = (a, 5.7).P; P")); parser = new PepaParser(); try { Symbol result = parser.parse(); pr = (PepaProcess)result.value; System.out.println("Process was " + PepaProcess.def(pr)); } catch (Exception e) { System.out.println("exception in first parse..."); } PepaLexer.preInit(new StringReader("# P = Q + R; P")); parser = new PepaParser(); try { Symbol result = parser.parse(); pr = (PepaProcess)result.value; System.out.println("Process was " + PepaProcess.def(pr)); } catch (Exception e) { System.out.println("exception in first parse..."); } PepaLexer.preInit(new StringReader("# P = (a,5).Q + (b,3.2).R; P")); parser = new PepaParser(); try { Symbol result = parser.parse(); pr = (PepaProcess)result.value; System.out.println("Process was " + PepaProcess.def(pr)); } catch (Exception e) { System.out.println("exception in first parse..."); } PepaLexer.preInit(new StringReader("# P = Q R; P")); parser = new PepaParser(); try { Symbol result = parser.parse(); pr = (PepaProcess)result.value; System.out.println("Process was " + PepaProcess.def(pr)); } catch (Exception e) { System.out.println("exception in first parse..."); } //PepaLexer.preInit(new StringReader("# P = ((a,3).Q (a,4).R)/ {a,y,z}; P")); //PepaLexer.preInit(new StringReader("# P = (a,3).Q (a,4).R; P")); } } // Awful that I need this, but I can't coerce something of type Object // up to something of type Pair! I can't use template // information in a cast, and if I do (Pair) on its own, it complains // that Pair is not a subtype of Pair public class ProcessDefn { public String name; public Process proc; public ProcessDefn(String name, Process proc) { this.name = name; this.proc = proc; } } public class VarDefn { public String name; public Double v; public VarDefn(String name, Double v) { this.name = name; this.v = v; } } // This is required in PepaParser.cup, since I cannot coerce an Object // to a Pizza type CSet - see the Pizza home page for why it's not // allowed public class ActionSet extends CSet { } public class PepaLexer { /* single lookahead character */ // protected static int next_char; /* advance input by one character */ // protected static void advance() // throws java.io.IOException // { next_char = System.in.read(); } protected static Yylex lexer; protected static Reader processinput; /* initialize the scanner */ public static void preInit(Reader r) { PepaLexer.processinput = r; } public static void init() throws java.io.IOException { PepaLexer.lexer = new Yylex(PepaLexer.processinput); } /* recognize and return the next complete token */ public static Symbol next_token() throws java.io.IOException { // ignore the first id - I don't need it (I hope) Debug.println(5, "[fetching a token "); Yytoken tok = PepaLexer.lexer.yylex(); Debug.println(5, tok.m_text + "]"); if (tok.m_text.equals("eof")) { return new Symbol(sym.EOF); } else { return new Symbol(tok.m_token.toInt(), tok); } } }