From c0041f893e05373c02a76352d4537366faa32206 Mon Sep 17 00:00:00 2001 From: Chris Myers Date: Fri, 18 Aug 2017 12:14:59 -0600 Subject: [PATCH] Cleaned up bin directory --- bin/LEMA | 12 - bin/LEMA.bat | 19 - bin/LEMA.linux | 11 - bin/LEMA.linux64 | 11 - bin/LEMA.mac | 12 - bin/LEMA.mac64 | 12 - bin/MSVCRT.DLL | Bin 323072 -> 0 bytes bin/andrew | 12 - bin/apply_post_filter.pl | 115 - bin/autogenT.py | 955 ------ bin/cText.py | 69 - bin/cText.pyc | Bin 1917 -> 0 bytes bin/checkHpn.pl | 173 -- bin/check_dot.pl | 152 - bin/cir2data.py | 292 -- bin/data2lhpn.py | 2700 ----------------- bin/detect-java.sh | 78 - bin/gcm2sbml.pl | 1190 -------- bin/genBackgroundGCM.pl | 41 - bin/gen_GeneNet_report.pl | 339 --- bin/iBioSim | 12 - bin/iBioSim.linux | 11 - bin/iBioSim.linux64 | 5 - bin/iBioSim.mac | 12 - bin/iBioSim.mac64 | 5 - bin/parg | Bin 60364 -> 0 bytes bin/prereqs.perl | 98 - bin/printnet | 3 - bin/remoteATACS.sh | 19 - bin/remoteTangATACS.sh | 19 - .../edu/utah/ece/async/ibiosim/gui/Gui.java | 10 +- 31 files changed, 5 insertions(+), 6382 deletions(-) delete mode 100755 bin/LEMA delete mode 100644 bin/LEMA.bat delete mode 100755 bin/LEMA.linux delete mode 100755 bin/LEMA.linux64 delete mode 100755 bin/LEMA.mac delete mode 100755 bin/LEMA.mac64 delete mode 100644 bin/MSVCRT.DLL delete mode 100755 bin/andrew delete mode 100755 bin/apply_post_filter.pl delete mode 100644 bin/autogenT.py delete mode 100755 bin/cText.py delete mode 100644 bin/cText.pyc delete mode 100755 bin/checkHpn.pl delete mode 100755 bin/check_dot.pl delete mode 100644 bin/cir2data.py delete mode 100644 bin/data2lhpn.py delete mode 100755 bin/detect-java.sh delete mode 100755 bin/gcm2sbml.pl delete mode 100755 bin/genBackgroundGCM.pl delete mode 100755 bin/gen_GeneNet_report.pl delete mode 100755 bin/iBioSim delete mode 100755 bin/iBioSim.linux delete mode 100755 bin/iBioSim.mac delete mode 100755 bin/parg delete mode 100755 bin/prereqs.perl delete mode 100755 bin/printnet delete mode 100755 bin/remoteATACS.sh delete mode 100755 bin/remoteTangATACS.sh diff --git a/bin/LEMA b/bin/LEMA deleted file mode 100755 index e3eb3b762..000000000 --- a/bin/LEMA +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# -# run-time wrapper for BioSim gui -export DYLD_LIBRARY_PATH=$LEMA/lib64:$DYLD_LIBRARY_PATH -CLASSPATH=$LEMA/gui/dist/classes - -for jarFile in $LEMA/gui/lib/*.jar -do - CLASSPATH=$CLASSPATH:$jarFile -done - -exec java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath $CLASSPATH -Dapple.laf.useScreenMenuBar=true -Xdock:name="LEMA" -Xdock:icon=$LEMA/gui/icons/LEMA.png main.Gui -lema diff --git a/bin/LEMA.bat b/bin/LEMA.bat deleted file mode 100644 index 4b1d1e4d0..000000000 --- a/bin/LEMA.bat +++ /dev/null @@ -1,19 +0,0 @@ -@rem *************************************************************************** -@rem -@rem This file is part of iBioSim. Please visit -@rem for the latest version of iBioSim. -@rem -@rem Copyright (C) 2017 University of Utah -@rem -@rem This library is free software; you can redistribute it and/or modify it -@rem under the terms of the Apache License. A copy of the license agreement is provided -@rem in the file named "LICENSE.txt" included with this software distribution -@rem and also available online at . -@rem -@rem *************************************************************************** -setlocal ENABLEDELAYEDEXPANSION -set CLASSPATH="%LEMA%\gui\dist\classes" -for /f %%a IN ('dir /b "%LEMA%\gui\lib\*.jar"') do call set CLASSPATH=!CLASSPATH!;"%LEMA%\gui\lib\%%a" -java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath %CLASSPATH% main.Gui -lema - - diff --git a/bin/LEMA.linux b/bin/LEMA.linux deleted file mode 100755 index 7b7b42284..000000000 --- a/bin/LEMA.linux +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -export LD_LIBRARY_PATH=$LEMA/lib:$LD_LIBRARY_PATH -CLASSPATH=$LEMA/gui/dist/classes - -for jarFile in $LEMA/gui/lib/*.jar -do - CLASSPATH=$CLASSPATH:$jarFile -done - -exec java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath $CLASSPATH main.Gui -lema diff --git a/bin/LEMA.linux64 b/bin/LEMA.linux64 deleted file mode 100755 index cc2ff6534..000000000 --- a/bin/LEMA.linux64 +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -export LD_LIBRARY_PATH=$LEMA/lib64:$LD_LIBRARY_PATH -CLASSPATH=$LEMA/gui/dist/classes - -for jarFile in $LEMA/gui/lib/*.jar -do - CLASSPATH=$CLASSPATH:$jarFile -done - -exec java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath $CLASSPATH main.Gui -lema diff --git a/bin/LEMA.mac b/bin/LEMA.mac deleted file mode 100755 index 20e522352..000000000 --- a/bin/LEMA.mac +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# -# run-time wrapper for BioSim gui -export DYLD_LIBRARY_PATH=$LEMA/lib:$DYLD_LIBRARY_PATH -CLASSPATH=$LEMA/gui/dist/classes - -for jarFile in $LEMA/gui/lib/*.jar -do - CLASSPATH=$CLASSPATH:$jarFile -done - -exec java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath $CLASSPATH -Dapple.laf.useScreenMenuBar=true -Xdock:name="LEMA" -Xdock:icon=$LEMA/gui/icons/LEMA.png main.Gui -lema diff --git a/bin/LEMA.mac64 b/bin/LEMA.mac64 deleted file mode 100755 index e3eb3b762..000000000 --- a/bin/LEMA.mac64 +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# -# run-time wrapper for BioSim gui -export DYLD_LIBRARY_PATH=$LEMA/lib64:$DYLD_LIBRARY_PATH -CLASSPATH=$LEMA/gui/dist/classes - -for jarFile in $LEMA/gui/lib/*.jar -do - CLASSPATH=$CLASSPATH:$jarFile -done - -exec java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath $CLASSPATH -Dapple.laf.useScreenMenuBar=true -Xdock:name="LEMA" -Xdock:icon=$LEMA/gui/icons/LEMA.png main.Gui -lema diff --git a/bin/MSVCRT.DLL b/bin/MSVCRT.DLL deleted file mode 100644 index 7da344bc1b9168e540496b03d890558fb5bde4a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 323072 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

{ly&_G{xtJ&^wW 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%0D9#24KyS@PV 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%0A_h!C?U}I>0Ai^Ryl) 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 -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* - -if ($#ARGV != 3){ - print "Usage: ./recheck_results.pl cutoff file_method_dot file_correct.dot file_output\n"; - exit(1); -} - -my $cutoff = $ARGV[0]; -my $outfile = $ARGV[3]; - -check_correctness($ARGV[1], $ARGV[2]); - - -sub check_correctness{ - my $filename = shift; - my $dot_file = shift; - - open (IN1, "$dot_file") or die "I cannot check dot correctness for $dot_file\n"; - open (IN2, "$filename") or die "I cannot check correctness for $filename\/method.dot\n"; - open (OUT, ">$outfile") or die "I cannot write the checked file\n"; - - my @in1 = ; - my @in2 = ; - close IN1; - close IN2; - - my $in1 = join ("",@in1); - my $in2 = join ("",@in2); - - $in1 =~ s/sp_//g; - $in2 =~ s/sp_//g; - - print OUT "digraph G {\n"; - #generate the states in the corrected output - for (my $i = 0; $i <= $#in2; $i++){ - if ($in2[$i] =~ m/shape=ellipse/){ - print OUT $in2[$i]; - } - } - - my $r_c = 0; - my $r_t = 0; - #check precision - while ($in1 =~ m/s([0-9]+) -> s([0-9]+) .+arrowhead=((vee|tee))/g){ - $r_t++; - my $state1 = $1; - my $state2 = $2; - my $arc = $3; - #print "I matched $state1 $arc $state2\n"; - if ($in2 =~ m/s$state1 -> s$state2 .*arrowhead=$arc/){ - $r_c++; - } - } - print "\tRecall: $r_c/$r_t = '" . $r_c/$r_t . "'\n"; - - my $p_c = 0; - my $p_t = 0; - #check precision - while ($in2 =~ m/s([0-9]+) -> s([0-9]+) (.+), *arrowhead=((vee|tee))/g){ - $p_t++; - my $state1 = $1; - my $state2 = $2; - my $mid = $3; - my $arc = $4; - - my $remove_arc = 0; - #print "I matched $state1 $arc $state2\n"; - if ($mid =~ m/label=\"[-]*([0-9]+[.]*[0-9]*)/){ - my $num = $1; - $num = (int (10000 * $num)) / 10000; -# $mid =~ m/(label=\"[-]*)[0-9]+[.]*[0-9]*/$1$num/; - if ($num < $cutoff){ - $mid =~ s/color=\"[^\"]+/color=\"green/; - $remove_arc = 1; - } - } - if (not $remove_arc){ -#WE DO NOT NEED TO CHECK THIS AT THIS STAGE -# if ($in1 =~ m/s$state1 -> s$state2 .*arrowhead=$arc/){ -# $p_c++; - print OUT "s$state1 -> s$state2 $mid,arrowhead=$arc]\n"; -# } -# else{ -# print OUT "s$state1 -> s$state2 $mid,arrowhead=$arc,style=dashed]\n"; -# } - } - } - if ($p_t > 0){ - print "\tPrecision: $p_c/$p_t = '" . $p_c/$p_t . "'\n"; - } - else{ - print "\tPrecision: $p_c/$p_t = '0'\n"; - } - - print OUT "\n}\n"; - - close OUT; - - return ($r_c,$r_t,$p_c,$p_t); - -} - diff --git a/bin/autogenT.py b/bin/autogenT.py deleted file mode 100644 index 15c1574ea..000000000 --- a/bin/autogenT.py +++ /dev/null @@ -1,955 +0,0 @@ -#!/usr/bin/python - -############################################################################## -## Copyright (c) 2007 by Scott R. Little -## University of Utah -## -## Permission to use, copy, modify and/or distribute, but not sell, this -## software and its documentation for any purpose is hereby granted -## without fee, subject to the following terms and conditions: -## -## 1. The above copyright notice and this permission notice must -## appear in all copies of the software and related documentation. -## -## 2. The name of University of Utah may not be used in advertising or -## publicity pertaining to distribution of the software without the -## specific, prior written permission of University of Utah. -## -## 3. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, -## EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY -## WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. -## -## IN NO EVENT SHALL UNIVERSITY OF UTAH OR THE AUTHORS OF THIS SOFTWARE BE -## LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES -## OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA -## OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON -## ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE -## OR PERFORMANCE OF THIS SOFTWARE. -## -############################################################################## - -### TODO ### -#Provide a method to specify different numbers of thresholds for different variables? -#Use properties to aid in the threshold generation? -#Update genBins to use the partial results list -#Add a brief comment regarding how to add optimization and cost functions - -import re, os.path, cText, copy, sys -from optparse import OptionParser - -#Regular expressions -lQuoteR = re.compile("\"+") -tQuoteR = re.compile("\"+") -numVarsR = re.compile("Variables: ") -numPointsR = re.compile("Points: ") -spaceR = re.compile("\s+") -lSpaceR = re.compile("^\s+") -tSpaceR = re.compile("\s+$") -lineSpaceR = re.compile("^\s+$") -newLR = re.compile("\n+") -lDotR = re.compile("^\.\w") -epsilonR = re.compile(".epsilon") -lengthR = re.compile(".length") -timeR = re.compile(".time") -absoluteTimeR = re.compile(".absoluteTime") -percentR = re.compile(".percent") -inputR = re.compile(".inputs") -outputR = re.compile(".outputs") -dmvcR = re.compile(".dmvc") -rateSamplingR = re.compile(".rateSampling") -pathLengthR = re.compile(".pathLength") -vaRateUpdateIntervalR = re.compile(".vaRateUpdateInterval") -minDelayValR = re.compile(".minDelayVal") -minRateValR = re.compile(".minRateVal") -minDivisionValR = re.compile(".minDivisionVal") -decPercentR = re.compile(".decPercent") -minVarValR = re.compile(".minVarVal") -maxVarValR = re.compile(".maxVarVal") -falseR = re.compile("false",re.I) #pass the I flag to be case insensitive -trueR = re.compile("true",re.I) #pass the I flag to be case insensitive -binCommentR = re.compile("^\#") -lParenR = re.compile("\\(+") -rParenR = re.compile("\\)+") -rowsR = re.compile("\\(.*?\\)") - -############################################################################## -# A class to hold the lists of places and transitions in the graph. -############################################################################## -class Variable: - "A continuous variable in the system being modeled." - def __init__(self,nameStr): - self.name = nameStr #the name of the variable - self.dmvc = None #Boolean denoting the status of the variable as a discrete multi-valued continuous (DMVC) variable - self.input = None #Boolean denoting that the variable is a model input - self.output = None #Boolean denoting that the variable is a model output - self.type = None #Describes the type of the variable using an enumerated type (VOLTAGE, CURRENT) which is needed by Verilog-A. - def __str__(self): - retStr = self.name+"[" - if self.dmvc: - retStr = retStr + "1]" - else: - retStr = retStr + "0]" - return retStr -## End Class Variable ######################################################## - -############################################################################## -# A class to hold the parameters specified in the threshold (.bins) file. -############################################################################## -class ThresholdParameters: - "The parameters possibly specified in the thresholds (.bins) file." - def __init__(self,numVars): - #Default values - self.epsilon = 0.1 #What is the +/- epsilon where signals are considered to be equivalent - self.length = 15 #the number of time points that a value must persist to be considered constant - self.time = 5e-6 #the amount of time that must pass to be considered constant when using absoluteTime - self.absoluteTime = False #when False time points are used to determine DMVC and when true absolutime time is used to determine DMVC - self.percent = 0.8 #a decimal value representing the percent of the total trace that must be constant to qualify to become a DMVC var - self.numValuesL = [] #the number of constant values for each variable...-1 indicates that the variable isn't considered a DMVC variable - self.vaRateUpdateInterval = 1e-6 #how often the rate is added to the continuous variable in the Verilog-A model output - for i in range(numVars): - self.numValuesL.append(-1) - def __str__(self): - retStr = "epsilon:"+str(self.epsilon)+" length:"+str(self.length)+" numValuesL:"+str(self.numValuesL) - return retStr - ############################################################################ - # Determine if two values are equal within the given epsilon. - ############################################################################ - def epsilonEquiv(self,v1,v2): - if abs(v1-v2) <= self.epsilon: - return True - else: - return False -## End Class ThresholdParameters ############################################# - -############################################################################## -# A class for data required for the discovery of discrete multi-valued -# continuous variables. -############################################################################## -class DMVCpart: - "Information about a single run of a constant value." - numDMVCparts = 0 - def __init__(self): - self.id = DMVCpart.numDMVCparts #unique numeric ID for each run - DMVCpart.numDMVCparts = DMVCpart.numDMVCparts + 1 - self.varInd = -1 #an index into the varsL array denoting which variable owns the run - self.valueL = [] #the list of values for this run - self.startPoint = -1 #an index into datL for the start point of the run - self.endPoint = -1 #an index into datL for the end point of the run - self.nextRun = None #a reference to the next sequential run - def __str__(self): - retStr = "Part:"+str(self.id)+" Start:"+str(self.startPoint)+" End:"+str(self.endPoint)+" Val:"+str(self.constVal()) - if self.nextRun: - retStr += " Next:"+str(self.nextRun.id) - else: - retStr += " Next:None" - return retStr - ############################################################################ - # Calculate the constant value from the value list. Currently it is an - # average of all list values. - ############################################################################ - def constVal(self): - total = 0 - for i in self.valueL: - total = total + i - return total/float(len(self.valueL)) - ############################################################################ - # Calculate the delay for a given DMVC run. - ############################################################################ - def calcDelay(self,datL): - ind1 = self.startPoint - ind2 = self.endPoint - delay = datL[ind2][0]-datL[ind1][0] - #Assuming that there is some time between runs we want to account for that time. If we assume a constant rate of change we can just split the difference - if self.nextRun: - ind3 = self.nextRun.startPoint - delay += ((datL[ind3][0]-datL[ind2][0])/2) - return delay -## End Class DMVCpart ######################################################## - -############################################################################## -# Remove leading & trailing space as well as trailing new line characters. -############################################################################## -def cleanLine(line): - lineNS = re.sub(lSpaceR,"",line) - lineNL = re.sub(newLR,"",lineNS) - lineTS = re.sub(tSpaceR,"",lineNL) - return lineTS - -############################################################################## -# Remove leading & trailing space as well as trailing new line characters. -############################################################################## -def cleanName(name): - nameNL = re.sub(lQuoteR,"",name) - nameTS = re.sub(tQuoteR,"",nameNL) - return nameTS - -############################################################################## -# Creates a 2 dimensional array of lists rows x cols with each value -# initialized to initVal. -############################################################################## -def create2Darray(rows,cols,initVal): - newL = [] - for i in range(rows): - initL = [] - for j in range(cols): - initL.append(initVal) - newL.append(initL) - return newL - -############################################################################## -# Create the list of variables. All data files must have the same variables -# in the same order. -############################################################################## -def extractVars(datFile): - varsL = [] - line = "" - inputF = open(datFile, 'r') - rowsL = inputF.read() - rowsM = rowsR.match(rowsL) - row = rowsM.group() - varNames = cleanRow(row) - varNamesL = varNames.split(",") - for varStr in varNamesL: - varStr = cleanName(varStr) - varsL.append(Variable(varStr)) - varsL[0].dmvc = False - inputF.close() - return varsL - -############################################################################## -# Parse a .dat file ensuring that the varsL matches the global list. -############################################################################## -def parseDatFile(datFile,varsL): - inputF = open(datFile, 'r') - linesL = inputF.read() - rowsL = rowsR.findall(linesL) - for i in range(len(rowsL)): - rowsL[i] = cleanRow(rowsL[i]) - numPoints = -1 - varNames = cleanRow(rowsL[0]) - varNamesL = [] - varNamesL = varNames.split(",") - for i in range(len(varNamesL)): - varNamesL[i] = cleanName(varNamesL[i]) - numPoints = len(varNamesL) - if len(varNamesL) == len(varsL): - for i in range(len(varNamesL)): - if varNamesL[i] != varsL[i].name: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Expected "+varsL[i].name+" in position "+str(i)+" but received "+varNamesL[i]+" in file: "+datFile - sys.exit() - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr + " Expected "+str(len(varsL))+" variables but received "+str(len(varNamesL))+" in file: "+datFile - sys.exit() - - datL = [] - for i in range(1,len(rowsL)): - valStrL = cleanRow(rowsL[i]).split(",") - valL = [] - for s in valStrL: - valL.append(float(s)) - datL.append(valL) - inputF.close() - return datL, numPoints - -############################################################################## -# Parse the .bins (thresholds) file. -############################################################################## -def parseBinsFile(binsFile,varsL): - global pathLength - global rateSampling - global minDelayVal - global minRateVal - global minDivisionVal - global decPercent - global minVarValL - global maxVarValL - global limitExists - - minVarValL = [] - maxVarValL = [] - limitExists = False - - for i in range(len(varsL)): - minVarValL.append(None) - maxVarValL.append(None) - - if not os.path.isfile(binsFile): - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" the .bins file, "+binsFile+" was not found." - sys.exit() - inputF = open(binsFile, 'r') - linesL = inputF.readlines() - tParam = ThresholdParameters(len(varsL)) - divisionsStrL = [] - numDivisions = 0 - for i in range(1,len(varsL)): - divisionsStrL.append([]) - for i in range(len(linesL)): - #Allow blank lines and comments - if lineSpaceR.match(linesL[i]) or binCommentR.match(linesL[i]): - continue - if lDotR.match(linesL[i]): - if epsilonR.match(linesL[i]): - epsilonL = spaceR.split(linesL[i]) - tParam.epsilon = abs(float(epsilonL[1])) - elif lengthR.match(linesL[i]): - lengthL = spaceR.split(linesL[i]) - tParam.length = float(lengthL[1]) - elif timeR.match(linesL[i]): - timeL = spaceR.split(linesL[i]) - tParam.time = float(timeL[1]) - elif vaRateUpdateIntervalR.match(linesL[i]): - vaRateUpdateIntervalL = vaRateUpdateIntervalR.split(linesL[i]) - tParam.vaRateUpdateInterval = float(vaRateUpdateIntervalL[1]) - elif absoluteTimeR.match(linesL[i]): - absoluteTimeL = spaceR.split(linesL[i]) - if trueR.match(absoluteTimeL[1]): - tParam.absoluteTime = True - elif falseR.match(absoluteTimeL[1]): - tParam.absoluteTime = False - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Attempted to set .absoluteTime with"+absoluteTimeL[i]+" which is unrecognized. It was not set. Please use True or False." - sys.exit() - elif percentR.match(linesL[i]): - percentL = spaceR.split(linesL[i]) - tParam.percent = float(percentL[1]) - elif inputR.match(linesL[i]): - cLine = cleanLine(linesL[i]) - inputL = spaceR.split(cLine) - for i in range(1,len(inputL)): - found = False - for j in range(1,len(varsL)): - if inputL[i] == varsL[j].name: - #print varsL[j].name+" is an input." - varsL[j].input = True - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+inputL[i]+" was specified as an input in the .bins file, but wasn't found in the variable list." - sys.exit() - elif outputR.match(linesL[i]): - cLine = cleanLine(linesL[i]) - outputL = spaceR.split(cLine) - for i in range(1,len(outputL)): - found = False - for j in range(1,len(varsL)): - if outputL[i] == varsL[j].name: - #print varsL[j].name+" is an output." - varsL[j].output = True - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+outputL[i]+" was specified as an output in the .bins file, but wasn't found in the variable list." - sys.exit() - elif dmvcR.match(linesL[i]): - cLine = cleanLine(linesL[i]) - dmvcL = spaceR.split(cLine) - outputL = spaceR.split(cLine) - for i in range(1,len(dmvcL)): - found = False - for j in range(1,len(varsL)): - if outputL[i] == varsL[j].name: - #print varsL[j].name+" is dmvc." - varsL[j].dmvc = True - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+outputL[i]+" was specified as dmvc in the .bins file, but wasn't found in the variable list." - sys.exit() - elif rateSamplingR.match(linesL[i]): - rateSamplingL = spaceR.split(linesL[i]) - rateSampling = int(rateSamplingL[1]) - elif pathLengthR.match(linesL[i]): - pathLengthL = spaceR.split(linesL[i]) - pathLength = int(pathLengthL[1]) - elif minDelayValR.match(linesL[i]): - minDelayValL = spaceR.split(linesL[i]) - minDelayVal = int(minDelayValL[1]) - elif minRateValR.match(linesL[i]): - minRateValL = spaceR.split(linesL[i]) - minRateVal = int(minDelayValL[1]) - elif minDivisionValR.match(linesL[i]): - minDivisionValL = spaceR.split(linesL[i]) - minDivisionVal = int(minDelayValL[1]) - elif decPercentR.match(linesL[i]): - decPercentL = spaceR.split(linesL[i]) - decPercent = int(decPercentL[1]) - elif minVarValR.match(linesL[i]): - limitExists = True - cLine = cleanLine(linesL[i]) - inputL = spaceR.split(cLine) - found = False - for i in range(1,len(varsL)): - if inputL[2] == varsL[i].name: - minVarValL[i] = inputL[1] - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+inputL[2]+" was specified in a target for .minVarVal in the .bins file, but wasn't found in the variable list." - sys.exit() - elif maxVarValR.match(linesL[i]): - limitExists = True - cLine = cleanLine(linesL[i]) - inputL = spaceR.split(cLine) - found = False - for i in range(1,len(varsL)): - if inputL[2] == varsL[i].name: - maxVarValL[i] = inputL[1] - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+inputL[2]+" was specified in a target for .maxVarVal in the .bins file, but wasn't found in the variable list." - sys.exit() - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Unparseable dot option in the thresholds file: "+linesL[i] - sys.exit() - else: - numDivisions += 1 - cLineL = cleanLine(linesL[i]).split(" ") - found = False - for j in range(1,len(varsL)): - if cLineL[0] == varsL[j].name: - divisionsStrL[j-1] = cLineL[1:] - found = True - break - if not found: - #cStr = cText.cSetFg(cText.RED) - cStr = "ERROR:" - #cStr += cText.cSetAttr(cText.NONE) - print cStr+" Variable not included in the data file." - print "Line: "+linesL[i] - sys.exit() - divisionsL = [[]] - for sL in divisionsStrL: - fL = [] - for s in sL: - if (s.find("?") == -1): - fL.append(float(s)) - else: - fL.append(s) - divisionsL.append(fL) - #print len(fL) - inputF.close() - if numDivisions != len(varsL)-1: - cStr = cText.cSetFg(cText.RED) - cStr += "WARNING:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" There is not a threshold for every variable in the dat file." - #sys.exit() - #print "divisionsL:"+str(divisionsL) - return divisionsL, tParam - -############################################################################## -# Remove leading and trailing parantheses -############################################################################## -def cleanRow(row): - rowNS = re.sub(lParenR,"",row) - rowTS = re.sub(rParenR,"",rowNS) - return rowTS - -############################################################################## -# Reorder the datL so each row is a list of data values for the ith -# variable. Also build a list of the extreme values for each -# variable. -############################################################################## -def reorderDatL(varsL): - datValsL = [] - datValsExtremaL = [] - for i in range(len(varsL)): - datValsL.append([]) - datValsExtremaL.append([]) - - i = 1 - while 0==0: - try: - datFile = "run-" + str(i) + ".tsd" - datL,numPoints = parseDatFile(datFile,varsL) - for j in range(len(varsL)): - for k in range(len(datL)): - datValsL[j].append(datL[k][j]) - for j in range(1,len(varsL)): - datValsExtremaL[j] = (min(datValsL[j]),max(datValsL[j])) - except: - break - i += 1 - return datValsL, datValsExtremaL - -############################################################################## -# Explore a potential DVMC run. If the run is valid (currently this -# means long enough) then return the run. Else return None. -############################################################################## -def exploreRun(datL,i,j,tParam): - run = DMVCpart() - run.startPoint = i - run.varInd = j - run.valueL.append(datL[i][j]) - while i+1 < len(datL) and tParam.epsilonEquiv(datL[run.startPoint][j],datL[i+1][j]): - run.valueL.append(datL[i+1][j]) - i = i+1 - #print "i:"+str(i)+" j:"+str(j) - run.endPoint = i - if not tParam.absoluteTime: - if ((run.endPoint-run.startPoint)+1) < tParam.length: - #print "Run is too short from "+str(run.startPoint)+" to "+str(run.endPoint)+" ["+str((run.endPoint-run.startPoint)+1)+"]" - return None, i - else: - #print "Found a run from "+str(run.startPoint)+" to "+str(run.endPoint)+"["+str((run.endPoint-run.startPoint)+1)+"]" - return run, i - else: - if run.calcDelay(datL) < tParam.time: - #print "Run is too short from "+str(run.startPoint)+" to "+str(run.endPoint)+" ["+str(run.calcDelay(datL))+"]" - return None, i - else: - #print "Found a run from "+str(run.startPoint)+" to "+str(run.endPoint)+" ["+str(run.calcDelay(datL))+"]" - return run, i - -############################################################################## -# Determine which variables should be considered multi-valued -# continuous variables. Marks varsL[i].dmvc for DMVC variables and -# returns a list of valid DMVC runs varsL long. Empty lists exist for -# non-DMVC places and lists of valid runs are present for DMVC -# variables. -############################################################################## -def findDMVC(datL,varsL,tParam): - tempRun = None - prevRun = None - runL = [] - for j in range(len(varsL)): - runL.append([]) - if varsL[j].dmvc != False: - #print "Examining variable["+str(j)+"]: "+varsL[j].name - mark = 0 - for i in range(len(datL)-1): - if i < mark: - continue - if tParam.epsilonEquiv(datL[i][j],datL[i+1][j]): - #print "Exploring from:"+str(i) - tempRun,mark = exploreRun(datL,i,j,tParam) - #print "Returning at:"+str(mark) - if tempRun != None: - if len(runL[j]) > 1: - prevRun.nextRun = tempRun - prevRun = tempRun - runL[j].append(tempRun) - #determine if a high enough percentage of the run is constant - if not tParam.absoluteTime: - numPoints = 0 - for run in runL[j]: - #print "run:"+str(run) - #print "runDelay:"+str(run.calcDelay(datL)) - numPoints += (run.endPoint-run.startPoint) + 1 - if (numPoints/float(len(datL))) < tParam.percent: - #print "Clearing runs for "+varsL[j].name+" ["+str(numPoints/float(len(datL)))+"]"+str(numPoints)+"/"+str(len(datL)) - runL[j] = [] #clear the runs if they don't meet the percentage requirement - else: - #print varsL[j].name+" is a DMVC. ["+str(numPoints/float(len(datL)))+"]" - varsL[j].dmvc = True - else: - absTime = 0.0 - for run in runL[j]: - #print "run:"+str(run) - #print "runDelay:"+str(run.calcDelay(datL)) - absTime += run.calcDelay(datL) - if (absTime/(datL[len(datL)-1][0]-datL[0][0])) < tParam.percent: - #print "Clearing runs for "+varsL[j].name+" ["+str(absTime/(datL[len(datL)-1][0]-datL[0][0]))+"]"+str(absTime)+"/"+str(datL[len(datL)-1][0]-datL[0][0]) - runL[j] = [] - else: - #print varsL[j].name+" is a DMVC. ["+str(absTime/(datL[len(datL)-1][0]-datL[0][0]))+"]" - varsL[j].dmvc = True - #return runL for processing during the graph building - return runL - -############################################################################## -# Create an initial set of divisions based upon the number of bins and -# the extreme values for each variable. These initial bins are -# evenly spaced. -############################################################################## -def initDivisionsL(datValsExtremaL,varsL,divisionsL): - #divisionsL = [] - #for i in range(len(varsL)): - # divisionsL.append([]) - for i in range(1,len(varsL)): - print varsL[i] - #print "i:"+str(i)+" "+str(datValsExtremaL[i]) - interval = float(abs(datValsExtremaL[i][1]-datValsExtremaL[i][0]) / (numThresholds+1)) - #print "interval:"+str(interval) - for j in range(0,len(divisionsL[i])): - if (divisionsL[i][j] == "?"): - divisionsL[i][j] = datValsExtremaL[i][0]+(interval*j) - return divisionsL - -############################################################################## -# Generate the bin encoding for each data point given the divisions. -############################################################################## -def genBins(datL,divisionsL): - #print "datL:"+str(datL) - #print "divisionsL:"+str(divisionsL) - binsL = create2Darray(len(divisionsL),len(datL[0]),-1) - for i in range(1,len(divisionsL)): - for j in range(len(datL[0])): - for k in range(len(divisionsL[i])): - if (datL[i][j] <= divisionsL[i][k]): - binsL[i][j] = k - break - else: - #handles the case when the datum is in the highest bin - #i.e. for 2 boundary numbers 3 bins are required - #print "binsL["+str(i)+"]["+str(j)+"] = "+str(k+1) - binsL[i][j] = k+1 - #print "binsL:"+str(binsL) - return binsL - -############################################################################## -# Determine if two bins are equivalent and return a Boolean. -############################################################################## -def equalBins(a,b,binsL,divisionsL): - for i in range(1,len(divisionsL)): - if binsL[i][a] != binsL[i][b]: - return False - return True - -############################################################################## -# Generate the rates for each data point given the bin encodings. -############################################################################## -def genRates(divisionsL,datL,binsL): - #Function notes: Rates can be calculated based on transitions or places. If rates are calculated based on places they are calculated based on the change in the bin for the entire line. If rates are calculated based on transitions they are calculated based on the change in the bin for each variable. These two methods have different results and it appears that place based rates are more stable. To help "smooth" out the rates there are several ways to modify the rate calcualation. One way is to change the rateSampling variable. This variable determines how long the bin must remain constant before a rate is calculated for that bin. It can be a numerical value or "inf." The "inf" setting only calculates the rate once per bin change. You can invalidate bin changes of short length using the pathLength variable. Any run of consecutive bins shorter than pathLength will not have its rate calculated. The rate is also only calculated if the time values differ for the two points as I have seen examples where this is a problem. - ratesL = create2Darray(len(divisionsL),len(datL[0]),'-') - if placeRates: - #Place based rate calculation - if rateSampling == "inf": - mark = 0 - for i in range(len(datL[0])): - if i < mark: - continue - while mark < len(datL[0]) and equalBins(i,mark,binsL,divisionsL): - mark += 1 - if datL[0][mark-1] != datL[0][i] and (mark-i) >= pathLength: - for j in range(1,len(divisionsL)): - ratesL[j][i] = (datL[j][mark-1]-datL[j][i])/(datL[0][mark-1]-datL[0][i]) - else: - for i in range(len(datL[0])-rateSampling): - calcRate = True - for k in range(rateSampling): - if not equalBins(i,i+k,binsL,divisionsL): - calcRate = False - break - if calcRate and datL[0][i+rateSampling] != datL[0][i]: - for j in range(1,len(divisionsL)): - ratesL[j][i] = (datL[j][i+rateSampling]-datL[j][i])/(datL[0][i+rateSampling]-datL[0][i]) - else: - cStr = cText.cSetFg(cText.YELLOW) - cStr += "WARNING:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+"this feature has not been tested." - #Transition based rate calculation - if rateSampling == "inf": - for j in range(1,len(divisionsL)): - mark = 0 - for i in range(len(datL[0])): - if i < mark: - continue - while mark < len(datL[0]) and equalBins(i,mark,binsL,divisionsL): - mark = mark + 1 - if datL[0][mark-1] != datL[0][i]: - ratesL[j][i] = (datL[j][mark-1]-datL[j][i])/(datL[0][mark-1]-datL[0][i]) - else: - for i in range(len(datL[0])-rateSampling): - for j in range(1,len(divisionsL)): - calcRate = True - for k in range(rateSampling): - if not equalBins(i,i+k,binsL,divisionsL): - calcRate = False - break - if calcRate and datL[0][i+rateSampling] != datL[0][i]: - ratesL[j][i] = (datL[j][i+rateSampling]-datL[j][i])/(datL[0][i+rateSampling]-datL[0][i]) - return ratesL - -############################################################################## -# Return the minimum rate for a given rate list. -############################################################################## -def minRate(ratesL): - #Remove the characters from the list before doing the comparison - cmpL = [] - for i in range(len(ratesL)): - if ratesL[i] != '-': - cmpL.append(ratesL[i]) - if len(cmpL) > 0: - return min(cmpL) - else: - return "-" - -############################################################################## -# Return the maximum rate for a given rate list. -############################################################################## -def maxRate(ratesL): - #Remove the characters from the list before doing the comparison - cmpL = [] - for i in range(len(ratesL)): - if ratesL[i] != '-': - cmpL.append(ratesL[i]) - if len(cmpL) > 0: - return max(cmpL) - else: - return "-" - -############################################################################## -# Give a score for the even distribution of points for all -# variables. 0 is the optimal score. -############################################################################## -def pointDistCost(datValsL,divisionsL,resL=[],updateVar=-1): - total = 0 - if updateVar == 0: - for i in range(len(divisionsL)): - resL.append(0) - #Fill up resL - for i in range(1,len(divisionsL)): - points = pointDistCostVar(datValsL[i],divisionsL[i]) - total += points - resL[i] = points - elif updateVar > 0: - #Incrementally calculate a total change - resL[updateVar] = pointDistCostVar(datValsL[updateVar], - divisionsL[updateVar]) - for i in resL: - total += i - else: - #Do a full calculation from scratch - for i in range(1,len(divisionsL)): - total += pointDistCostVar(datValsL[i],divisionsL[i]) - return total - -############################################################################## -# Give a score for the even distribution of points for an individual -# variable. 0 is the optimal score. -############################################################################## -def pointDistCostVar(datValsL,divisionsL): - optPointsPerBin = len(datValsL)/(len(divisionsL)+1) - #print "optPointsPerBin:"+str(optPointsPerBin) - pointsPerBinL = [] - for i in range(len(divisionsL)+1): - pointsPerBinL.append(0) - for i in range(len(datValsL)): - top = True - for j in range(len(divisionsL)): - if datValsL[i] <= divisionsL[j]: - pointsPerBinL[j] += 1 - top = False - break - if top: - pointsPerBinL[len(divisionsL)] += 1 - - #print "pointsPerBinL:"+str(pointsPerBinL) - score = 0 - for points in pointsPerBinL: - score += abs(points - optPointsPerBin) - return score - -############################################################################## -# Give a score for the range of rates for all variables. 0 is the -# optimal score. -############################################################################## -def rateRangeCost(datValsL,divisionsL,resL=[],updateVar=-1): - total = 0 - binsL = genBins(datValsL,divisionsL) - ratesL = genRates(divisionsL,datValsL,binsL) - #print "ratesL:"+str(ratesL) - for i in range(1,len(divisionsL)): - maxR = maxRate(ratesL[i]) - minR = minRate(ratesL[i]) - total += abs(maxR-minR) - return total - -############################################################################## -# Look for the optimal thresholds using a greedy algorithm. -############################################################################## -def greedyOpt(divisionsL,datValsL,datValsExtremaL,initDivL): - resL = [] #Used to keep partial results for cost functions - updateVar = 0 #The variable that was updated to help optimize cost function recalculation - bestDivisionsL = copy.deepcopy(divisionsL) - bestCost = costFunc(datValsL,divisionsL,resL,updateVar) - numMoves = 0 - print "Starting optimization..." - while numMoves < iterations: - for i in range(1,len(divisionsL)): - for j in range(len(divisionsL[i])): - if (initDivL[i][j] != "?"): - #move left - if (j == 0): - if divisionsL[i][j] != "?": - distance = abs(divisionsL[i][j] - datValsExtremaL[i][0])/2 - else: - distance = abs(divisionsL[i][j] - divisionsL[i][j-1])/2 - else: - distance = abs(divisionsL[i][j] - divisionsL[i][j-1])/2 - newDivisionsL = copy.deepcopy(divisionsL) - newDivisionsL[i][j] -= distance - newCost = costFunc(datValsL,newDivisionsL,resL,i) - numMoves += 1 - if numMoves % 500 == 0: - print str(numMoves)+"/"+str(iterations) - if newCost < bestCost: - bestCost = newCost - divisionsL = newDivisionsL - else: - #move right - if j == len(divisionsL[i])-1: - distance = abs(datValsExtremaL[i][1] - divisionsL[i][j])/2 - else: - distance = abs(divisionsL[i][j+1] - divisionsL[i][j])/2 - newDivisionsL = copy.deepcopy(divisionsL) - newDivisionsL[i][j] += distance - newCost = costFunc(datValsL,newDivisionsL,resL,i) - numMoves += 1 - if numMoves % 500 == 0: - print str(numMoves)+"/"+str(iterations) - if newCost < bestCost: - bestCost = newCost - divisionsL = newDivisionsL - if numMoves > iterations: - return divisionsL - return divisionsL - -############################################################################## -# Look for the optimal thresholds using a greedy algorithm for the -############################################################################## -def writeBinsFile(varsL,divisionsL,binsFile): - outputF = open(binsFile, 'w') - flag = False - for i in range(len(varsL)): - if (varsL[i].dmvc == True): - if (flag == False): - outputF.write(".dmvc ") - flag = True - outputF.write(varsL[i].name + " ") - if (flag == True): - outputF.write("\n") - for i in range(1,len(varsL)): - if len(divisionsL[i]) > 0: - outputF.write(varsL[i].name) - for div in divisionsL[i]: - outputF.write(" "+str(div)) - outputF.write("\n") - outputF.close() - -############################################################################## -############################################################################## -def main(): - global numThresholds - global iterations - global optFunc - global costFunc - - usage = "usage: %prog [options] datFile1 ... datFileN" - parser = OptionParser(usage=usage) - parser.set_defaults(binsFile=None,numThresholds=None,costF="p",optF="g") - parser.add_option("-b", "--bins", action="store", dest="binsFile", help="The name of the .bins file to be created. If this is not provided the basename of the first input data file is used.") - parser.add_option("-t", "--thresholds", action="store", dest="numThresholds", help="The number of thresholds to create during autogeneration.") - parser.add_option("-i", "--iterations", action="store", dest="iterations", help="The number of iterations of the optimization algorithm to run.") - parser.add_option("-c", "--cost", action="store", dest="costF", help="The cost function to use: r - Minimize the distance between rates; p - Average the number of points in each bin.") - parser.add_option("-o", "--optimization", action="store", dest="optF", help="The optimization function to use: g - Greedy algorithm.") - - (options, args) = parser.parse_args() - - #if len(args) > 0: - # datFileL = args - #else: - # print "At least one data file is required." - # parser.print_help() - # sys.exit() - - if not options.binsFile: - baseFileL = os.path.splitext(datFileL[0]) - binsFile = baseFileL[0]+".bins" - else: - binsFile = options.binsFile - - if options.numThresholds: - numThresholds = int(options.numThresholds) - - if options.iterations: - iterations = int(options.iterations) - - if options.optF == "g": - optFunc = greedyOpt - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr + options.optFunc + " is not a valid option for the optimization function." - parser.print_help() - sys.exit() - - if options.costF == "r": - costFunc = rateRangeCost - elif options.costF == "p": - costFunc = pointDistCost - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr + options.costFunc + " is not a valid option for the cost function." - parser.print_help() - sys.exit() - - varsL = extractVars("run-1.tsd") - datValsL, datValsExtremaL = reorderDatL(varsL) - if os.path.isfile(options.binsFile): - initDivL, tParam = parseBinsFile(options.binsFile,varsL) - dmvcRunL = findDMVC(datValsL,varsL,tParam) - divisionsL = initDivisionsL(datValsExtremaL,varsL,initDivL) - print "Iterations: "+str(iterations) - print "Optimization function: "+optFunc.func_name - print "Cost function: "+costFunc.func_name - print "Initial divisionsL:"+str(divisionsL) - print "Initial score:"+str(costFunc(datValsL,divisionsL)) - divisionsL = optFunc(divisionsL,datValsL,datValsExtremaL,initDivL) - print "Final divisionsL:"+str(divisionsL) - print "Final score:"+str(costFunc(datValsL,divisionsL)) - writeBinsFile(varsL,divisionsL,binsFile) - -############################################################################## -############################################################################## - -########### -# Globals # -########### -numThresholds = 2 #the default number of thresholds to create...it can be overridden from the command line -iterations = 10000 #the default number of iterations -rateSampling = "inf" #How many points should exist between the sampling of different rates..."inf" samples once/threshold -pathLength = 10 #For "inf" rate sampling the number of time points that a "run" must persist for the rate to be calculated. This is just another parameter to help with the data smoothing. -placeRates = True #When true the script calculates rates based on places. When false it calculates rates based on transitions although there is very little infrastructure for transition based rates and it isn't well tested. -optFunc = None #The name of the optimization function that will be used. -costFunc = None #The name of the cost function that will be used in the optimization function. - - -if __name__ == "__main__": - main() diff --git a/bin/cText.py b/bin/cText.py deleted file mode 100755 index 07f52cfe4..000000000 --- a/bin/cText.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python - -############################################################################## -## Copyright (c) 2007 by Scott R. Little -## University of Utah -## -## Permission to use, copy, modify and/or distribute, but not sell, this -## software and its documentation for any purpose is hereby granted -## without fee, subject to the following terms and conditions: -## -## 1. The above copyright notice and this permission notice must -## appear in all copies of the software and related documentation. -## -## 2. The name of University of Utah may not be used in advertising or -## publicity pertaining to distribution of the software without the -## specific, prior written permission of University of Utah. -## -## 3. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, -## EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY -## WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. -## -## IN NO EVENT SHALL UNIVERSITY OF UTAH OR THE AUTHORS OF THIS SOFTWARE BE -## LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES -## OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA -## OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON -## ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE -## OR PERFORMANCE OF THIS SOFTWARE. -## -############################################################################## - -#attributes for color selection -NONE = "00" -BOLD = "01" -DIM = "02" -UNDERLINE = "04" -BLINK = "05" -REVERSE = "07" -HIDDEN = "08" - -#available colors for fg and bg -BLACK = 0 -RED = 1 -GREEN = 2 -YELLOW = 3 -BLUE = 4 -MAGENTA = 5 -CYAN = 6 -WHITE = 7 - -def cSetAll(attr, fgColor, bgColor): - return "\033["+attr+";"+str(fgColor+30)+";"+str(bgColor+40)+"m" - -def cSetAttr(attr): - return "\033["+attr+"m" - -def cSetFg(fgColor): - return "\033["+str(fgColor+30)+"m" - -def cSetBg(bgColor): - return "\033["+str(bgColor+40)+"m" - -def cSetFgBg(fgColor,bgColor): - return "\033["+str(fgColor+30)+";"+str(bgColor+40)+"m" - -def cSetAttrFg(attr, fgColor): - return "\033["+attr+";"+str(fgColor+30)+"m" - -def cSetAttrBg(attr, bgColor): - return "\033["+attr+";"+str(bgColor+40)+"m" diff --git a/bin/cText.pyc b/bin/cText.pyc deleted file mode 100644 index 17781e0ec75b0f14a871aa41299cbb9600161a51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1917 zcmdn|iI*$PJ<>Co0SXuy7#JKF7#ND@GB7ZtFfc?hFr+XtL@_d?Ffl|iF{CgvL@_g@ zurNfiFr=_DM6oiYurWljF{H3FM6olZa4uZvZ=VFNCQNdfUHa`DJd!eIV3IJ zIX@>K!cKy)L7ExBM6oUd1A~5OacWVqer_d*EJ`g-O)N^z&`-+D*LTXy56;ZhPs+^G zPYy|~DA6mZ1e=*0oLb_TlVb$(7RYT3jL9HZfG~&)_AU>qcZ0!BgLx&0gMk6!0Nj03-&9`3T8O@`?!G1G8a$Z5>5sNhEP8j*B~EHKi3jaxI6iH`gwy(pdi;U z*Pvk65>OiT@N{u;^@A#KboK@t5aj9t7I6=9b@c-)i*)tz@ehYs;}hx%HpbV{-PJF| z5iIB&=?F0-+`}`(HHepifgy+wMDT+M0T3YwB7{JMFiI%^3PJ|bb67DbUE1X4r(2nYaOIX{vB diff --git a/bin/checkHpn.pl b/bin/checkHpn.pl deleted file mode 100755 index 5256f98db..000000000 --- a/bin/checkHpn.pl +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/perl -#******************************************************************************* -# -# This file is part of iBioSim. Please visit -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* - -############################################################################### -## -## File Name : checkHpn.pl -## Author : -## E-mail : little@cs.utah.edu -## Date Created : 05/12/2004 -## -## Description : A simple script to check results for the ATACS THPN -## analysis engine. -## -## Assumptions : -## -## ToDo : -## -############################################################################### -## CVS HOOKS -## -## $Source: /home/ming/cvsroot/BioSim/bin/Attic/checkHpn.pl,v $ -############################################################################### -## $Id: checkHpn.pl,v 1.1 2010/02/16 18:11:26 myers Exp $ -############################################################################### - -use strict; - -#the following 2 hashes give the current "correct" values for states -#and zones on the examples. -my %stateH = -( - "water" => 2, - "water2x" => 2, - "waterAMS" => 10, - "billiardsAMS" => 66, - "temperatureAMS" => 39, - "diode_noosc" => 14, - "diode_osc" => 17, - "2x2diode" => 4, - "3x3diode" => 6, - "4x4diode" => 16, - "naive_4x4noosc" => 12, - "naive_4x4osc" => 13, - "our_4x4noosc" => 15, - "our_4x4nooscprop" => 41, -# "our_4x4nooscprop2" => 37, - "our_4x4nooscprop4" => 14, - "our_4x4osc" => 14, - "our_4x4oscprop" => 22, -# "our_4x4oscprop2" => 17, - "our_4x4oscprop4" => 17, - "our_noosc" => 9, - "our_osc" => 13, - "pll2" => 27, - "pll3" => 20, - "pll_lite" => 5, - "vco" => 2, - "waterEI" => 11, - "billiardsEI" => 28, - "temperatureEI" => 23 - ); - -my %zoneH = -( - "water" => 11, - "water2x" => 11, - "waterAMS" => 10, - "billiardsAMS" => 134, - "temperatureAMS" => 48, - "diode_noosc" => 3612, - "diode_osc" => 3343, - "2x2diode" => 57, - "3x3diode" => 522, - "4x4diode" => 13334, - "naive_4x4noosc" => 68, - "naive_4x4osc" => 101, - "our_4x4noosc" => 1473, - "our_4x4nooscprop" => 8479, -# "our_4x4nooscprop2" => 3829, - "our_4x4nooscprop4" => 3109, - "our_4x4osc" => 698, - "our_4x4oscprop" => 802, -# "our_4x4oscprop2" => 296, - "our_4x4oscprop4" => 2830, - "our_noosc" => 271, -# "our_osc" => 359, - "pll2" => 1497, - "pll3" => 615, - "pll_lite" => 11, - "vco" => 7, - "waterEI" => 14, - "billiardsEI" => 48, - "temperatureEI" => 37 - ); - -my $file; - -#grab the size of @ARGV -my $arg_size = scalar(@ARGV); - -if($arg_size != 1) { - print "Wrong number of arguments to the script.\n"; - exit(-1); -} -else { - $file = $ARGV[0]; -} - -my $foundWarning = 0; -my $foundZoneCnt = 0; -my $foundStateCnt = 0; -my $status = 0; - -open(LOG, "$file.log"); -print "$file: "; -my $ok = 1; -while() { - chomp; - my $line = $_; - if($line =~ /Warning:/ && !$foundWarning) { - if($line !~ /^Warning: Petri net contains/) { - print "Failed! A warning was given.\n"; - $ok = 0; - $foundWarning = 1; - $status = 1; - } - } - if($line =~ /^States:/) { - $foundStateCnt = 1; - my @stateA = split / /, $line; - if($stateA[1] != $stateH{$file}) { - print "Failed! Wrong state count -- given $stateA[1] expected $stateH{$file}\n"; - $ok = 0; - $status = 2; - } - } - if($line =~ /^Zones: \d+$/) { - $foundZoneCnt = 1; - my @zoneA = split / /, $line; - if($zoneA[1] != $zoneH{$file}) { - print "Failed! Wrong zone count -- given $zoneA[1] expected $zoneH{$file}\n"; - $ok = 0; - $status = 3; - } - } -} -close LOG; -if($ok && $foundZoneCnt && $foundStateCnt) { - print "Ok\n"; -} - -if(!$foundZoneCnt) { - print "No zone count found!\n"; - $status = 4; -} - -if(!$foundStateCnt) { - print "No state count found!\n"; - $status = 5; -} -exit($status); diff --git a/bin/check_dot.pl b/bin/check_dot.pl deleted file mode 100755 index 75b80e4a2..000000000 --- a/bin/check_dot.pl +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/perl -#******************************************************************************* -# -# This file is part of iBioSim. Please visit -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* - -#This file writes the checked dot file. - -if ($#ARGV == 1){ - print "Normal Usage: ./check_dot.pl Unchecked_Dot, Master_Dot, Out_Dot\n"; - print "Assumed usage: ./check_dot.pl Unchecked_Dot, [find the masterdot ] Out_Dot\n"; - $assumed = $ARGV[1]; - $assumed =~ s/(.*)\/(.*)\/(.*)\/([^\/]*).dot/$1\/$2\/$2.dot/; - if (not -e "$assumed"){ - print "ERROR: Unable to find a master dot file $assumed from $ARGV[1]\n"; - exit(1); - } - - $unchecked = $ARGV[0]; - $master_dot = $assumed; - $out = $ARGV[1]; - -} -elsif ($#ARGV != 2){ - print "Usage: ./check_dot.pl Unchecked_Dot, Master_Dot, Out_Dot\n"; - exit(1); -} -else{ - $unchecked = $ARGV[0]; - $master_dot = $ARGV[1]; - $out = $ARGV[2]; -} - - - -if (not -e "$master_dot"){ - print "ERROR: unable to check correctness for non exsistant? $master_dot\n"; - exit(1); -} -if (not -e "$unchecked"){ - print "ERROR: unable to check correctness for non exsistant? '$unchecked'\n"; - exit(1); -} -open (IN1, "$master_dot") or die "I cannot check dot correctness for $master_dot\n"; -open (IN2, "$unchecked") or die "I cannot check correctness for $filename\/method.dot\n"; -open (CHECKED, ">$out") or die "I cannot write the checked file\n"; - -my @in1 = ; -my @in2 = ; -close IN1; -close IN2; - -my $in1 = join ("",@in1); -my $in2 = join ("",@in2); - -$in1 =~ s/sp_//g; -$in2 =~ s/sp_//g; - -print CHECKED "digraph G {\n"; -#generate the states in the corrected output -for (my $i = 0; $i <= $#in2; $i++){ - if ($in2[$i] =~ m/shape=ellipse/){ - print CHECKED $in2[$i]; - } -} - -#draw things acording to the following plan -# \ True -#Reported \ a r n -# \ ___________________________ -# a | blue | red | black | -# | solid | dashed | dashed | -# | normal | tee | onormal | -# |-----------------|---------| -# r | blue | red | black | -# | dashed | solid | dashed | -# | normal | tee | obox | -# |-----------------|---------| -# n | blue | red | | -# | dotted | dotted | | -# | normal | tee | | -# ----------------------------- -# -#Check the first 2 columns above -while ($in1 =~ m/s([0-9]+) -> s([0-9]+) (.+)arrowhead= *((vee|tee))/g){ - my $state1 = $1; - my $state2 = $2; - my $mid = $3; - my $arc = $4; -# if ($mid =~ m/label=\"[-]*([0-9]+[.]*[0-9]*)/){ -# my $num = $1; -# $num = (int (10000 * $num)) / 10000; -# if ($num < $green_level){ -# $mid =~ s/color=\"[^\"]+/color=\"green/; -# } -# } - if ($in2 =~ m/s$state1 -> s$state2 (.*)arrowhead=$arc/){ - $tmp = $1; - $arc =~ s/vee/normal/; - print CHECKED "s$state1 -> s$state2 $tmp arrowhead=$arc]\n"; - } - elsif ($in2 =~ m/s$state1 -> s$state2 (.*)arrowhead=/){ - $tmp = $1; - if ($tmp =~ m/blue/){ - $tmp =~ s/blue/firebrick/; - } - else{ - $tmp =~ s/firebrick/blue/; - } - $arc =~ s/vee/normal/; - print CHECKED "s$state1 -> s$state2 $tmp style=dashed, arrowhead=$arc]\n"; - } - else{ - $arc =~ s/vee/normal/; - #$mid =~ s/color=\"[^\"]+/color=\"gray/; - print CHECKED "s$state1 -> s$state2 $mid style=dotted, arrowhead=$arc]\n"; - } -} -#Check the third column -while ($in2 =~ m/s([0-9]+) -> s([0-9]+) (.+)arrowhead=((vee|tee))/g){ - my $state1 = $1; - my $state2 = $2; - my $mid = $3; - my $arc = $4; - if ($in1 =~ m/s$state1 -> s$state2 /){ - #do nothing as this was already taken care of above - } - else{ - $mid =~ s/color=\"[^\"]+/color=\"black/; - $arc =~ s/(vee|normal)/onormal/; - $arc =~ s/tee/obox/; - print CHECKED "s$state1 -> s$state2 $mid style=dashed, arrowhead=$arc]\n"; - } -} - - - - -print CHECKED "\n}\n"; - -close CHECKED; - - diff --git a/bin/cir2data.py b/bin/cir2data.py deleted file mode 100644 index 1dcd67e4c..000000000 --- a/bin/cir2data.py +++ /dev/null @@ -1,292 +0,0 @@ -#!/usr/bin/python - -############################################################################## -## Copyright (c) 2007 by Scott R. Little -## University of Utah -## -## Permission to use, copy, modify and/or distribute, but not sell, this -## software and its documentation for any purpose is hereby granted -## without fee, subject to the following terms and conditions: -## -## 1. The above copyright notice and this permission notice must -## appear in all copies of the software and related documentation. -## -## 2. The name of University of Utah may not be used in advertising or -## publicity pertaining to distribution of the software without the -## specific, prior written permission of University of Utah. -## -## 3. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, -## EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY -## WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. -## -## IN NO EVENT SHALL UNIVERSITY OF UTAH OR THE AUTHORS OF THIS SOFTWARE BE -## LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES -## OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA -## OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON -## ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE -## OR PERFORMANCE OF THIS SOFTWARE. -## -############################################################################## -## DESCRIPTION: This script takes a circuit or SPICE output data and -## processes it in preparation for the data2lhpn script. Given a -## circuit file the script will run the specified simulations and -## conver the data. Given an ASCII SPICE RAW file the script will -## process the file. -############################################################################## - -### TODO ### -#Add Spectre support? -#Add support for *.cir files to run multiple simulations -#clean-up code that modifies loop variables (b/c the modification isn't really working) -#allow blank lines in the .bins file -#check to see if ngspice/ngsconvert exist and return an nice error message if they don't -#Add gnuplot script generation - -import re, os, sys -from optparse import OptionParser - -#Regular expressions -ngsNumVarsR = re.compile("No. Variables: ") -ngsNumPointsR = re.compile("No. Points: ") -ngsVarsR = re.compile("Variables:") -ngsValsR = re.compile("Values:") -micaDataInitR = re.compile("Index ") -spaceR = re.compile("\s+") -lSpaceR = re.compile("^\s+") -tSpaceR = re.compile("\s+$") -newLR = re.compile("\n+") -lNumR = re.compile("^\d") -numR = re.compile("\d") -varCleanR = re.compile("\(|\)|\#|:") - -############################################################################## -# Clean-up the given line by removing leading and trailing space as well as -# trailing new line characters. -############################################################################## -def cleanLine(line): - lineNS = re.sub(lSpaceR,"",line) - lineNL = re.sub(newLR,"",lineNS) - lineTS = re.sub(tSpaceR,"",lineNL) - return lineTS - -############################################################################## -# Clean up variable names (to make them compatible w/ the .g file format of -# ATACS) by removing any parens. -############################################################################## -def cleanVarName(line): - lineNP = re.sub(varCleanR,"",line) - return lineNP - -############################################################################## -# Convert ngspice data into the dat output format. -############################################################################## -def ngspiceConvert(asciiFile,datFile,nodesL): - print "Converting: "+asciiFile - numVars = -1 - numPoints = -1 - varNamesL = [] - valsL = [] - inputF = open(asciiFile, 'r') - outputF = open(datFile, 'w') - linesL = inputF.readlines() - for i in range(len(linesL)): - if ngsNumVarsR.match(linesL[i]): - varStrL = ngsNumVarsR.split(linesL[i]) - numVars = int(varStrL[1]) - print "Number of variables:"+str(numVars) - if ngsNumPointsR.match(linesL[i]): - pointStrL = ngsNumPointsR.split(linesL[i]) - numPoints = int(pointStrL[1]) - print "Number of points:"+str(numPoints) - if ngsVarsR.match(linesL[i]): - varHeaderL = spaceR.split(linesL[i]) - if varHeaderL[1] != "0": - i = i+1 - for j in range(numVars): - varName = linesL[i] - varNameNS = cleanLine(varName) - varNameL = spaceR.split(varNameNS) - i = i+1 - if len(nodesL) > 1: - if j in nodesL: - if varNameL[1] == "0": - #print "varName["+str(j)+"]: "+varNameL[2] - varNamesL.append(cleanVarName(varNameL[2])) - else: - #print "varName["+str(j)+"]: "+varNameL[1] - varNamesL.append(cleanVarName(varNameL[1])) - else: - #print "varName: "+varNameL[1] - varNamesL.append(cleanVarName(varNameL[1])) - if ngsValsR.match(linesL[i]): - i = i+1 - for j in range(numPoints): - varsL = [] - for k in range(numVars): - #print "i: "+str(i)+" j: "+str(j)+" k: "+str(k) - varVal = linesL[i] - varValNS = cleanLine(varVal) - #print "k:"+str(k) - if k == 0: - #print "["+str(k)+"] "+"varValNS:"+varValNS - varValL = spaceR.split(varValNS) - valStr = varValL[1] - else: - valStr = varValNS - #print "nodesL:"+str(len(nodesL))+":" - #print nodesL - if len(nodesL) > 1: - if k in nodesL: - varsL.append(valStr) - else: - varsL.append(valStr) - i = i+1 - #print "varsL:" - #print varsL - valsL.append(varsL) - #print "i:"+str(i)+" len(linesL):"+str(len(linesL))+" linesL[i+1]:"+str(linesL[i+1]) - if (i < len(linesL)) and not(numR.search(linesL[i])): - #print "Empty line" - i = i+1 #account for the empty line - break - if len(nodesL) > 1: - if len(nodesL) <= numVars: - numVars = len(nodesL) - else: - print "Error: the number of variables is less than the number of nodes provided on the command line." - return - outputF.write("Variables: "+str(numVars)+"\n") - outputF.write("Points: "+str(numPoints)+"\n") - for name in varNamesL: - outputF.write(name+" ") - outputF.write("\n") - for valL in valsL: - for val in valL: - outputF.write(val+" ") - outputF.write("\n") - inputF.close() - outputF.close() - -############################################################################## -# Convert mica data into the dat output format. This isn't really needed -# as Mica generates ASCII raw format which the standard script flow can read. -############################################################################## -def micaConvert(cirFile,datFile,nodesL): - numVars = -1 - numPoints = -1 - varNamesL = [] - valsL = [] - inputF = open(cirFile, 'r') - outputF = open(datFile, 'w') - linesL = inputF.readlines() - for i in range(len(linesL)): - if micaDataInitR.match(linesL[i]): - cleanLineStr = cleanLine(linesL[i]) - tempVarNamesL = spaceR.split(cleanLineStr) - tempVarNamesL.pop(0) - print "varNames:"+str(varNamesL) - if len(nodesL) > 1: - for j in range(tempVarNamesL): - if j in nodesL: - varNamesL.append(tempVarNameL[j]) - else: - varNamesL = tempVarNamesL - numVars = len(varNamesL) - numPoints = 0 - i = i+2 #skip the final header line and position i at the - #beginning of the data - while lNumR.match(linesL[i]): - print "line["+str(i)+"]"+linesL[i] - newValsL = spaceR.split(linesL[i]) - newValsL.pop(0) #remove the Index - if len(nodesL) > 1: - finalValsL = [] - for j in range(newValsL): - if j in nodesL: - finalValsL.append(newValsL[j]) - valsL.append(finalValsL) - else: - valsL.append(newValsL) - i = i + 1 - numPoints = numPoints + 1 - break - outputF.write("Variables: "+str(numVars)+"\n") - outputF.write("Points: "+str(numPoints)+"\n") - for name in varNamesL: - outputF.write(name+" ") - outputF.write("\n") - for valL in valsL: - for val in valL: - outputF.write(val+" ") - outputF.write("\n") - inputF.close() - outputF.close() - -############################################################################## -############################################################################## -def main(): - usage = "usage: %prog [options] file1 ... fileN" - parser = OptionParser(usage=usage) - parser.set_defaults(doSpice=False, doRawConvert=False, simType="ngspice", nodes="") - parser.add_option("-n", "--nodes", action="store", dest="nodes", help="A comma separated list of the nodes to output to the data file. Time is row 0 and always output.") - parser.add_option("-s", "--spice", action="store_true", dest="doSpice", help="Run ngspice on the provided file(s).") - parser.add_option("-r", "--rawconvert", action="store_true", dest="doRawConvert", help="Run ngsconvert on the provided file(s). Providing -s automatically enables -r.") - parser.add_option("-t", "--type", action="store", dest="simType", help="Select the type of simulator used.") - - (options, args) = parser.parse_args() - - if len(args) > 0: - fileL = args - else: - print "At least one data file is required." - parser.print_help() - sys.exit() - - nodesStrL = options.nodes.split(',') - nodesL = [] - if (options.nodes != ""): - for s in nodesStrL: - nodesL.append(int(s)) - #Ensure that time gets output - if not(0 in nodesL): - nodesL.insert(0,0) - #print "nodesL:"+str(nodesL) - - for i in range(len(fileL)): - baseFileL = os.path.splitext(fileL[i]) - cirFile = fileL[i] - rawFile = baseFileL[0] + ".raw" - asciiFile = baseFileL[0] + ".ascii" - datFile = baseFileL[0] + ".dat" - - if(options.doRawConvert or options.doSpice): - convertFile = asciiFile - else: - convertFile = fileL[i] - - if(options.doSpice): - spiceCmd = spicePath + " -b -r " + rawFile + " " + cirFile - if(os.system(spiceCmd) != 0): - print "An error occured with the following SPICE command: " + spiceCmd - - if(options.doRawConvert or options.doSpice): - rawConvertCmd = rawConvertPath + " b " + rawFile + " a " + asciiFile - if(os.system(rawConvertCmd) != 0): - print "An error occured with the following raw convert command: " + rawConvertCmd - - if options.simType == "ngspice": - ngspiceConvert(convertFile,datFile,nodesL) - elif options.simType == "mica": - micaConvert(convertFile,datFile,nodesL) - -############################################################################## -############################################################################## - -########### -# Globals # -########### -spicePath = "ngspice" -rawConvertPath = "ngsconvert" - -if __name__ == "__main__": - main() diff --git a/bin/data2lhpn.py b/bin/data2lhpn.py deleted file mode 100644 index b9d132341..000000000 --- a/bin/data2lhpn.py +++ /dev/null @@ -1,2700 +0,0 @@ -#!/usr/bin/python - -############################################################################## -## Copyright (c) 2007 by Scott R. Little -## University of Utah -## -## Permission to use, copy, modify and/or distribute, but not sell, this -## software and its documentation for any purpose is hereby granted -## without fee, subject to the following terms and conditions: -## -## 1. The above copyright notice and this permission notice must -## appear in all copies of the software and related documentation. -## -## 2. The name of University of Utah may not be used in advertising or -## publicity pertaining to distribution of the software without the -## specific, prior written permission of University of Utah. -## -## 3. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, -## EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY -## WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. -## -## IN NO EVENT SHALL UNIVERSITY OF UTAH OR THE AUTHORS OF THIS SOFTWARE BE -## LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGESp -## OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA -## OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON -## ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE -## OR PERFORMANCE OF THIS SOFTWARE. -## -############################################################################## -## DESCRIPTION: This script takes as input .dat files that have been -## preprocessed by cir2data.py, a .bins file that gives threshold -## information for the data as well as configuration information for -## the graph generation, and an optional file containing a property to -## verify. The script processes the data files and generates a graph. -## This graph is output as an LHPN, VHDL-AMS, and Verilog-A file. The -## Verilog-A file is deterministic and therefore potentially contains -## fewer behaviors. -############################################################################## - -### TODO ### -#Property Parsing -##scale property values? -##check property to see if it contains valid variables or at least munge those variable names similar to other variables? -##Add property output to the VHDL-AMS output -#Think about dealing w/ initial conditions that aren't the same (check for multiple tokens in a loop on initialization?) -#devise some units tests -#determine how to handle normalization overflow/underflow -#allow epsilon values on a per variable basis? -#consider potentially breaking the state or creating a special start and/or end state instead of removing "spurious" start and end information -#add code to extract the proper places and check for cycles after the graph has been built -#update coverage information for non-input DMVC variables -#what about autodetecting items like pathLength? -#determine how to calculate delay for non-input DMVC variable assignments -#write a range of variable assignments as it is now supported by the g parser -#Remove expansion places and push the assignments to the proper transition -#Check for consistent starting state -#Add rate assignment removal when an assignment won't change the rate. -#Add a places and transitions count for the LHPN...add it to the comments -#Add a debug level option to turn off temporary file generation -#Allow the sliding window to be time based as well? -#Ensure that rates are in a place for a specified length of time (fix normalization) - -import re, os.path, fpformat, math, sys, copy, cText -from optparse import OptionParser - -#Regular expressions -numVarsR = re.compile("Variables: ") -numPointsR = re.compile("Points: ") -spaceR = re.compile("\s+") -lSpaceR = re.compile("^\s+") -tSpaceR = re.compile("\s+$") -lineSpaceR = re.compile("^\s+$") -newLR = re.compile("\n+") -lDotR = re.compile("^\.\w") -epsilonR = re.compile(".epsilon") -lengthR = re.compile(".length") -timeR = re.compile(".time") -absoluteTimeR = re.compile(".absoluteTime") -percentR = re.compile(".percent") -inputR = re.compile(".inputs") -outputR = re.compile(".outputs") -dmvcR = re.compile(".dmvc") -rateSamplingR = re.compile(".rateSampling") -pathLengthR = re.compile(".pathLength") -vaRateUpdateIntervalR = re.compile(".vaRateUpdateInterval") -minDelayValR = re.compile(".minDelayVal") -minRateValR = re.compile(".minRateVal") -minDivisionValR = re.compile(".minDivisionVal") -decPercentR = re.compile(".decPercent") -minVarValR = re.compile(".minVarVal") -maxVarValR = re.compile(".maxVarVal") -falseR = re.compile("false",re.I) #pass the I flag to be case insensitive -trueR = re.compile("true",re.I) #pass the I flag to be case insensitive -binCommentR = re.compile("^\#") -lParenR = re.compile("\\(+") -rParenR = re.compile("\\)+") -rowsR = re.compile("\\(.*?\\)") -lQuoteR = re.compile("\"+") -tQuoteR = re.compile("\"+") - -############################################################################## -# A class to hold the lists of places and transitions in the graph. -############################################################################## -class Variable: - "A continuous variable in the system being modeled." - def __init__(self,nameStr): - self.name = nameStr #the name of the variable - self.dmvc = None #Boolean denoting the status of the variable as a discrete multi-valued continuous (DMVC) variable - self.input = None #Boolean denoting that the variable is a model input - self.output = None #Boolean denoting that the variable is a model output - self.type = None #Describes the type of the variable using an enumerated type (VOLTAGE, CURRENT) which is needed by Verilog-A. - def __str__(self): - retStr = self.name+"[" - if self.dmvc: - retStr = retStr + "1]" - else: - retStr = retStr + "0]" - return retStr -## End Class Variable ######################################################## - -############################################################################## -# A class to hold the lists of places and transitions in the graph. -############################################################################## -class Coverage: - "The coverage statistics generated by the model generation." - def __init__(self,filesL): - self.index = -1 #an index for the file currently being processed - self.filesL = filesL #a list of the file names to be processed - self.placesL = [] #a list of the number of new places created by each file - self.transitionsL = [] #a list of the number of new transistions created by each file - self.ratesL = [] #a list of the number of new rates created by each file - self.delaysL = [] #a list of the number of new delays created by each file - for i in range(len(filesL)): - self.placesL.append(0) - self.transitionsL.append(0) - self.ratesL.append(0) - self.delaysL.append(0) - def __str__(self): - retStr = "" - for i in range(len(self.filesL)): - retStr += self.filesL[i]+":\n" - retStr += "\tPlaces:"+str(self.placesL[i])+"\n" - retStr += "\tTransitions:"+str(self.transitionsL[i])+"\n" - retStr += "\tRates:"+str(self.ratesL[i])+"\n" - retStr += "\tDelays:"+str(self.delaysL[i])+"\n" - return retStr - ############################################################################ - # Increment the rate list if a new rate is found.. - ############################################################################ - def isNewRate(self,t1,t2): - newRate = False - if t1[0] != t2[0]: - self.ratesL[self.index] += 1 - newRate = True - if t1[1] != t2[1]: - self.ratesL[self.index] += 1 - newRate = True - return newRate - ############################################################################ - # Increment the delay list if a new delay is found.. - ############################################################################ - def isNewDelay(self,t1,t2): - if t1[0] != t2[0]: - self.delaysL[self.index] += 1 - if t1[1] != t2[1]: - self.delaysL[self.index] += 1 -## End Class Coverage ######################################################## - -############################################################################## -# A class for data required for the discovery of discrete multi-valued -# continuous variables. -############################################################################## -class DMVCpart: - "Information about a single run of a constant value." - numDMVCparts = 0 - def __init__(self): - self.id = DMVCpart.numDMVCparts #unique numeric ID for each run - DMVCpart.numDMVCparts = DMVCpart.numDMVCparts + 1 - self.varInd = -1 #an index into the varsL array denoting which variable owns the run - self.valueL = [] #the list of values for this run - self.startPoint = -1 #an index into datL for the start point of the run - self.endPoint = -1 #an index into datL for the end point of the run - self.nextRun = None #a reference to the next sequential run - def __str__(self): - retStr = "Part:"+str(self.id)+" Start:"+str(self.startPoint)+" End:"+str(self.endPoint)+" Val:"+str(self.constVal()) - if self.nextRun: - retStr += " Next:"+str(self.nextRun.id) - else: - retStr += " Next:None" - return retStr - ############################################################################ - # Calculate the constant value from the value list. Currently it is an - # average of all list values. - ############################################################################ - def constVal(self): - total = 0 - for i in self.valueL: - total = total + i - return total/float(len(self.valueL)) - ############################################################################ - # Calculate the delay for a given DMVC run. - ############################################################################ - def calcDelay(self,datL): - ind1 = self.startPoint - ind2 = self.endPoint - delay = datL[ind2][0]-datL[ind1][0] - #Assuming that there is some time between runs we want to account for that time. If we assume a constant rate of change we can just split the difference - if self.nextRun: - ind3 = self.nextRun.startPoint - delay += ((datL[ind3][0]-datL[ind2][0])/2) - return delay -## End Class DMVCpart ######################################################## - -############################################################################## -# A class to store data regarding non-input DMVC assignments. -############################################################################## -class AsgnPart: - "Information about an individual assignment that will be made based on predicate values." - def __init__(self): - self.var = None #the variable that is being assigned - self.valL = [] #a list of the values for the variable collected via DMVC analysis - self.delayL = [] #a list of the delays values for the variable collected via DMVC analysis. This is the delay from the previous bin change to the bin where the assignment has happened. - def __str__(self): - retStr = "Var:"+str(self.var)+"\n" - retStr += "\tValues:"+str(self.valL) - retStr += "\tDelays:"+str(self.delayL) - return retStr - ############################################################################ - # Calculate the minimum delay. - ############################################################################ - def minDelay(self): - return min(self.delayL) - ############################################################################ - # Calculate the minimum delay and return a properly rounded integer string. - ############################################################################ - def minDelayInt(self): - return str(int(math.floor(min(self.delayL)))) - ############################################################################ - # Calculate the minimum delay. - ############################################################################ - def maxDelay(self): - return max(self.delayL) - ############################################################################ - # Calculate the maximum delay and return a properly rounded integer string. - ############################################################################ - def maxDelayInt(self): - return str(int(math.ceil(max(self.delayL)))) - ############################################################################ - # Calculate the average delay. - ############################################################################ - def avgDelay(self): - total = 0 - for val in self.delayL: - total += val - return total/len(self.delayL) - ############################################################################ - # Calculate the minimum value. - ############################################################################ - def minValue(self): - return min(self.valL) - ############################################################################ - # Calculate the maximum value. - ############################################################################ - def maxValue(self): - return max(self.valL) - ############################################################################ - # Calculate the average value. - ############################################################################ - def avgValue(self): - total = 0 - for val in self.valL: - total += val - return total/len(self.valL) -## End Class AsgnPart ######################################################## - -############################################################################## -# A class to hold the parameters specified in the threshold (.bins) file. -############################################################################## -class ThresholdParameters: - "The parameters possibly specified in the thresholds (.bins) file." - def __init__(self,numVars): - #Default values - self.epsilon = 0.1 #What is the +/- epsilon where signals are considered to be equivalent - self.length = 15 #the number of time points that a value must persist to be considered constant - self.time = 5e-6 #the amount of time that must pass to be considered constant when using absoluteTime - self.absoluteTime = False #when False time points are used to determine DMVC and when true absolutime time is used to determine DMVC - self.percent = 0.8 #a decimal value representing the percent of the total trace that must be constant to qualify to become a DMVC var - self.numValuesL = [] #the number of constant values for each variable...-1 indicates that the variable isn't considered a DMVC variable - self.vaRateUpdateInterval = 1e-6 #how often the rate is added to the continuous variable in the Verilog-A model output - for i in range(numVars): - self.numValuesL.append(-1) - def __str__(self): - retStr = "epsilon:"+str(self.epsilon)+" length:"+str(self.length)+" numValuesL:"+str(self.numValuesL) - return retStr - ############################################################################ - # Determine if two values are equal within the given epsilon. - ############################################################################ - def epsilonEquiv(self,v1,v2): - if abs(v1-v2) <= self.epsilon: - return True - else: - return False -## End Class ThresholdParameters ############################################# - -############################################################################## -# A class to hold the lists of places and transitions in the graph. -############################################################################## -class Graph: - "A graph containing the places, transitions, and rates required to generate an analog model." - def __init__(self,varsL,failProp): - self.placeD = {} #a dictionary of places where the key is a string of binEncodingL joined with "" - self.transitionD = {} #a dictionary of transitions where the key is a tuple of (incomingP, outgoingP) - self.initRateL = [] #a list of rates for the initial place encoding - for i in range(len(varsL)): - self.initRateL.append([]) - self.initValL = [] #a list of initial values in the initial place - for i in range(len(varsL)): - self.initValL.append([]) - self.initMarkingL = [] - self.delayScaleFactor = -1 - self.varScaleFactor = -1 - self.failProp = failProp - #Generate the place and transition for the failure property - if failProp: - self.failProp = True - place = Place(varsL,PROP) - place.binEncodingL = ["f1"] - place.property = failProp - self.placeD[place.keyStr()] = place - self.initMarkingL.append(place) - transition = Transition(len(varsL),None,place) - self.transitionD[(None,place.keyStr())] = transition - place.outgoingL.append(transition) - def __str__(self): - retStr = "" - joinStr = "" - retStr = retStr + "Places:\n" - placeL = self.placeD.values() - placeL.sort() - for p in placeL: - retStr = retStr + str(p) - retStr = retStr + "Transitions:\n" - transitionL = self.transitionD.values() - transitionL.sort() - for t in transitionL: - retStr = retStr + str(t) - return retStr - ############################################################################ - # Return a string containg the minimum initial rate for the given variable. - # The string is a conservatively rounded integer. - ############################################################################ - def minInitRateInt(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.initRateL[i])): - if self.initRateL[i][j] != '-': - cmpL.append(self.initRateL[i][j]) - if len(cmpL) > 0: - return str(int(math.floor(min(cmpL)))) - else: - return "-" - ############################################################################ - # Return a string containing the maximum initial rate for the given - # variable. The string is a conservatively rounded integer. - ############################################################################ - def maxInitRateInt(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.initRateL[i])): - if self.initRateL[i][j] != '-': - cmpL.append(self.initRateL[i][j]) - if len(cmpL) > 0: - return str(int(math.ceil(max(cmpL)))) - else: - return "-" - ############################################################################ - # Return a string containg the minimum initial value for the given variable. - # The string is a conservatively rounded integer. - ############################################################################ - def minInitValInt(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.initValL[i])): - if self.initValL[i][j] != '-': - cmpL.append(self.initValL[i][j]) - if len(cmpL) > 0: - return str(int(math.floor(min(cmpL)))) - else: - return "-" - ############################################################################ - # Return a string containing the maximum initial value for the given - # variable. The string is a conservatively rounded integer. - ############################################################################ - def maxInitValInt(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.initValL[i])): - if self.initValL[i][j] != '-': - cmpL.append(self.initValL[i][j]) - if len(cmpL) > 0: - return str(int(math.ceil(max(cmpL)))) - else: - return "-" - ############################################################################ - # Calculate the minimum non-zero delay for the graph. - ############################################################################ - def minDelay(self): - minDelay = None - for p in self.placeD.values(): - if p.isDmvcP(): - if not minDelay and p.minTime() != "-" and p.minTime != 0: - minDelay = p.minTime() - elif p.minTime() != "-" and p.minTime() < minDelay and p.minTime() != 0: - minDelay = p.minTime() - elif p.isAsgnP(): - for a in p.asg: - if a.valL: - if not minDelay and a.minDelay() != "-" and a.minDelay() != 0: - minDelay = a.minDelay() - elif a.minDelay() != "-" and a.minDelay() < minDelay and a.minDelay() != 0: - minDelay = a.minDelay() - return minDelay - ############################################################################ - # Calculate the maximum delay for the graph. - ############################################################################ - def maxDelay(self): - maxDelay = None - for p in self.placeD.values(): - if p.isDmvcP(): - if not maxDelay and p.maxTime() != "-": - maxDelay = p.maxTime() - elif p.maxTime() != "-" and p.maxTime() > maxDelay: - maxDelay = p.maxTime() - elif p.isAsgnP(): - for a in p.asg: - if a.valL: - if not maxDelay and a.maxDelay() != "-": - maxDelay = a.maxDelay() - elif a.maxDelay() != "-" and a.maxDelay() > maxDelay: - maxDelay = a.maxDelay() - return maxDelay - ############################################################################ - # Calculate the minimum non-zero rate for the graph. - ############################################################################ - def minRate(self): - minRate = None - for p in self.placeD.values(): - if p.isRateP(): - for i in range(1,len(p.ratesL)): - if not minRate and p.minRate(i) != "-" and p.minRate(i) != 0: - minRate = p.minRate(i) - elif p.minRate(i) != "-" and p.minRate(i) < minRate and p.minRate(i) != 0: - minRate = p.minRate(i) - return minRate - ############################################################################ - # Calculate the maximum rate for the graph. - ############################################################################ - def maxRate(self): - maxRate = None - for p in self.placeD.values(): - if p.isRateP(): - for i in range(1,len(p.ratesL)): - if not maxRate and p.maxRate(i) != "-": - maxRate = p.maxRate(i) - elif p.maxRate(i) != "-" and p.maxRate(i) > maxRate: - maxRate = p.maxRate(i) - return maxRate - ############################################################################ - # Scale the delay values in the graph by scaleFactor. - ############################################################################ - def scaleDelay(self,scaleFactor,squash): - for p in self.placeD.values(): - if p.isDmvcP(): - for i in range(len(p.dmvcTimeL[p.dmvcVar])): - p.dmvcTimeL[p.dmvcVar][i] *= scaleFactor - if p.isAsgnP(): - for a in p.asg: - for i in range(len(a.delayL)): - a.delayL[i] *= scaleFactor - if p.isRateP(): - for i in range(1,len(p.ratesL)): - for j in range(len(p.ratesL[i])): - if p.ratesL[i][j] != "-": - p.ratesL[i][j] /= scaleFactor - if(squash): - if(abs(p.ratesL[i][j]) < 10): - p.ratesL[i][j] = 0 - for i in range(1,len(self.initRateL)): - for j in range(len(self.initRateL[i])): - if self.initRateL[i][j] != "-": - self.initRateL[i][j] /= scaleFactor - if(squash): - if(abs(self.initRateL[i][j]) < 10): - self.initRateL[i][j] = 0 - ############################################################################ - # Scale the variable values in the graph by scaleFactor. - ############################################################################ - def scaleVariable(self,scaleFactor,divisionsL): - for p in self.placeD.values(): - if p.isRateP(): - for i in range(1,len(p.ratesL)): - for j in range(len(p.ratesL[i])): - if p.ratesL[i][j] != "-": - p.ratesL[i][j] *= scaleFactor - elif p.isDmvcP(): - p.dmvcVal *= scaleFactor - elif p.isAsgnP(): - for a in p.asg: - for i in range(len(a.valL)): - a.valL[i] *= scaleFactor - #Scale initial values, initial rates, and thresholds. - for i in range(1,len(self.initValL)): - for j in range(len(self.initValL[i])): - self.initValL[i][j] *= scaleFactor - if self.initRateL[i][j] != "-": - self.initRateL[i][j] *= scaleFactor - for j in range(len(divisionsL[i])): - divisionsL[i][j] *= scaleFactor -## End Class Graph ########################################################### - -############################################################################## -# A class to hold a place and its corresponding information. -############################################################################## -class Place: - "A place in the generated LHPN." - numPlaces = 0 - varsL = [] - def __init__(self,varsL,pType): - self.placeNum = Place.numPlaces #unique numeric ID for each place - Place.numPlaces = Place.numPlaces + 1 - self.type = pType #a string giving the place an enumerated type (RATE,DMVC, or PROP) - self.binEncodingL = [] #an encoding for the place based on bins or for DMVC places d_varsL index_id# - self.ratesL = [] #a list of lists (numVars long) containing rates that correspond to the place - for i in range(len(varsL)): - self.ratesL.append([]) - self.dmvcTimeL = [] #a list of the times for each dmvc run that composes the place - for i in range(len(varsL)): - self.dmvcTimeL.append([]) - self.dmvcVal = None #the DMVC constant value for the place - self.dmvcVar = None #an index into varsL for the DMVC variable - self.dmvcInfoL = [] #a list that is indexed by divisions and contains value and delay information for places representing non-input DMVC variables - self.asg = [] #a list of assignments to be made in this place - self.asgBinL = [] #the bin encoding for the place from which the asgn place is expanded - self.preExGrandParentBinL = None #the bin encoding for the pre-expansion grandparent place - self.property = "" #the property for PROP places - self.color = 0 #color for use is cycle detection (0=white,1=grey,2=black) - self.outgoingL = [] #a list of outgoing transitions - self.incomingL = [] #a list of incoming transitions - Place.varsL = varsL - def __str__(self): - retStr = "p" + str(self.placeNum) + " ("+self.keyStr()+") " - if self.isRateP(): - retStr += "[rate]\n" - for i in range(1,len(Place.varsL)): - if not Place.varsL[i].dmvc: - retStr += "\t" + Place.varsL[i].name + ":rate[" + str(self.minRate(i)) + "," + str(self.maxRate(i)) + "]\n" - elif self.isDmvcP(): - retStr += "[dmvc]\n" - for i in range(1,len(Place.varsL)): - if Place.varsL[i].dmvc: - retStr += "\t" + Place.varsL[i].name + ":time[" + str(self.minTime()) + "," + str(self.maxTime()) + "]\n" - elif self.isPropP(): - retStr += "[prop]\n" - retStr += "\t" + self.property + "\n" - elif self.isAsgnP(): - retStr += "[asgn]\n" - elif self.isTraceP(): - retStr += "[trace]\n" - outL = [] - for outgoing in self.outgoingL: - if outgoing: - outL.append(str(outgoing.transitionNum)) - inL = [] - for incoming in self.incomingL: - if incoming: - inL.append(str(incoming.transitionNum)) - joinStr = "," - retStr += "\tIncoming t: " + joinStr.join(inL) + "\n" + "\tOutgoing t: " + joinStr.join(outL) + "\n" - return retStr - def __eq__(self,other): - if self.keyStr() == other.keyStr(): - return True - else: - return False - def __ne__(self,other): - return not (self == other) - def __cmp__(self,other): - if self.placeNum < other.placeNum: - return -1 - elif self.placeNum == other.placeNum: - return 0 - else: - return 1 - ############################################################################ - # Return a list of variables where the bin differs between the give places. - ############################################################################ - def diff(self,other,varsL): - diffL = [] - if self.isAsgnP(): - selfL = self.asgBinL - else: - selfL = self.binEncodingL - if other.isAsgnP(): - otherL = other.asgBinL - else: - otherL = other.binEncodingL - for i in range(min((len(selfL),len(otherL)))): - if selfL[i] != otherL[i]: - diffL.append(i+1) #This is i+1 b/c binEncoding omits the time variable (varsL[0]) and this list should consistently index into varsL - if(len(diffL)>1): - #Verilog-A supports multiple changes on DMVC variables, but not on continuous rate variables. - warn = False - for j in diffL: - if not varsL[j].dmvc: - warn = True - if warn: - cStr = cText.cSetFg(cText.YELLOW) - cStr += "WARNING:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" a transition connects places (p"+str(self.placeNum)+",p"+str(other.placeNum)+") where more than one bin changes." - return diffL - ############################################################################ - # Return an integer that describes the order of meta-bins. If the order is - # 1 the bin is reachable from a core bin in 1 step, etc. - ############################################################################ - def orderMetaBin(self,divisionsL): - order = 0 - for i in range(len(self.binEncodingL)): - if self.binEncodingL[i] == -1 or self.binEncodingL[i] == (len(divisionsL[i+1])+1): - order = order + 1 - return order - ############################################################################ - # Remove all values greater than zero in the rate list for the given - # variable. - ############################################################################ - def rmRateGtZero(self,i): - newRatesL = [] - if int(self.maxRateInt(i)) > 0: - newRatesL.append(0) - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] <= 0: - newRatesL.append(self.ratesL[i][j]) - if len(newRatesL) == 0: - newRatesL.append(0) - self.ratesL[i] = newRatesL - ############################################################################ - # Remove all values greater than zero in the rate list for the given - # variable. - ############################################################################ - def rmRateLtZero(self,i): - newRatesL = [] - if int(self.minRateInt(i)) < 0: - newRatesL.append(0) - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] >= 0: - newRatesL.append(self.ratesL[i][j]) - if len(newRatesL) == 0: - newRatesL.append(0) - self.ratesL[i] = newRatesL - ############################################################################ - # Return a string containg the minimum rate for the given place. The - # string is a conservatively rounded integer. - ############################################################################ - def minRateInt(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] != '-': - cmpL.append(self.ratesL[i][j]) - if len(cmpL) > 0: - return str(int(math.floor(min(cmpL)))) - else: - return "-" - ############################################################################ - # Return a string containing the maximum rate for the given place. The - # string is a conservatively rounded integer. - ############################################################################ - def maxRateInt(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] != '-': - cmpL.append(self.ratesL[i][j]) - if len(cmpL) > 0: - return str(int(math.ceil(max(cmpL)))) - else: - return "-" - ############################################################################ - # Return the minimum rate for the given place. - ############################################################################ - def minRate(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] != '-': - cmpL.append(self.ratesL[i][j]) - if len(cmpL) > 0: - return min(cmpL) - else: - return "-" - ############################################################################ - # Return the maximum rate for the given place. - ############################################################################ - def maxRate(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] != '-': - cmpL.append(self.ratesL[i][j]) - if len(cmpL) > 0: - return max(cmpL) - else: - return "-" - ############################################################################ - # Return the average rate for the given place. - ############################################################################ - def avgRate(self,i): - cmpL = [] - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] != '-': - cmpL.append(self.ratesL[i][j]) - if len(cmpL) > 0: - total = 0 - for val in cmpL: - total += val - return total/float(len(cmpL)) - else: - return "-" - ############################################################################ - # Return the minimum timing bound for the place as a properly rounded - # integer string. - ############################################################################ - def minTimeInt(self): - if self.dmvcTimeL[self.dmvcVar]: - return str(int(math.floor(min(self.dmvcTimeL[self.dmvcVar])))) - else: - return "-" - ############################################################################ - # Return the maximum timing bound for the place as a properly rounded - # integer string. - ############################################################################ - def maxTimeInt(self): - if self.dmvcTimeL[self.dmvcVar]: - return str(int(math.ceil(max(self.dmvcTimeL[self.dmvcVar])))) - else: - return "-" - ############################################################################ - # Return the minimum timing bound for the place. - ############################################################################ - def minTime(self): - if self.dmvcTimeL[self.dmvcVar]: - return min(self.dmvcTimeL[self.dmvcVar]) - else: - return "-" - ############################################################################ - # Return the maximum timing bound for the place. - ############################################################################ - def maxTime(self): - if self.dmvcTimeL[self.dmvcVar]: - return max(self.dmvcTimeL[self.dmvcVar]) - else: - return "-" - ############################################################################ - # Return the avg timing bound for the place. - ############################################################################ - def avgTime(self): - totVal = 0 - for val in self.dmvcTimeL[self.dmvcVar]: - totVal += val - return totVal/float(len(self.dmvcTimeL[self.dmvcVar])) - ############################################################################ - # Return a string representing the bin encoding for the place. - ############################################################################ - def keyStr(self): - keyStr = "" - for i in range(len(self.binEncodingL)): - keyStr += str(self.binEncodingL[i]) - return keyStr - ############################################################################ - # Return True if the place is along the edge of the thresholds and may - # require limiting places and false otherwise. - ############################################################################ - def isEdgeP(self,divisionsL): - for i in range(len(self.binEncodingL)): - if self.binEncodingL[i] == 0 or self.binEncodingL[i] == (len(divisionsL[i+1])): - return True - return False - ############################################################################ - # Return True if the place is not a meta-bin. - ############################################################################ - def isCoreP(self,divisionsL): - for i in range(len(self.binEncodingL)): - if self.binEncodingL == -1 or self.binEncodingL[i] == len(divisionsL[i+1])+1: - return False - return True - ############################################################################ - # Return True if the place is representing a RATE place and false otherwise. - ############################################################################ - def isRateP(self): - if self.type == RATE: - return True - else: - return False - ############################################################################ - # Return True if the place is representing a DMVC place and false otherwise. - ############################################################################ - def isDmvcP(self): - if self.type == DMVC: - return True - else: - return False - ############################################################################ - # Return True if the place is representing a PROP place and false otherwise. - ############################################################################ - def isPropP(self): - if self.type == PROP: - return True - else: - return False - ############################################################################ - # Return True if the place is representing a ASGN place and false otherwise. - ############################################################################ - def isAsgnP(self): - if self.type == ASGN: - return True - else: - return False - ############################################################################ - # Return True if the place represents a TRACE place and false otherwise. - ############################################################################ - def isTraceP(self): - if self.type == TRACE: - return True - else: - return False -## End Class Place ########################################################### - -############################################################################## -# A class to hold a transition and its required data items. -############################################################################## -class Transition: - "A transition in the generated LHPN." - numTransitions = 0 - def __init__(self,numVars): - self.transitionNum = Transition.numTransitions #unique numeric ID for each transition - Transition.numTransitions = Transition.numTransitions + 1 - self.ratesL = [] #a list of lists (numVars long) containing rates that - #correspond to the rates in the outgoingP - for i in range(numVars): - self.ratesL.append([]) - self.outgoingP = None #Place in the postset of the transition - self.incomingP = None #Place in the preset of the transition - self.core = False - def __init__(self,numVars,outgoingP,incomingP): - self.transitionNum = Transition.numTransitions #unique numeric ID for each transition - Transition.numTransitions = Transition.numTransitions + 1 - self.ratesL = [] #a list of lists (numVars long) containing rates that - #correspond to the rates in the outgoingP - for i in range(numVars): - self.ratesL.append([]) - self.outgoingP = outgoingP #Place in the postset of the transition - self.incomingP = incomingP #Place in the preset of the transition - self.core = False - def __str__(self): - retStr = "t" + str(self.transitionNum) + "\n" - minRateStr = "-" - maxRateStr = "-" - maxRateL = [] - minRateL = [] - for j in range(1,len(self.ratesL)): - maxRateL.append(self.maxRateInt(j)) - minRateL.append(self.minRateInt(j)) - retStr += "\tmaxRate: " + str(maxRateL) + " minRate: " + str(minRateL) + "\n" - if self.outgoingP: - retStr += "\tOutgoing p: " + str(self.outgoingP.placeNum) + "\n" - if self.incomingP: - retStr += "\tIncoming p: " + str(self.incomingP.placeNum) + "\n" - return retStr - def __eq__(self,other): - if self.outgoingP != other.outgoingP: - return False - if self.incomingP != other.incomingP: - return False - return True - def __ne__(self,ohter): - return not(self == other) - def __cmp__(self,other): - if self.transitionNum < other.transitionNum: - return -1 - elif self.transitionNum == other.transitionNum: - return 0 - else: - return 1 - ############################################################################ - # Return a string containing the maximum rate for the given transition - ############################################################################ - def maxRateInt(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] != '-': - cmpL.append(self.ratesL[i][j]) - if len(cmpL) > 0: - return str(int(math.ceil(max(cmpL)))) - else: - return "-" - ############################################################################ - # Return a string containing the maximum rate for the given transition - ############################################################################ - def minRateInt(self,i): - #Remove the characters from the list before doing the comparison - cmpL = [] - for j in range(len(self.ratesL[i])): - if self.ratesL[i][j] != '-': - cmpL.append(self.ratesL[i][j]) - if len(cmpL) > 0: - return str(int(math.floor(min(cmpL)))) - else: - return "-" -## End Class Transition ###################################################### - -############################################################################## -# Given a place this function does a DFS of the graph. When a place -# is first visited it is colored grey. If during the DFS another grey -# place is encountered this indicates a cycle. When all outgoing -# transitions for a node have been explored that place is colored -# black. -############################################################################## -def colorGraph(p): - #print "colorGraph(p"+str(p.placeNum)+":"+str(p.color)+")" - if p.color == 1: - return True - elif p.color == 2: - return False - else: - #print "Coloring: p"+str(p.placeNum)+":gray" - p.color = 1 - for out in p.outgoingL: - if colorGraph(out.outgoingP): - return True - #print "Coloring: p"+str(p.placeNum)+":black" - p.color = 2 - return False - -############################################################################## -# Given a list of places determine if there is a cycle. -############################################################################## -def isCycle(placeL): - for p in placeL: - if colorGraph(p): - return True - return False - -############################################################################## -# Remove leading & trailing space as well as trailing new line characters. -############################################################################## -def cleanLine(line): - lineNS = re.sub(lSpaceR,"",line) - lineNL = re.sub(newLR,"",lineNS) - lineTS = re.sub(tSpaceR,"",lineNL) - return lineTS - -############################################################################## -# Remove leading & trailing space as well as trailing new line characters. -############################################################################## -def cleanName(name): - nameNL = re.sub(lQuoteR,"",name) - nameTS = re.sub(tQuoteR,"",nameNL) - return nameTS - -############################################################################## -# Creates a 2 dimensional array of lists rows x cols with each value -# initialized to initVal. -############################################################################## -def create2Darray(rows,cols,initVal): - newL = [] - for i in range(rows): - initL = [] - for j in range(cols): - initL.append(initVal) - newL.append(initL) - return newL - -############################################################################## -# Return true if a DMVC variable exists and false otherwise. -############################################################################## -def dmvcVarExists(varsL): - for var in varsL: - if var.dmvc: - return True - return False - -############################################################################## -# Create the list of variables. All data files must have the same variables -# in the same order. -############################################################################## -def extractVars(datFile): - varsL = [] - line = "" - inputF = open(datFile, 'r') - rowsL = inputF.read() - rowsM = rowsR.match(rowsL) - row = rowsM.group() - varNames = cleanRow(row) - varNamesL = varNames.split(",") - for varStr in varNamesL: - varStr = cleanName(varStr) - varsL.append(Variable(varStr)) - varsL[0].dmvc = False - inputF.close() - return varsL - -############################################################################## -# Parse a .dat file ensuring that the varsL matches the global list. -############################################################################## -def parseDatFile(datFile,varsL): - inputF = open(datFile, 'r') - linesL = inputF.read() - rowsL = rowsR.findall(linesL) - for i in range(len(rowsL)): - rowsL[i] = cleanRow(rowsL[i]) - numPoints = -1 - varNames = cleanRow(rowsL[0]) - varNamesL = [] - varNamesL = varNames.split(",") - numPoints = len(varNamesL) - if len(varNamesL) == len(varsL): - for i in range(len(varNamesL)): - varNamesL[i] = cleanName(varNamesL[i]) - if varNamesL[i] != varsL[i].name: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Expected "+varsL[i].name+" in position "+str(i)+" but received "+varNamesL[i]+" in file: "+datFile - sys.exit() - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr + " Expected "+str(len(varsL))+" variables but received "+str(len(varNamesL))+" in file: "+datFile - sys.exit() - - datL = [] - for i in range(1,len(rowsL)): - valStrL = cleanRow(rowsL[i]).split(",") - valL = [] - for s in valStrL: - valL.append(float(s)) - datL.append(valL) - inputF.close() - return datL, numPoints - -############################################################################## -# Remove leading and trailing parantheses -############################################################################## -def cleanRow(row): - rowNS = re.sub(lParenR,"",row) - rowTS = re.sub(rParenR,"",rowNS) - return rowTS - -############################################################################## -# Parse the .bins (thresholds) file. -############################################################################## -def parseBinsFile(binsFile,varsL,trace): - global pathLength - global rateSampling - global minDelayVal - global minRateVal - global minDivisionVal - global decPercent - global minVarValL - global maxVarValL - global limitExists - - minVarValL = [] - maxVarValL = [] - limitExists = False - - for i in range(len(varsL)): - minVarValL.append(None) - maxVarValL.append(None) - - if not os.path.isfile(binsFile): - if trace: - cStr = cText.cSetFg(cText.GREEN) - cStr += "NOTE:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" a .bins file was not read." - return - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" the .bins file, "+binsFile+" was not found." - sys.exit() - inputF = open(binsFile, 'r') - linesL = inputF.readlines() - tParam = ThresholdParameters(len(varsL)) - divisionsStrL = [] - numDivisions = 0 - for i in range(1,len(varsL)): - divisionsStrL.append([]) - for i in range(len(linesL)): - #Allow blank lines and comments - if lineSpaceR.match(linesL[i]) or binCommentR.match(linesL[i]): - continue - if lDotR.match(linesL[i]): - if epsilonR.match(linesL[i]): - epsilonL = spaceR.split(linesL[i]) - tParam.epsilon = abs(float(epsilonL[1])) - elif lengthR.match(linesL[i]): - lengthL = spaceR.split(linesL[i]) - tParam.length = float(lengthL[1]) - elif timeR.match(linesL[i]): - timeL = spaceR.split(linesL[i]) - tParam.time = float(timeL[1]) - elif vaRateUpdateIntervalR.match(linesL[i]): - vaRateUpdateIntervalL = vaRateUpdateIntervalR.split(linesL[i]) - tParam.vaRateUpdateInterval = float(vaRateUpdateIntervalL[1]) - elif absoluteTimeR.match(linesL[i]): - absoluteTimeL = spaceR.split(linesL[i]) - if trueR.match(absoluteTimeL[1]): - tParam.absoluteTime = True - elif falseR.match(absoluteTimeL[1]): - tParam.absoluteTime = False - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Attemptd to set .absoluteTime with"+absoluteTimeL[i]+" which is unrecognized. It was not set. Please use True or False." - sys.exit() - elif percentR.match(linesL[i]): - percentL = spaceR.split(linesL[i]) - tParam.percent = float(percentL[1]) - elif inputR.match(linesL[i]): - cLine = cleanLine(linesL[i]) - inputL = spaceR.split(cLine) - for i in range(1,len(inputL)): - found = False - for j in range(1,len(varsL)): - if inputL[i] == varsL[j].name: - #print varsL[j].name+" is an input." - varsL[j].input = True - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+inputL[i]+" was specified as an input in the .bins file, but wasn't found in the variable list." - sys.exit() - elif outputR.match(linesL[i]): - cLine = cleanLine(linesL[i]) - outputL = spaceR.split(cLine) - for i in range(1,len(outputL)): - found = False - for j in range(1,len(varsL)): - if outputL[i] == varsL[j].name: - #print varsL[j].name+" is an output." - varsL[j].output = True - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+outputL[i]+" was specified as an output in the .bins file, but wasn't found in the variable list." - sys.exit() - elif dmvcR.match(linesL[i]): - cLine = cleanLine(linesL[i]) - dmvcL = spaceR.split(cLine) - for i in range(1,len(dmvcL)): - found = False - for j in range(1,len(varsL)): - if dmvcL[i] == varsL[j].name: - #print varsL[j].name+" is dmvc." - varsL[j].dmvc = True - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+outputL[i]+" was specified as dmvc in the .bins file, but wasn't found in the variable list." - sys.exit() - elif rateSamplingR.match(linesL[i]): - rateSamplingL = spaceR.split(linesL[i]) - rateSampling = int(rateSamplingL[1]) - elif pathLengthR.match(linesL[i]): - pathLengthL = spaceR.split(linesL[i]) - pathLength = int(pathLengthL[1]) - elif minDelayValR.match(linesL[i]): - minDelayValL = spaceR.split(linesL[i]) - minDelayVal = int(minDelayValL[1]) - elif minRateValR.match(linesL[i]): - minRateValL = spaceR.split(linesL[i]) - minRateVal = int(minDelayValL[1]) - elif minDivisionValR.match(linesL[i]): - minDivisionValL = spaceR.split(linesL[i]) - minDivisionVal = int(minDelayValL[1]) - elif decPercentR.match(linesL[i]): - decPercentL = spaceR.split(linesL[i]) - decPercent = int(decPercentL[1]) - elif minVarValR.match(linesL[i]): - limitExists = True - cLine = cleanLine(linesL[i]) - inputL = spaceR.split(cLine) - found = False - for i in range(1,len(varsL)): - if inputL[2] == varsL[i].name: - minVarValL[i] = inputL[1] - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+inputL[2]+" was specified in a target for .minVarVal in the .bins file, but wasn't found in the variable list." - sys.exit() - elif maxVarValR.match(linesL[i]): - limitExists = True - cLine = cleanLine(linesL[i]) - inputL = spaceR.split(cLine) - found = False - for i in range(1,len(varsL)): - if inputL[2] == varsL[i].name: - maxVarValL[i] = inputL[1] - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" "+inputL[2]+" was specified in a target for .maxVarVal in the .bins file, but wasn't found in the variable list." - sys.exit() - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Unparseable dot option in the thresholds file: "+linesL[i] - sys.exit() - else: - numDivisions += 1 - cLineL = cleanLine(linesL[i]).split(" ") - found = False - for j in range(1,len(varsL)): - if cLineL[0] == varsL[j].name: - divisionsStrL[j-1] = cLineL[1:] - found = True - break - if not found: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Unparseable line in the thresholds file." - print "Line: "+linesL[i] - sys.exit() - divisionsL = [[]] - for sL in divisionsStrL: - fL = [] - for s in sL: - fL.append(float(s)) - divisionsL.append(fL) - inputF.close() - if numDivisions != len(varsL)-1 and not trace: - cStr = cText.cSetFg(cText.RED) - cStr += "Warning:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" There is not a threshold for every variable in the dat file." - #sys.exit() - print "divisionsL:"+str(divisionsL) - return divisionsL, tParam - -############################################################################## -# Parse the .prop (property) file. -############################################################################## -def parsePropFile(propFile): - inputF = open(propFile, 'r') - rawProperty = inputF.readline() - return cleanLine(rawProperty) - -############################################################################## -# Generate the bin encoding for each data point given the divisions. -############################################################################## -def genBins(varsL,datL,divisionsL): - binsL = create2Darray(len(datL),len(varsL),-1) - #print "divisionsL:"+str(divisionsL) - for i in range(len(datL)): - #loop through each variable except time - for j in range(1,len(varsL)): - for k in range(len(divisionsL[j])): - #print "Compare: "+str(datL[i][j])+"<="+str(divisionsL[j][k]) - if (datL[i][j] <= divisionsL[j][k]): - #print "binsL["+str(i)+"]["+str(j)+"] = "+str(k) - binsL[i][j] = k - #print str(binsL[i][j]), - break - else: - #handles the case when the datum is in the highest bin - #i.e. for 2 boundary numbers 3 bins are required - #print "binsL["+str(i)+"]["+str(j)+"] = "+str(k+1) - binsL[i][j] = k+1 - #print str(binsL[i][j]), - #print "\n" - return binsL - -############################################################################## -# Generate the rates for each data point given the bin encodings. -############################################################################## -def genRates(varsL,datL,binsL,rateSampling): - #Function notes: Rates can be calculated based on transitions or places. If rates are calculated based on places they are calculated based on the change in the bin for the entire line. If rates are calculated based on transitions they are calculated based on the change in the bin for each variable. These two methods have different results and it appears that place based rates are more stable. To help "smooth" out the rates there are several ways to modify the rate calcualation. One way is to change the rateSampling variable. This variable determines how long the bin must remain constant before a rate is calculated for that bin. It can be a numerical value or "inf." The "inf" setting only calculates the rate once per bin change. You can invalidate bin changes of short length using the pathLength variable. Any run of consecutive bins shorter than pathLength will not have its rate calculated. The rate is also only calculated if the time values differ for the two points as I have seen examples where this is a problem. - ratesL = create2Darray(len(datL),len(varsL),'-') - if placeRates: - #Place based rate calculation - if rateSampling == "inf": - #inf means that the window size is equal to the bin size - mark = 0 - for i in range(len(datL)): - if i < mark: - continue - while mark < len(datL) and binsL[i] == binsL[mark]: - mark += 1 - if datL[mark-1][0] != datL[i][0] and (mark-i) >= pathLength: - for j in range(1,len(varsL)): - ratesL[i][j] = (datL[mark-1][j]-datL[i][j])/(datL[mark-1][0]-datL[i][0]) - #print str(binsL[mark]) - #print "\n" - else: - #Calculate the rates for each data point until the window would move the second point outside the bin - for i in range(len(datL)-rateSampling): - calcRate = True - #Check to make sure the bin persists for at least one window size - for k in range(rateSampling): - if binsL[i] != binsL[i+k]: - calcRate = False - break - if calcRate and datL[i+rateSampling][0] != datL[i][0]: - for j in range(1,len(varsL)): - ratesL[i][j] = (datL[i+rateSampling][j]-datL[i][j])/(datL[i+rateSampling][0]-datL[i][0]) - #Transition-based rates - else: - #Transition based rate calculation - if rateSampling == "inf": - for j in range(1,len(varsL)): - mark = 0 - for i in range(len(datL)): - if i < mark: - continue - while mark < len(datL) and binsL[i][j] == binsL[mark][j]: - mark = mark + 1 - if datL[mark-1][0] != datL[i][0]: - ratesL[i][j] = (datL[mark-1][j]-datL[i][j])/(datL[mark-1][0]-datL[i][0]) - else: - for i in range(len(datL)-rateSampling): - for j in range(1,len(varsL)): - calcRate = True - for k in range(rateSampling): - if binsL[i][j] != binsL[i+k][j]: - calcRate = False - break - if calcRate and datL[i+rateSampling][0] != datL[i][0]: - ratesL[i][j] = (datL[i+rateSampling][j]-datL[i][j])/(datL[i+rateSampling][0]-datL[i][0]) - return ratesL - -############################################################################## -# Explore a potential DVMC run. If the run is valid (currently this -# means long enough) then return the run. Else return None. -############################################################################## -def exploreRun(datL,i,j,tParam): - run = DMVCpart() - run.startPoint = i - run.varInd = j - run.valueL.append(datL[i][j]) - while i+1 < len(datL) and tParam.epsilonEquiv(datL[run.startPoint][j],datL[i+1][j]): - run.valueL.append(datL[i+1][j]) - i = i+1 - #print "i:"+str(i)+" j:"+str(j) - run.endPoint = i - if not tParam.absoluteTime: - if ((run.endPoint-run.startPoint)+1) < tParam.length: - #print "Run is too short from "+str(run.startPoint)+" to "+str(run.endPoint)+" ["+str((run.endPoint-run.startPoint)+1)+"]" - return None, i - else: - #print "Found a run from "+str(run.startPoint)+" to "+str(run.endPoint)+"["+str((run.endPoint-run.startPoint)+1)+"]" - return run, i - else: - if run.calcDelay(datL) < tParam.time: - #print "Run is too short from "+str(run.startPoint)+" to "+str(run.endPoint)+" ["+str(run.calcDelay(datL))+"]" - return None, i - else: - #print "Found a run from "+str(run.startPoint)+" to "+str(run.endPoint)+" ["+str(run.calcDelay(datL))+"]" - return run, i - -############################################################################## -# Determine which variables should be considered multi-valued -# continuous variables. Marks varsL[i].dmvc for DMVC variables and -# returns a list of valid DMVC runs varsL long. Empty lists exist for -# non-DMVC places and lists of valid runs are present for DMVC -# variables. -############################################################################## -def findDMVC(datL,varsL,tParam): - tempRun = None - prevRun = None - runL = [] - for j in range(len(varsL)): - runL.append([]) - if varsL[j].dmvc != False: - #print "Examining variable["+str(j)+"]: "+varsL[j].name - mark = 0 - for i in range(len(datL)-1): - if i < mark: - continue - if tParam.epsilonEquiv(datL[i][j],datL[i+1][j]): - #print "Exploring from:"+str(i) - tempRun,mark = exploreRun(datL,i,j,tParam) - #print "Returning at:"+str(mark) - if tempRun != None: - if len(runL[j]) > 1: - prevRun.nextRun = tempRun - prevRun = tempRun - runL[j].append(tempRun) - #determine if a high enough percentage of the run is constant - if not tParam.absoluteTime: - numPoints = 0 - for run in runL[j]: - #print "run:"+str(run) - #print "runDelay:"+str(run.calcDelay(datL)) - numPoints += (run.endPoint-run.startPoint) + 1 - if (numPoints/float(len(datL))) < tParam.percent: - #print "Clearing runs for "+varsL[j].name+" ["+str(numPoints/float(len(datL)))+"]"+str(numPoints)+"/"+str(len(datL)) - runL[j] = [] #clear the runs if they don't meet the percentage requirement - else: - #print varsL[j].name+" is a DMVC. ["+str(numPoints/float(len(datL)))+"]" - varsL[j].dmvc = True - else: - absTime = 0.0 - for run in runL[j]: - #print "run:"+str(run) - #print "runDelay:"+str(run.calcDelay(datL)) - absTime += run.calcDelay(datL) - if (absTime/(datL[len(datL)-1][0]-datL[0][0])) < tParam.percent: - #print "Clearing runs for "+varsL[j].name+" ["+str(absTime/(datL[len(datL)-1][0]-datL[0][0]))+"]"+str(absTime)+"/"+str(datL[len(datL)-1][0]-datL[0][0]) - runL[j] = [] - else: - #print varsL[j].name+" is a DMVC. ["+str(absTime/(datL[len(datL)-1][0]-datL[0][0]))+"]" - varsL[j].dmvc = True - #return runL for processing during the graph building - return runL - -############################################################################## -# Update the rate information in the graph. -############################################################################## -def updateRateInfo(g,varsL,datL,binsL,ratesL,cvg): - prevTranKey = "" #previous place key used for generating transitions - place = None #current place - prevPlace = None #previous place (required to build outgoingL) - transition = None - newRate = False - ratePlaceL = [] - for i in range(len(datL)-1): - #only generate graph nodes for places for data which have calculated rates - if ratesL[i][1] != "-": - key = "" - for j in range(1,len(varsL)): - key += str(binsL[i][j]) - prevPlace = place - #Find or create the place for the given key - if g.placeD.has_key(key): - place = g.placeD.get(key) - else: - place = Place(varsL,RATE) - ratePlaceL.append(place) - cvg.placesL[cvg.index] += 1 - binEncodingL = [] - for j in range(1,len(varsL)): - binEncodingL.append(binsL[i][j]) - place.binEncodingL = binEncodingL - g.placeD[key] = place - #Add the rate for the time point to the place's rate list - for j in range(1,len(varsL)): - if varsL[j].dmvc: - #Don't add rates for DMVC vars - continue - oldR = (place.minRate(j),place.maxRate(j)) - place.ratesL[j].append(ratesL[i][j]) - newR = (place.minRate(j),place.maxRate(j)) - #print "New rate?"+str(oldR)+":"+str(newR) - newRate = False - newRate = cvg.isNewRate(oldR,newR) - #if(newRate): - #print "New rate:p"+str(place.binEncodingL)+":"+str(newR)+"--"+str(oldR) - #If the bin encoding for the place has changed then find & update or add the corresponding transition - if prevTranKey != key: - if g.transitionD.has_key((prevTranKey,key)): - transition = g.transitionD.get((prevTranKey,key)) - elif prevPlace: - #print "Creating new transition (p"+str(prevPlace.placeNum)+"->p"+str(place.placeNum)+") for i value:"+str(i)+" key:"+key - transition = Transition(len(varsL),place,prevPlace) - transition.core = True - cvg.transitionsL[cvg.index] += 1 - g.transitionD[(prevTranKey,key)] = transition - if transition: - if prevPlace: - if transition not in prevPlace.outgoingL: - prevPlace.outgoingL.append(transition) - if transition not in place.incomingL: - place.incomingL.append(transition) - prevTranKey = key - #Add the rate for the time point to the transition's rate list - if transition: - for j in range(1,len(varsL)): - if varsL[j].dmvc: - #Don't add rates for DMVC vars - continue - transition.ratesL[j].append(ratesL[i][j]) - -############################################################################## -# Update the time information in the graph. -############################################################################## -def updateTimeInfo(g,varsL,datL,dmvcRunL,tParam,cvg,divisionsL): - prevTranKey = "" #previous place key used for generating transitions - place = None #current place - prevPlace = None #previous place (required to build outgoingL) - transition = None - for i in range(len(varsL)): - #Only produce time based loops for DMVC input variables - if varsL[i].dmvc and varsL[i].input: - #print "Working on DMVC variable: "+varsL[i].name - dmvcCnt = 0 - dmvcPlaceL = [] - prevPlace = None - place = None - transition = None - for runPart in dmvcRunL[i]: - #print "runPart:"+str(runPart)+" Time:"+str(datL[runPart.endPoint][0]-datL[runPart.startPoint][0]) - exists = False - #check to see if a place already exists for that value - placeL = g.placeD.values() - if len(placeL) > 1: - for p in placeL: - #print "p:"+str(p) - if p.isDmvcP(): - if tParam.epsilonEquiv(runPart.constVal(),p.dmvcVal) and p.dmvcVar == runPart.varInd: - oldD = (p.minTime(),p.maxTime()) - p.dmvcTimeL[i].append(runPart.calcDelay(datL)) - newD = (p.minTime(),p.maxTime()) - #print "New delay1?"+str(oldD)+":"+str(newD) - cvg.isNewDelay(oldD,newD) - exists = True - prevPlace = place - place = p - #if the place doesn't already exist create it - if not exists: - #print "The place doesn't exist...creating it for constVal:"+str(runPart.constVal()) - prevPlace = place - place = Place(varsL,DMVC) - cvg.placesL[cvg.index] += 1 - place.binEncodingL = ["d_",str(i),"_"+str(dmvcCnt)] - dmvcCnt = dmvcCnt + 1 - place.dmvcVar = i - oldD = (place.minTime(),place.maxTime()) - place.dmvcTimeL[i].append(runPart.calcDelay(datL)) - newD = (place.minTime(),place.maxTime()) - #print "New delay2?"+str(oldD)+":"+str(newD) - cvg.isNewDelay(oldD,newD) - place.dmvcVal = runPart.constVal() - dmvcPlaceL.append(place) - g.placeD[place.keyStr()] = place -# else: - #print "The place exists...updating it for constVal:"+str(runPart.constVal()) - #update the place with additional timing values -# oldD = (place.minTime(),place.maxTime()) -# place.dmvcTimeL[i].append(runPart.calcDelay(datL)) -# newD = (place.minTime(),place.maxTime()) -# #print "New delay3?"+str(oldD)+":"+str(newD) -# cvg.isNewDelay(oldD,newD) - #Create new transitions as needed and connect them to the appropriate places - if prevPlace: - if g.transitionD.has_key((prevPlace.keyStr(),place.keyStr())): - transition = g.transitionD.get((prevPlace.keyStr(),place.keyStr())) - else: - transition = Transition(len(varsL),place,prevPlace) - transition.core = True - cvg.transitionsL[cvg.index] += 1 - g.transitionD[(prevPlace.keyStr(),place.keyStr())] = transition - if transition not in prevPlace.outgoingL: - prevPlace.outgoingL.append(transition) - if transition not in place.incomingL: - place.incomingL.append(transition) - for p in dmvcPlaceL: - if len(p.dmvcTimeL[p.dmvcVar]) > 1: - #print "Checking remove for p"+str(p.placeNum)+"["+varsL[p.dmvcVar].name+"]" - #print "List: "+str(p.dmvcTimeL[p.dmvcVar]) - #print "Checking first item: "+str(p.dmvcTimeL[p.dmvcVar][0]) - if p.dmvcTimeL[p.dmvcVar][0] > p.avgTime(): - #print "max()1:"+str(max(p.dmvcTimeL[p.dmvcVar][1:])) - #print "percent:"+str(max(p.dmvcTimeL[p.dmvcVar][1:])*decPercent)+" difference:"+str(p.dmvcTimeL[p.dmvcVar][0]-max(p.dmvcTimeL[p.dmvcVar][1:])) - if p.dmvcTimeL[p.dmvcVar][0] > max(p.dmvcTimeL[p.dmvcVar][1:]): - if p.dmvcTimeL[p.dmvcVar][0]-max(p.dmvcTimeL[p.dmvcVar][1:]) > decPercent*max(p.dmvcTimeL[p.dmvcVar][1:]): - #print "Removing:"+str(p.dmvcTimeL[p.dmvcVar][0]) - p.dmvcTimeL[p.dmvcVar].pop(0) - else: - #print "min()2:"+str(min(p.dmvcTimeL[p.dmvcVar][1:])) - #print "percent:"+str(min(p.dmvcTimeL[p.dmvcVar][1:])*decPercent)+" difference:"+str(min(p.dmvcTimeL[p.dmvcVar][1:])-p.dmvcTimeL[p.dmvcVar][0]) - if p.dmvcTimeL[p.dmvcVar][0] < min(p.dmvcTimeL[p.dmvcVar][1:]): - if (min(p.dmvcTimeL[p.dmvcVar][1:])-p.dmvcTimeL[p.dmvcVar][0]) > decPercent*min(p.dmvcTimeL[p.dmvcVar][1:]): - #print "Removing:"+str(p.dmvcTimeL[p.dmvcVar][0]) - p.dmvcTimeL[p.dmvcVar].pop(0) - - #print "Checking last item: "+str(p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL[p.dmvcVar])-1]) - if p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL[p.dmvcVar])-1] > p.avgTime(): - #print "max()3:"+str(max(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])) - #print "percent:"+str(max(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])*decPercent)+" difference:"+str(p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL)-1]-max(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])) - if p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL[p.dmvcVar])-1] > max(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1]): - if (p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL)-1]-max(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])) > max(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])*decPercent: - #print "Removing:"+str(p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL[p.dmvcVar])-1]) - p.dmvcTimeL[p.dmvcVar].pop(len(p.dmvcTimeL[p.dmvcVar])-1) - else: - #print "min()4:"+str(min(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])) - #print "percent:"+str(min(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])*decPercent)+" difference:"+str(min(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])-p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL[p.dmvcVar])-1]) - if p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL[p.dmvcVar])-1] < min(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1]): - if (min(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])-p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL[p.dmvcVar])-1]) > min(p.dmvcTimeL[p.dmvcVar][:len(p.dmvcTimeL)-1])*decPercent: - #print "Removing:"+str(p.dmvcTimeL[p.dmvcVar][len(p.dmvcTimeL[p.dmvcVar])-1]) - p.dmvcTimeL[p.dmvcVar].pop(len(p.dmvcTimeL[p.dmvcVar])-1) - #print "List: "+str(p.dmvcTimeL[p.dmvcVar]) - elif varsL[i].dmvc: - #process non-input DMVC variables - if g.placeD.has_key("d_"+str(i)+"_0"): - place = g.placeD.get("d_"+str(i)+"_0") - else: - place = Place(varsL,DMVC) - cvg.placesL[cvg.index] += 1 - place.binEncodingL = ["d_",str(i),"_0"] - place.dmvcVar = i - place.dmvcVal = dmvcRunL[i][0].constVal() - g.placeD[place.keyStr()] = place - for j in range(len(divisionsL[i])+1): - place.dmvcInfoL.append(AsgnPart()) - place.dmvcInfoL[j].var = i - for runPart in dmvcRunL[i]: - foundBin = False - for j in range(len(divisionsL[i])): - if runPart.constVal() <= divisionsL[i][j]: - #add a delay of 0 for now - #print "updateTIME1 "+varsL[i].name+"["+place.keyStr()+"]"+" p"+str(place.placeNum)+".dmvcInfoL["+str(j)+"]:"+str(runPart.constVal()) - place.dmvcInfoL[j].valL.append(runPart.constVal()) - place.dmvcInfoL[j].delayL.append(0.0) - foundBin = True - break - if not foundBin: - #print "updateTIME2 "+varsL[i].name+"["+place.keyStr()+"]"+" p"+str(place.placeNum)+".dmvcInfoL["+str(len(divisionsL[i]))+"]:"+str(runPart.constVal()) - place.dmvcInfoL[len(divisionsL[i])].valL.append(runPart.constVal()) - place.dmvcInfoL[len(divisionsL[i])].delayL.append(0.0) - -############################################################################## -# Generate the graph based on the time series data and bin encodings. -############################################################################## -def updateGraph(g,varsL,datL,binsL,ratesL,dmvcRunL,tParam,failProp,cvg,divisionsL): - prevTranKey = "" #previous place key used for generating transitions - place = None #current place - prevPlace = None #previous place (required to build outgoingL) - transition = None - #Generate rate based places & transitions - updateRateInfo(g,varsL,datL,binsL,ratesL,cvg) - #Generate time based places and transitions - updateTimeInfo(g,varsL,datL,dmvcRunL,tParam,cvg,divisionsL) - #Assign the proper initial conditions and markings - initMark = 0 - for i in range(1,len(varsL)): - if not (varsL[i].dmvc and varsL[i].input): - #rate based and noninput DMVC places - for j in range(len(datL)): - if ratesL[j][i] != "-": - g.initValL[i].append(datL[j][i]) - g.initRateL[i].append(ratesL[j][i]) - initMark = j - break - key = "" - for k in range(1,len(varsL)): - key = key + str(binsL[initMark][k]) - if not (g.placeD.get(key) in g.initMarkingL): - g.initMarkingL.append(g.placeD.get(key)) - else: - #dmvc input places - place = g.placeD.get("d_"+str(i)+"_0") - if place: - g.initValL[i].append(place.dmvcVal) - g.initRateL[i].append("-") - if not(place in g.initMarkingL) and varsL[i].input: - #print "Adding DMVC place p"+str(place.placeNum)+" to initMarking." - g.initMarkingL.append(place) - else: - cStr = cText.cSetFg(cText.RED) - cStr += "ERROR:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" No initial place was found for "+varsL[i].name+"." - sys.exit() - -############################################################################## -# Add places and transitions to the graph to do non-input DMVC variable -# assignments. -############################################################################## -def expandGraph(g,varsL): - #print "Initial graph:"+str(g) - exG = copy.deepcopy(g) - #return exG - transitionL = exG.transitionD.values() - transitionL.sort() - nonInDmvcL = [] - for i in range(len(varsL)): - if varsL[i].dmvc and not varsL[i].input: - nonInDmvcL.append(i) - for t in transitionL: - if t.outgoingP and t.incomingP and t.incomingP.isRateP() and t.outgoingP.isRateP(): - #print "Working on t"+str(t.transitionNum) - diffL = t.outgoingP.diff(t.incomingP,varsL) - prevPlaceNum = t.incomingP.placeNum - curPlaceNum = t.outgoingP.placeNum - for var in diffL: - if var in nonInDmvcL: - srcP = exG.placeD.get("d_"+str(var)+"_0") - #print "Creating/modifying ex_"+str(prevPlaceNum)+"_"+str(curPlaceNum) - if exG.placeD.has_key("ex_"+str(prevPlaceNum)+"_"+str(curPlaceNum)): - pEx = exG.placeD.get("ex_"+str(prevPlaceNum)+"_"+str(curPlaceNum)) - else: - #Create a new place that is spliced into the graph by - #point t to the new place and pointing the new transition - #to curPlace - pEx = Place(varsL,ASGN) - pEx.binEncodingL = ["ex_",str(prevPlaceNum),"_",str(curPlaceNum)] - pEx.asgBinL = t.incomingP.binEncodingL - pEx.incomingL.append(t) - tEx = Transition(len(varsL),t.outgoingP,pEx) - tEx.core = True - pEx.outgoingL.append(tEx) - preExP = g.placeD.get(t.incomingP.keyStr()) - pEx.preExGrandParentBinL = preExP.incomingL[0].incomingP - t.outgoingP.incomingL.remove(t) - t.outgoingP.incomingL.append(tEx) - t.outgoingP = pEx - exG.placeD[pEx.keyStr()] = pEx - exG.transitionD[(t.outgoingP.keyStr(),pEx.keyStr())] = tEx - nextT = pEx.outgoingL[0] - #Use a deep copy here b/c the future of these lists requires - #them to actually be separate objects that can be modified - #independently - pEx.asg.append(copy.deepcopy(srcP.dmvcInfoL[nextT.outgoingP.binEncodingL[var-1]])) - return exG - -############################################################################## -# Replace transitions with multiple bin changes with new transitions that are # only single bin changes. -############################################################################## -def rmMultipleBinChange(g,varsL): - sbcG = copy.deepcopy(g) - transitionL = sbcG.transitionD.values() - transitionL.sort() - for t in transitionL: - if t.outgoingP and t.incomingP: - diffL = t.outgoingP.diff(t.incomingP,varsL) - if len(diffL) > 1: - print "t" + str(t.transitionNum) + " has multiple bin changes...(p" + str(t.incomingP.placeNum) + ",p" + str(t.outgoingP.placeNum) + ")" - - return sbcG - -############################################################################## -# Return the minimum division value. -############################################################################## -def minDiv(divisionsL): - minDivision = None - for i in range(1,len(divisionsL)): - for j in range(len(divisionsL[i])): - if not minDivision: - minDivision = divisionsL[i][j] - elif divisionsL[i][j] < minDivision: - minDivision = divisionsL[i][j] - return minDivision - -############################################################################## -# Return the maximum division value. -############################################################################## -def maxDiv(divisionsL): - maxDivision = None - for i in range(1,len(divisionsL)): - for j in range(len(divisionsL[i])): - if not maxDivision: - maxDivision = divisionsL[i][j] - elif divisionsL[i][j] > maxDivision: - maxDivision = divisionsL[i][j] - return maxDivision - -############################################################################## -# Adjust the values of rates, delay values, and thresholds so that both can -# be represnted as integers of sufficient accuracy in the LHPN model. -############################################################################## -def normalizeValues(g,varsL,divisionsL): - normG = copy.deepcopy(g) - normDivisionsL = copy.deepcopy(divisionsL) - placeL = normG.placeD.values() - placeL.sort() - transitionL = normG.transitionD.values() - transitionL.sort() - - minDelay = g.minDelay() - maxDelay = g.maxDelay() - print "minDelay:"+str(minDelay)+" maxDelay:"+str(maxDelay) - scaleFactor = 1.0 - #Determine the scaling factor (using factors of 10) to bring the - #smallest delay value above minDelayVal - if minDelay: - for i in range(18): - if scaleFactor > (minDelayVal/minDelay): - break - scaleFactor *= 10.0 - print "minDelay is: "+str(minDelay*scaleFactor)+" after scaling by "+str(scaleFactor) - #check for overflow then scale the appropriate graph values - if maxDelay and int(maxDelay*scaleFactor) > sys.maxint: - cStr = cText.cSetFg(cText.YELLOW) - cStr += "WARNING:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Delay scaling has caused an overflow." - normG.delayScaleFactor = scaleFactor - normG.scaleDelay(scaleFactor,False) - - minRate = normG.minRate() - maxRate = normG.maxRate() - print "minRate:"+str(minRate)+" maxRate:"+str(maxRate) - scaleFactor = 1.0 - #Determine the scaling factor (using factors of 10) to bring the - #smallest rate value above minRateVal - if minRate: - for i in range(14): - if scaleFactor > abs(minRateVal/minRate): - break - scaleFactor *= 10.0 - for i in range(14): - #TODO: Make this number configurable? - if abs(maxRate*scaleFactor) < (sys.maxint/1000): - break - scaleFactor /= 10.0 - print "minRate is: "+str(minRate*scaleFactor)+" after scaling by "+str(scaleFactor) - else: - print "No minimum rate." - #check for overflow then scale the appropriate graph values - if maxRate and int(maxRate*scaleFactor) > sys.maxint: - cStr = cText.cSetFg(cText.YELLOW) - cStr += "WARNING:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Rate scaling has caused an overflow." - normG.varScaleFactor = scaleFactor - normG.scaleVariable(scaleFactor,normDivisionsL) - - #Divisions need to be scaled so that they are larger than - #minDivisionVal. Yes, it is possible this is accomplished w/ rate - #scaling unless there aren't any rates based variables in the - #system. In that case this code should move the divisions - #appropriately. - minDivision = minDiv(normDivisionsL) - maxDivision = maxDiv(normDivisionsL) - print "minDivision:"+str(minDivision)+" maxDivision:"+str(maxDivision) - scaleFactor = 1.0 - print "minDivision: "+str(minDivision) - if minDivision and minDivision != 0: - for i in range(14): - if abs(scaleFactor * minDivision) > minDivisionVal: - break - scaleFactor *= 10 - print "minDivision is: "+str(minDivision*scaleFactor)+" after scaling by "+str(scaleFactor) - #check for overflow then scale the appropriate graph values - if int(maxDivision*scaleFactor) > sys.maxint: - cStr = cText.cSetFg(cText.YELLOW) - cStr += "WARNING:" - cStr += cText.cSetAttr(cText.NONE) - print cStr+" Division scaling has caused an overflow." - normG.varScaleFactor *= scaleFactor - normG.scaleVariable(scaleFactor,normDivisionsL) - #normG.scaleDelay(1e15,False) - return normG, normDivisionsL - -############################################################################## -# Write out a *.sort file containing the derived bin encodings. -############################################################################## -def writeSortFile(varsL,numPoints,datL,binsL,sortFile): - outputF = open(sortFile, 'w') - outputF.write("Variables: "+str(len(varsL))+"\n") - outputF.write("Points: "+str(numPoints)+"\n") - for i in range(len(varsL)): - outputF.write(varsL[i].name+" ") - outputF.write("\n") - for i in range(len(datL)): - for j in range(len(datL[i])): - outputF.write(str(datL[i][j])+" ") - for j in range(1,len(binsL[i])): - outputF.write(str(binsL[i][j])+" ") - outputF.write("\n") - outputF.close() - -############################################################################## -# Write out a *.rate file containing the derived bin encodings and calculated -# rates. -############################################################################## -def writeRateFile(varsL,numPoints,datL,binsL,ratesL,rateFile): - outputF = open(rateFile, 'w') - outputF.write("Variables: "+str(len(varsL))+"\n") - outputF.write("Points: "+str(numPoints)+"\n") - for i in range(len(varsL)): - outputF.write(varsL[i].name+" ") - outputF.write("\n") - for i in range(len(datL)): - for j in range(len(datL[i])): - outputF.write(str(datL[i][j])+" ") - for j in range(1,len(binsL[i])): - outputF.write(str(binsL[i][j])+" ") - for j in range (1,len(binsL[i])): - outputF.write(str(ratesL[i][j])+" ") - outputF.write("\n") - outputF.close() - -############################################################################## -# Write out an informational header for the output file given a string for -# comments (cStr). -############################################################################## -def writeInformationalHeader(outputF,cStr,tParam,g): - outputF.write(cStr+"\n") - outputF.write(cStr+"Delay scale factor: "+str(g.delayScaleFactor)+"\n") - outputF.write(cStr+"Variable scale factor: "+str(g.varScaleFactor)+"\n") - outputF.write(cStr+"\n") - -############################################################################## -# Add places and transitions to support the meta-bins for limited variables. -############################################################################## -def addMetaBins(g,varsL,divisionsL): - print "addMetaBins:begin" - placeL = g.placeD.values() - placeL.sort() - for p in placeL: - if p.isRateP(): - print "Working on:" + str(p) - for i in range(len(p.binEncodingL)): - if int(p.minRateInt(i+1)) < 0: - syncBinEncodingL = copy.deepcopy(p.binEncodingL) - foundBin = False - while not foundBin: - syncBinEncodingL[i] = syncBinEncodingL[i] - 1 - if syncBinEncodingL[i] == -1 and not g.placeD.has_key(binEncoding2Str(syncBinEncodingL)): - #Create a new meta-bin for the place - foundBin = True - metaP = Place(varsL,RATE) - placeL.append(metaP) #new - metaP.ratesL = copy.deepcopy(p.ratesL) - metaP.rmRateLtZero(i+1) - metaP.binEncodingL = syncBinEncodingL - g.placeD[metaP.keyStr()] = metaP - t = Transition(len(varsL),metaP,p) - g.transitionD[(p.keyStr(),metaP.keyStr())] = t - p.outgoingL.append(t) - metaP.incomingL.append(t) - elif g.placeD.has_key(binEncoding2Str(syncBinEncodingL)): - foundBin = True - syncP = g.placeD.get(binEncoding2Str(syncBinEncodingL)) - if not g.transitionD.has_key((p.keyStr(),syncP.keyStr())): - t = Transition(len(varsL),syncP,p) - g.transitionD[(p.keyStr(),syncP.keyStr())] = t - p.outgoingL.append(t) - syncP.incomingL.append(t) - if int(p.maxRateInt(i+1)) > 0: - syncBinEncodingL = copy.deepcopy(p.binEncodingL) - foundBin = False - while not foundBin: - syncBinEncodingL[i] = syncBinEncodingL[i] + 1 - if syncBinEncodingL[i] == len(divisionsL[i+1])+1 and not g.placeD.has_key(binEncoding2Str(syncBinEncodingL)): - #Create a new meta-bin for the place - foundBin = True - metaP = Place(varsL,RATE) - placeL.append(metaP) #new - metaP.ratesL = copy.deepcopy(p.ratesL) - metaP.rmRateGtZero(i+1) - metaP.binEncodingL = syncBinEncodingL - g.placeD[metaP.keyStr()] = metaP - t = Transition(len(varsL),metaP,p) - g.transitionD[(p.keyStr(),metaP.keyStr())] = t - p.outgoingL.append(t) - metaP.incomingL.append(t) - elif g.placeD.has_key(binEncoding2Str(syncBinEncodingL)): - foundBin = True - syncP = g.placeD.get(binEncoding2Str(syncBinEncodingL)) - if not g.transitionD.has_key((p.keyStr(),syncP.keyStr())): - t = Transition(len(varsL),syncP,p) - g.transitionD[(p.keyStr(),syncP.keyStr())] = t - p.outgoingL.append(t) - syncP.incomingL.append(t) - print "addMetaBins:end" - -############################################################################## -# Convert a binEncodingL to a string. -############################################################################## -def binEncoding2Str(binEncodingL): - keyStr = "" - for i in range(len(binEncodingL)): - keyStr += str(binEncodingL[i]) - return keyStr - -############################################################################## -# -############################################################################## -def writePSfile(g,varsL,psFile): - outputF = open(psFile, 'w') - #writePSfile(outputF) - placeL = g.placeD.values() - placeL.sort() - for p in placeL: - for i in range(len(p.binEncodingL)): - outputF.write(str(p.binEncodingL[i]+1)+" ") - outputF.write("(p" + str(p.placeNum)+ ") labelbox\n") - outputF.write("\n") - transitionL = g.transitionD.values() - transitionL.sort() - for t in transitionL: - pSrc = t.incomingP - pSync = t.outgoingP - diffL = pSrc.diff(pSync,varsL) - if diffL[0] == 1 and len(diffL) == 1: - #outputF.write("t"+str(t.transitionNum)+" ") - outputF.write(str(pSrc.binEncodingL[1]+1) + " " + str(pSrc.binEncodingL[0]+1) + " " + str(pSync.binEncodingL[0]+1) + " ") - if pSync.binEncodingL[0] > pSrc.binEncodingL[0]: - if t.core: - outputF.write("drawArrowRight\n") - else: - outputF.write("drawDashArrowRight\n") - else: - if t.core: - outputF.write("drawArrowLeft\n") - else: - outputF.write("drawDashArrowLeft\n") - elif diffL[0] == 2 and len(diffL) == 1: - outputF.write(str(pSrc.binEncodingL[0]+1) + " " + str(pSrc.binEncodingL[1]+1) + " " + str(pSync.binEncodingL[1]+1) + " ") - if pSync.binEncodingL[1] > pSrc.binEncodingL[1]: - if t.core: - outputF.write("drawArrowUp\n") - else: - outputF.write("drawDashArrowUp\n") - else: - if t.core: - outputF.write("drawArrowDown\n") - else: - outputF.write("drawDashArrowDown\n") - -############################################################################## -# Write out a *.g file for derived graph. -############################################################################## -def writeGfile(varsL,datL,binsL,ratesL,divisionsL,tParam,g,gFile): - outputF = open(gFile, 'w') - writeInformationalHeader(outputF,"#",tParam,g) - outputF.write(".outputs fail") - outputF.write("\n") - outputF.write(".dummy") - placeL = g.placeD.values() - placeL.sort() - transitionL = g.transitionD.values() - transitionL.sort() - for t in transitionL: - outputF.write(" t" + str(t.transitionNum)) - outputF.write("\n") - outputF.write("#@@.variables") - for i in range(1,len(varsL)): - outputF.write(" "+varsL[i].name) - outputF.write("\n") - outputF.write("#@@.init_state [0") - outputF.write("]\n") - #write out the graph of places and transitions - outputF.write(".graph\n") - for p in placeL: - if p.incomingL: - for t in p.incomingL: - outputF.write("t"+str(t.transitionNum)+" p"+str(p.placeNum)+"\n") - if p.outgoingL: - for t in p.outgoingL: - outputF.write("p"+str(p.placeNum)+" t"+str(t.transitionNum)+"\n") - outputF.write(".marking {") - for p in g.initMarkingL: - outputF.write("p"+str(p.placeNum)+" ") - outputF.write("}\n") - outputF.write("#@@.init_vals {") - for i in range(1,len(varsL)): - outputF.write("<" + varsL[i].name + "=[" + g.minInitValInt(i) + "," + g.maxInitValInt(i) + "]>") - outputF.write("}\n") - outputF.write("#@@.init_rates {") - for i in range(1,len(varsL)): - if g.initRateL[i][0] == "-" or varsL[i].dmvc: - outputF.write("<"+varsL[i].name+"=0>") - else: - outputF.write("<" + varsL[i].name + "=[" + g.minInitRateInt(i) + "," + g.maxInitRateInt(i) + "]>") - outputF.write("}\n") - outputF.write("#@@.enablings {") - if g.failProp: - enFailAnd = "&~fail" - enFail = "~fail" - else: - enFailAnd = "" - enFail = "" - for t in transitionL: - if t.outgoingP and t.incomingP and t.incomingP.isRateP() and t.outgoingP.isRateP(): - diffL = t.outgoingP.diff(t.incomingP,varsL) - #print "t"+str(t.transitionNum)+" - len(diffL):"+str(len(diffL)) - condStr = "" - for ind in range(len(diffL)): - if ind > 0: - condStr += "&" - i = diffL[ind] - #Note: the i-1 floating around here are to compensate for the fact that binEncoding doesn't include time (varsL[0]), so to index into binEncoding using a varsL index you must subtract 1 - if (t.incomingP.binEncodingL[i-1] < t.outgoingP.binEncodingL[i-1]): - print "Incoming place:" + t.incomingP.keyStr() + " Outgoing place:" + t.outgoingP.keyStr() - if t.incomingP.binEncodingL[i-1] == -1: - val = minVarValL[i] - condStr += "(" + varsL[i].name + ">=" + val + ")" - elif t.incomingP.binEncodingL[i-1] == len(divisionsL[i]): - val = maxVarValL[i] - condStr += "(" + varsL[i].name + ">=" + val + ")" - else: - print "binEncodingL:" + str(t.incomingP.binEncodingL[i-1]) - val = str(int(divisionsL[i][t.incomingP.binEncodingL[i-1]])) - condStr += "(" + varsL[i].name + ">=" + val + ")" - else: - print "Incoming place:" + t.incomingP.keyStr() + " Outgoing place:" + t.outgoingP.keyStr() - if t.outgoingP.binEncodingL[i-1] == -1: - val = minVarValL[i] - condStr += "~(" + varsL[i].name + ">=" + val + ")" - elif t.outgoingP.binEncodingL[i-1] == len(divisionsL[i]): - val = maxVarValL[i] - condStr += "~(" + varsL[i].name + ">=" + val + ")" - else: - val = str(int(divisionsL[i][t.outgoingP.binEncodingL[i-1]])) - condStr += "~(" + varsL[i].name + ">=" + val + ")" - t.enabling = condStr + enFailAnd - outputF.write("") - if t.outgoingP and t.incomingP and t.incomingP.isDmvcP() and t.outgoingP.isDmvcP() and enFail: - t.enabling = enFail - outputF.write("") - if t.incomingP and t.incomingP.isPropP(): - t.enabling = t.incomingP.property - outputF.write("") - if t.outgoingP and t.incomingP and t.incomingP.isAsgnP() and t.outgoingP.isRateP(): - diffL = t.outgoingP.diff(t.incomingP,varsL) - condStr = "" - for ind in range(len(diffL)): - if ind > 0: - condStr += "&" - i = diffL[ind] - #Note: the i-1 floating around here are to compensate for the fact that binEncoding doesn't include time (varsL[0]), so to index into binEncoding using a varsL index you must subtract 1 - if (t.incomingP.asgBinL[i-1] < t.outgoingP.binEncodingL[i-1]): - val = str(int(divisionsL[i][t.incomingP.asgBinL[i-1]])) - condStr += "(" + varsL[i].name + ">=" + val + ")" - else: - val = str(int(divisionsL[i][t.outgoingP.binEncodingL[i-1]])) - condStr += "~(" + varsL[i].name + ">=" + val + ")" - t.enabling = condStr + enFailAnd - outputF.write("") - outputF.write("}\n") - hasRates = False - for i in range(1,len(varsL)): - if not varsL[i].dmvc: - hasRates = True - break - if hasRates: - outputF.write("#@@.rate_assignments {") - if placeRates: - #Place based rate generation output - for p in placeL: - if p.isRateP(): - for t in p.incomingL: - for i in range(1,len(varsL)): - if not varsL[i].dmvc: - outputF.write("") - else: - #Transition based rate generation output - for t in transitionL: - if t.outgoingP and t.incomingP: - diffL = t.outgoingP.diff(t.incomingP,varsL) - for i in diffL: - if t.incomingP.isRateP(): - outputF.write("") - outputF.write("}\n") - if dmvcVarExists(varsL): - flag = 0 - #outputF.write("#@@.assignments {") - for p in placeL: - if p.isDmvcP(): - for t in p.incomingL: - if flag == 0: - outputF.write("#@@.assignments {") - flag = 1 - outputF.write("") - if p.isAsgnP(): - for a in p.asg: - if a.valL: - for t in p.incomingL: - if flag == 0: - outputF.write("#@@.assignments {") - flag = 1 - outputF.write("") - if flag == 1: - outputF.write("}\n") - flag = 0 - #outputF.write("#@@.delay_assignments {") - for p in placeL: - if p.isDmvcP(): - for t in p.outgoingL: - if flag == 0: - outputF.write("#@@.delay_assignments {") - flag = 1 - outputF.write("") - if flag == 1: - outputF.write("}\n") - if g.failProp: - outputF.write("#@@.boolean_assignments {") - for p in placeL: - if p.isPropP(): - for t in p.outgoingL: - outputF.write("") - outputF.write("}\n") - outputF.write("#@@.continuous") - for i in range(1,len(varsL)): - outputF.write(" "+varsL[i].name) - outputF.write("\n") - outputF.write(".end\n") - outputF.close() - -############################################################################## -# Compare DMVC places by the dmvcVar. -############################################################################## -def placeVarCmp(a,b): - if a.dmvcVar < b.dmvcVar: - return -1 - elif a.dmvcVar == b.dmvcVar: - return 0 - else: - return 1 - -############################################################################## -# Compare RATE places according to their bins. -############################################################################## -def placeBinCmp(a,b): - for i in range(len(a.binEncodingL)): - if a.binEncodingL[i] < b.binEncodingL[i]: - return -1 - elif a.binEncodingL[i] > b.binEncodingL[i]: - return 1 - return 0 - -############################################################################## -# Write out a *.va file for derived graph. -############################################################################## -def writeVerilogAfile(varsL,datL,binsL,ratesL,divisionsL,tParam,g,vaFile): - outputF = open(vaFile, 'w') - outputF.write("`include \"disciplines.h\"\n\n") - baseFileL = os.path.splitext(vaFile) - outputF.write("module "+baseFileL[0]+"(") - for i in range(1,len(varsL)): - if i != 1: - outputF.write(",") - outputF.write(varsL[i].name+"_io") - outputF.write(");\n") - - #Create electrical io variables for the inputs/outputs of the system - for i in range(1,len(varsL)): - outputF.write("\tinout "+varsL[i].name+"_io;\n") - outputF.write("\telectrical "+varsL[i].name+"_io;\n") - outputF.write("\n") - - #create real variables for each variable in the system - for i in range(1,len(varsL)): - #print varsL[i].name+" "+str(varsL[i].input)+" "+str(varsL[i].dmvc) - if not varsL[i].input: - outputF.write("\treal "+varsL[i].name+"_var;\n") - outputF.write("\n") - - #create real variables for the rate of each continuous variable - #(DMVC variables and handled with time and as such don't need a - #variable - rateVarL = [] - for i in range(1,len(varsL)): - if not varsL[i].dmvc and not varsL[i].input: - rateVarL.append(i) - outputF.write("\treal rate_"+varsL[i].name+";\n") - outputF.write("\n") - - outputF.write("\n\tanalog begin\n") - - #Setup the initial values and rates - outputF.write("\t\t@@(initial_step) begin\n") - for i in range(1,len(varsL)): - if not varsL[i].input: - outputF.write("\t\t\t"+varsL[i].name+"_var = "+str(g.initValL[i][0])+";\n") - if not varsL[i].dmvc and not varsL[i].input: - outputF.write("\t\t\trate_"+varsL[i].name+" = "+str(g.initRateL[i][0]*tParam.vaRateUpdateInterval)+";\n") - outputF.write("\t\tend\n\n") - - #Create lists of appropriately sorted places. - placeL = g.placeD.values() - placeL.sort() - ratePlaceL = [] - dmvcPlaceL = [] - asgnPlaceL = [] - for p in placeL: - if p.isRateP(): - ratePlaceL.append(p) - elif p.isDmvcP(): - if varsL[p.dmvcVar].input: - dmvcPlaceL.append(p) - elif p.isAsgnP(): - asgnPlaceL.append(p) - dmvcPlaceL.sort(placeVarCmp) - dmvcPlaceVarL = [] - for i in range(len(varsL)): - dmvcPlaceVarL.append([]) - for p in dmvcPlaceL: - dmvcPlaceVarL[p.dmvcVar].append(p) - - #Generate @@(cross) statements for continuous rate variables. This - #should work as the probability of two of these changing at the same - #time is unlikely - for p in ratePlaceL: - tL = p.incomingL - for t in tL: - if t.outgoingP and t.incomingP and t.incomingP.isRateP() and t.outgoingP.isRateP(): - diffL = t.outgoingP.diff(t.incomingP,varsL) - for i in diffL: - #print "diff:"+str(varsL[i].name) - hasRate = False - for j in range(1,len(varsL)): - if not varsL[j].dmvc and not varsL[j].input: - hasRate = True - break - #Note: the i-1 floating around here is to compensate for the - #fact that binEncoding doesn't include time (varsL[0]), so - #to index into binEncoding using a varsL index you must - #subtract 1 - if hasRate: - if (t.incomingP.binEncodingL[i-1] < t.outgoingP.binEncodingL[i-1]): - direction = "1" - val = divisionsL[i][t.incomingP.binEncodingL[i-1]] - else: - direction = "-1" - val = divisionsL[i][t.outgoingP.binEncodingL[i-1]] - if varsL[i].input: - outputF.write("\t\t@@(cross(V("+varsL[i].name+"_io) - "+str(val)+","+direction+")) begin\n") - else: - outputF.write("\t\t@@(cross("+varsL[i].name+"_var - "+str(val)+","+direction+")) begin\n") - for j in range(1,len(varsL)): - if not varsL[j].dmvc and not varsL[j].input: - outputF.write("\t\t\trate_"+varsL[j].name+" = "+str(p.avgRate(j)*tParam.vaRateUpdateInterval)+";\n") - outputF.write("\t\tend\n") - outputF.write("\n") - - #Generate the if statements used for assignment places - #print "Asg places...["+str(len(asgnPlaceL))+"]" - for p in asgnPlaceL: - #print "p"+str(p.placeNum)+"["+p.keyStr()+"]--["+p.preExGrandParentBinL.keyStr()+"]->["+p.incomingL[0].incomingP.keyStr()+"]->["+p.outgoingL[0].outgoingP.keyStr()+"]" - outputF.write("\t\tif(") - condStr = "" - cnt = 0 - diffL = p.outgoingL[0].outgoingP.diff(p.incomingL[0].incomingP,varsL) - #print "diffL:"+str(diffL) - for i in range(1,len(varsL)): - #print "divisionsL["+str(i)+"]:"+str(divisionsL[i])+" len:"+str(len(divisionsL[i])) - #print "p.incomingL[0].incomingP.incomingL[0].incomingP.binEncodingL["+str(i-1)+"]"+str(p.incomingL[0].incomingP.incomingL[0].incomingP.binEncodingL) - if cnt > 0: - condStr = " && " - cnt += 1 - if i in diffL and varsL[i].input: - #case for the zero bin - if (p.outgoingL[0].outgoingP.binEncodingL[i-1] == 0): - val = str(divisionsL[i][p.outgoingL[0].outgoingP.binEncodingL[i-1]]) - outputF.write(condStr+"!(V("+varsL[i].name+"_io) >= "+val+")") - #case for the high order bin - elif (p.outgoingL[0].outgoingP.binEncodingL[i-1] == len(divisionsL[i])): - #print "p.incomingL[0].incomingP.binEncodingL["+str(len(divisionsL[i])-1)+"]"+str(p.incomingL[0].incomingP.binEncodingL) - val = str(divisionsL[i][len(divisionsL[i])-1]) - outputF.write(condStr+"(V("+varsL[i].name+"_io) >= "+val+")") - #case for everything else - else: - val = str(divisionsL[i][p.outgoingL[0].outgoingP.binEncodingL[i-1]]) - outputF.write(condStr+"(V("+varsL[i].name+"_io) >= "+val+")") - val = str(divisionsL[i][p.outgoingL[0].outgoingP.binEncodingL[i]]) - outputF.write(condStr+"!(V("+varsL[i].name+"_io) >= "+val+")") - else: - #case for the zerio bin - if (p.incomingL[0].incomingP.binEncodingL[i-1] == 0): - val = str(divisionsL[i][p.incomingL[0].incomingP.binEncodingL[i-1]]) - outputF.write(condStr+"!(V("+varsL[i].name+"_io) >= "+val+")") - #case for the high order bin - elif (p.incomingL[0].incomingP.binEncodingL[i-1] == len(divisionsL[i])): - #print "p.incomingL[0].incomingP.binEncodingL["+str(len(divisionsL[i])-1)+"]"+str(p.incomingL[0].incomingP.binEncodingL) - val = str(divisionsL[i][len(divisionsL[i])-1]) - outputF.write(condStr+"(V("+varsL[i].name+"_io) >= "+val+")") - #case for everything else - else: - val = str(divisionsL[i][p.incomingL[0].incomingP.binEncodingL[i-1]]) - outputF.write(condStr+"(V("+varsL[i].name+"_io) >= "+val+")") - val = str(divisionsL[i][p.incomingL[0].incomingP.binEncodingL[i]]) - outputF.write(condStr+"(V("+varsL[i].name+"_io) >= "+val+")") - outputF.write(") begin\n") - for a in p.asg: - #print "Assignment:"+str(a) - if a.valL: - outputF.write("\t\t\t"+varsL[a.var].name+"_var = "+str(a.avgValue())+";\n") - outputF.write("\t\tend\n") - outputF.write("\n") - - #Create a periodic timing loop for each DMVC variable to set its value - #ASSUMPTION: all dmvc input variable graphs are loops - for i in range(len(dmvcPlaceVarL)): - period = 0.0 - for p in dmvcPlaceVarL[i]: - period += p.avgTime() - initVal = 0.0 - for p in dmvcPlaceVarL[i]: - if not varsL[p.dmvcVar].input: - outputF.write("\t\t@@(timer("+str(initVal)+","+str(period)+")) begin\n") - outputF.write("\t\t\t"+varsL[p.dmvcVar].name+"_var = "+str(p.dmvcVal)+";\n") - outputF.write("\t\tend\n") - initVal += p.avgTime() - outputF.write("\n") - - #Setup a timer to add the appropriate value to each continuous - #variable at the proper time - hasRate = False - for i in range(1,len(varsL)): - if not varsL[i].dmvc and not varsL[i].input: - hasRate = True - break - if hasRate: - outputF.write("\t\t@@(timer(0.0,"+str(tParam.vaRateUpdateInterval)+")) begin\n") - for i in range(1,len(varsL)): - if not varsL[i].dmvc and not varsL[i].input: - outputF.write("\t\t\t"+varsL[i].name+"_var = "+varsL[i].name+"_var + rate_"+varsL[i].name+";\n") - outputF.write("\t\tend\n\n") - - #Ensure that the values on the variables get pushed to the output - #ports quickly - for i in range(1,len(varsL)): - if not varsL[i].input: - outputF.write("\t\tV("+varsL[i].name+"_io) <+ transition("+varsL[i].name+"_var,1p,1p,1p);\n") - - outputF.write("\tend\n") - outputF.write("endmodule\n") - outputF.close() - -############################################################################## -# Write out a *.vhd file for derived graph. -############################################################################## -def writeVHDLAMSfile(varsL,datL,binsL,ratesL,divisionsL,tParam,g,vhdFile): - outputF = open(vhdFile, 'w') - outputF.write("library IEEE;\n") - outputF.write("use IEEE.std_logic_1164.all;\n") - outputF.write("use work.handshake.all;\n") - outputF.write("use work.nondeterminism.all;\n\n") - outputF.write("entity amsDesign is\n") - outputF.write("end amsDesign;\n\n") - - baseFileL = os.path.splitext(vhdFile) - outputF.write("architecture "+baseFileL[0]+" of amsDesign is\n") - for i in range(1,len(varsL)): - outputF.write("\tquantity "+varsL[i].name+":real;\n") - outputF.write("\nbegin\n") - - for i in range(1,len(varsL)): - outputF.write("\tbreak "+varsL[i].name+" => "+str(int(g.initValL[i][0]))+".0;\n") - outputF.write("\n") - - for i in range(1,len(varsL)): - if varsL[i].dmvc: - outputF.write("\t"+varsL[i].name+"'dot == 0.0;\n") - outputF.write("\n") - - placeL = g.placeD.values() - placeL.sort() - ratePlaceL = [] - dmvcPlaceL = [] - for p in placeL: - if p.isRateP(): - ratePlaceL.append(p) - elif p.isDmvcP(): - dmvcPlaceL.append(p) - dmvcPlaceL.sort(placeVarCmp) - dmvcPlaceVarL = [] - for i in range(len(varsL)): - dmvcPlaceVarL.append([]) - for p in dmvcPlaceL: - dmvcPlaceVarL[p.dmvcVar].append(p) - - ifL = [] - for i in varsL: - ifL.append([]) - for p in ratePlaceL: - tL = p.incomingL - for t in tL: - if t.outgoingP and t.incomingP and t.incomingP.isRateP() and t.outgoingP.isRateP(): - diffL = t.outgoingP.diff(t.incomingP,varsL) - ifStr = "" - for i in diffL: - #Note: the i-1 floating around here is to compensate for the fact that binEncoding doesn't include time (varsL[0]), so to index into binEncoding using a varsL index you must subtract 1 - if (t.incomingP.binEncodingL[i-1] < t.outgoingP.binEncodingL[i-1]): - above = True - val = divisionsL[i][t.incomingP.binEncodingL[i-1]] - else: - above = False - val = divisionsL[i][t.outgoingP.binEncodingL[i-1]] - if above: - ifStr = varsL[i].name+"'above("+str(val)+")" - #outputF.write("\tif "+varsL[i].name+"'above("+str(val)+") use\n") - else: - ifStr = "not "+varsL[i].name+"'above("+str(val)+")" - #outputF.write("\tif not "+varsL[i].name+"'above("+str(val)+") use\n") - for j in range(1,len(varsL)): - rateStr = "" - if not varsL[j].dmvc and not varsL[j].input: - rateStr = varsL[j].name + "'dot == span(" + str(p.minRateInt(j))+".0,"+str(p.maxRateInt(j))+".0)" - ifL[j].append((ifStr,rateStr)) - #outputF.write("\t\t" + varsL[j].name + "'dot == span(" + str(p.minRateInt(j))+".0,"+str(p.maxRateInt(j))+".0);\n") - #outputF.write("\tend use;\n") - #outputF.write("\n"); - for i in range(1,len(varsL)): - for j in range(len(ifL[i])): - if j == 0: - outputF.write("\tif "+ifL[i][j][0]+" use\n") - else: - outputF.write("\telsif "+ifL[i][j][0]+" use\n") - outputF.write("\t\t"+ifL[i][j][1]+";\n") - if ifL[i]: - outputF.write("\tend use;\n\n") - - outputF.write("\tprocess\n") - outputF.write("\tbegin\n") - for i in range(len(dmvcPlaceVarL)): - for p in dmvcPlaceVarL[i]: - outputF.write("\t\twait for delay("+str(p.maxTimeInt())+","+str(p.minTimeInt())+");\n") - outputF.write("\t\tbreak "+varsL[p.dmvcVar].name+" => "+str(p.dmvcVal)+";\n") - outputF.write("\tend process;\n\n") - outputF.write("end "+baseFileL[0]+";\n") - outputF.close() - -############################################################################## -# Print statistics about the graph representing the model. level is the -# verbosity level for the statistics. -############################################################################## -def printStatistics(level,g,divisionsL,varsL): - print "Places:"+str(len(g.placeD)) - print "Transitions:"+str(len(g.transitionD)) - print "Rates:" - placeL = g.placeD.values() - ratePlaceL = [] - for p in placeL: - if p.isRateP(): - ratePlaceL.append(p) - ratePlaceL.sort(placeBinCmp) - #The code below is an indexing nightmare due to the fact that binEncodingL is only as long as the number of continuous variable while all of the other arrays are as long as time+continuous variables. - for p in ratePlaceL: - #print "p"+str(p.placeNum)+"binEncodingL:"+str(p.binEncodingL) - printStr = "" - binLen = len(p.binEncodingL) - if p.type == ASGN: - binLen += -1 - for i in range(binLen): - if i != 0: - printStr += ", " - if p.binEncodingL[i] == 0: - printStr += varsL[i+1].name+"<"+str(divisionsL[i+1][int(p.binEncodingL[i])]) - elif p.binEncodingL[i] == len(divisionsL[i+1]): - printStr += varsL[i+1].name+">="+str(divisionsL[i+1][int(p.binEncodingL[i])-1]) - else: - printStr += varsL[i+1].name+">="+str(divisionsL[i+1][int(p.binEncodingL[i])-1])+", "+varsL[i+1].name+"<"+str(divisionsL[i+1][int(p.binEncodingL[i])]) - printStr += " " - for i in range(len(p.binEncodingL)): - if not varsL[i+1].dmvc: - if intRates: - printStr += varsL[i+1].name+":["+str(p.minRateInt(i+1))+","+str(p.maxRateInt(i+1))+"] " - else: - printStr += varsL[i+1].name+":["+str(p.minRate(i+1))+","+str(p.maxRate(i+1))+"] " - print printStr - if level > 1: - for p in placeL: - print "p"+str(p.placeNum)+" ["+p.keyStr()+"]" - -############################################################################## -############################################################################## -def main(): - usage = "usage: %prog [options] datFile1 ... datFileN" - parser = OptionParser(usage=usage) - parser.set_defaults(binsFile="",propFile="",trace=False) - parser.add_option("-b", "--bins", action="store", dest="binsFile", help="The name of the file containing the thresholds to be used.") - parser.add_option("-p", "--prop", action="store", dest="propFile", help="The name of the file containing the property to be verified.") - parser.add_option("-l", "--lhpn", action="store", dest="gFile", help="The name of the .g (LHPN) file to be created.") - - (options, args) = parser.parse_args() - - #if not len(args) > 0: - # cStr = cText.cSetFg(cText.RED) - # cStr += "ERROR:" - # cStr += cText.cSetAttr(cText.NONE) - # print cStr + " At least one data file is required." - # parser.print_help() - #sys.exit() - #print "args:"+str(args) - datFileL = [] - i = 1 - while os.path.isfile("run-" + str(i) + ".tsd"): - #print i - datFileL.append("run-" + str(i) + ".tsd") - i += 1 - #datFileL = [i-2] - print "length " + str(i) + str(len(datFileL)) - #for i in range(len(datFileL)): - # datFileL[i] = tempDatL[i] - - #The thresholds, variables, and prop files are the same for all - #dat files, so process them before processing the individual dat - #files - baseFileL = os.path.splitext("run-1.tsd") - if options.binsFile == "": - options.binsFile = baseFileL[0] + ".bins" - #The variable names and ordering must be consistent across files, - #so it is extracted from the first dat file and checked against - #every other dat file - varsL = extractVars("run-1.tsd") - divisionsL, tParam = parseBinsFile(options.binsFile,varsL,options.trace) - gFile = options.gFile - psFile = baseFileL[0] + ".ps" - vaFile = baseFileL[0] + ".va" - vhdFile = baseFileL[0] + ".vhd" - - failProp = "" - if options.propFile: - failProp = parsePropFile(options.propFile) - - g = Graph(varsL,failProp) - cvg = Coverage(datFileL) - for i in range(len(datFileL)): - #print "Working on: "+datFileL[i] - cvg.index = i - baseFileL = os.path.splitext(datFileL[i]) - sortFile = baseFileL[0] + ".sort" - rateFile = baseFileL[0] + ".rate" - - datL,numPoints = parseDatFile(datFileL[i],varsL) - - binsL = genBins(varsL,datL,divisionsL) - writeSortFile(varsL,numPoints,datL,binsL,sortFile) - #print "print1 " + str(len(varsL)) + str(len(datFileL)) - - ratesL = genRates(varsL,datL,binsL,rateSampling) - writeRateFile(varsL,numPoints,datL,binsL,ratesL,rateFile) - - dmvcRunL = findDMVC(datL,varsL,tParam) - updateGraph(g,varsL,datL,binsL,ratesL,dmvcRunL,tParam,failProp,cvg,divisionsL) - print "print2 " + str(len(varsL)) - - #Graph expansion is used for non-input DMVC places - exG = expandGraph(g,varsL) - print "Expanded graph:" - print str(exG) - #writeVerilogAfile(varsL,datL,binsL,ratesL,divisionsL,tParam,exG,vaFile) - sbcG = rmMultipleBinChange(exG,varsL) - #Values need to be normalized for the g file - normG, normDivisionsL = normalizeValues(sbcG,varsL,divisionsL) - if limitExists: - addMetaBins(normG,varsL,divisionsL) - print "Graph with meta-bins:\n" + str(normG) - writeGfile(varsL,datL,binsL,ratesL,normDivisionsL,tParam,normG,gFile) - #writePSfile(normG,varsL,psFile) - writeVHDLAMSfile(varsL,datL,binsL,ratesL,normDivisionsL,tParam,normG,vhdFile) - #printStatistics(9,normG,divisionsL,varsL) - print "Coverage:\n"+str(cvg) -############################################################################## -############################################################################## - -########### -# Globals # -########### -rateSampling = "inf" #How many points should exist between the sampling of different rates..."inf" samples once/threshold -pathLength = 15 #For "inf" rate sampling the number of time points that a "run" must persist for the rate to be calculated. This is just another parameter to help with the data smoothing. -placeRates = True #When true the script calculates rates based on places. When false it calculates rates based on transitions although there is very little infrastructure for transition based rates and it isn't well tested. -intRates = True #When true printStatistics prints the rates as integers. When false the rates are printed as floats. -minDelayVal = 10 #delay values must be greater than minDelayVal after scaling -minRateVal = 10 #rate values must be greater than minDelayVal after scaling -minDivisionVal = 10 #division values must be greater than minDelayVal after scaling -decPercent = 0.15 #to remove perimiter effects remove the first or last time value if they are >decPercent*100% different than a non-first or non-last extreme value - - -#Place types -RATE = 0 -DMVC = 1 -PROP = 2 -ASGN = 3 -TRACE = 4 -#Variable types -VOLTAGE = 10 -CURRENT = 11 - -if __name__ == "__main__": - main() diff --git a/bin/detect-java.sh b/bin/detect-java.sh deleted file mode 100755 index 00ec19ee2..000000000 --- a/bin/detect-java.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/bash -#******************************************************************************* -# -# This file is part of iBioSim. Please visit -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* -# This script attempts to find an existing installation of Java that meets a minimum version -# requirement on a Linux machine. If it is successful, it will export a JAVA_HOME environment -# variable that can be used by another calling script. -# -# To specify the required version, set the REQUIRED_VERSION to the major version required, -# e.g. 1.3, but not 1.3.1. -REQUIRED_VERSION=1.7 - -# Transform the required version string into a number that can be used in comparisons -REQUIRED_VERSION=`echo $REQUIRED_VERSION | sed -e 's;\.;0;g'` -# Check JAVA_HOME directory to see if Java version is adequate -if [ $JAVA_HOME ] -then - JAVA_EXE=$JAVA_HOME/bin/java - $JAVA_EXE -version 2> tmp.ver - VERSION=`cat tmp.ver | grep "java version" | awk '{ print substr($3, 2, length($3)-2); }'` - rm tmp.ver - VERSION=`echo $VERSION | awk '{ print substr($1, 1, 3); }' | sed -e 's;\.;0;g'` - if [ $VERSION ] - then - if [ $VERSION -ge $REQUIRED_VERSION ] - then - JAVA_HOME=`echo $JAVA_EXE | awk '{ print substr($1, 1, length($1)-9); }'` - else - JAVA_HOME= - fi - else - JAVA_HOME= - fi -fi - -# If the existing JAVA_HOME directory is adequate, then leave it alone -# otherwise, use 'locate' to search for other possible java candidates and -# check their versions. -if [ $JAVA_HOME ] -then - : -else - for JAVA_EXE in `locate bin/java | grep java$ | xargs echo` - do - if [ $JAVA_HOME ] - then - : - else - $JAVA_EXE -version 2> tmp.ver 1> /dev/null - VERSION=`cat tmp.ver | grep "java version" | awk '{ print substr($3, 2, length($3)-2); }'` - rm tmp.ver - VERSION=`echo $VERSION | awk '{ print substr($1, 1, 3); }' | sed -e 's;\.;0;g'` - if [ $VERSION ] - then - if [ $VERSION -ge $REQUIRED_VERSION ] - then - JAVA_HOME=`echo $JAVA_EXE | awk '{ print substr($1, 1, length($1)-9); }'` - fi - fi - fi - done -fi - -# If the correct Java version is detected, then export the JAVA_HOME environment variable -if [ $JAVA_HOME ] -then - export JAVA_HOME -fi diff --git a/bin/gcm2sbml.pl b/bin/gcm2sbml.pl deleted file mode 100755 index faa2db0c5..000000000 --- a/bin/gcm2sbml.pl +++ /dev/null @@ -1,1190 +0,0 @@ -#!/usr/bin/perl -w -#******************************************************************************* -# -# This file is part of iBioSim. Please visit -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* - -use Getopt::Std; - -#Global indicing variables -$STATE = 0; -$PROTEIN = 1; -$CONST = 2; - -$PROMOTER = 0; -$INPUT = 1; -$OUTPUT = 2; -$ARROWHEAD = 3; -$STOC = 4; -$TYPE = 5; - -#Global reaction params -$deg = 0.0003; -$kf_dimer = 20.0; -$kr_dimer = 1.0; - -$kf_complex = 20.0; -$kr_complex = 1.0; - -$kf_rep = 0.8; -$kr_rep = 1.0; - -$kf_bind_dimer = 10.0; -$kr_bind_dimer = 1.0; - -$kf_act = .00033; -$kr_act = 1.0; - -$rnap_binding = 0.033; -$rnap_unbinding = 1.0; -$ocr = 0.1; -$unactived_production = 0.01; -$activated_production = 0.1; -$num_promo = 1.0; -$stochiometry = 1.; -$num_RNAP = 30.0; -$dimer_deg = .0003; - -$spastic = 0.9; - -sub getuid{ - return $uid++; -} - -sub main{ - if ($#ARGV == 1){ - #open up the dotfile and store it to an array - my $file = $ARGV[0]; - $abv_name = $ARGV[0]; - open (FILE, "$file") or die "I cannot open dot $file\n"; - @dot_file = ; - close FILE; - #open out outfile and start writing to it when writing to OUT - open (OUT, ">$ARGV[1]") or die "I cannot open out file $ARGV[1]\n"; - $dot_file = join ("", @dot_file); - - #create the normal sbml file - fill_hashes(); - build_reactions(); -# print_all_info(); - - create_real_network($file); - } elsif ($#ARGV >= 2) { - getopts ('dcf:'); - if ($opt_c) { - print "Applying biochemical abstraction\n"; - } - if ($opt_d) { - print "Applying dimer degradation\n"; - } - #open up the dotfile and store it to an array - $abv_name = $ARGV[$#ARGV-1]; - my $file = $ARGV[$#ARGV-1]; - open (FILE, "$file") or die "I cannot open dot $file\n"; - @dot_file = ; - close FILE; - - if ($opt_f) { - $file = $opt_f; - open (FILE, "$file") or die "I cannot open parameterfile $file\n"; - @parameter_file = ; - close FILE; - $parameter_file = join ("", @parameter_file); - load_parameters(); - } - - #open out outfile and start writing to it when writing to OUT - open (OUT, ">$ARGV[$#ARGV]") or die "I cannot open out file $ARGV[$#ARGV]\n"; - $dot_file = join ("", @dot_file); - - #create the normal sbml file - fill_hashes(); - build_reactions(); -# print_all_info(); - - create_real_network($file); - - } else { - print "Usage: dot2sbml.pl dotfile outfile or dot2sbml.pl -options dotfile outfile\n"; - } - -} - -sub load_parameters{ - print "Loading parameters\n"; - if ($parameter_file =~ m/deg[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing degradation to: $1\n"; - $deg = $1; - } - if ($parameter_file =~ m/dimer_deg[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing dimer degradation to: $1\n"; - $dimer_deg = $1; - } - if ($parameter_file =~ m/kf_dimer[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing dimer formation to: $1\n"; - $kf_dimer = $1; - } - if ($parameter_file =~ m/kr_dimer[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing dimer breaking to: $1\n"; - $kr_dimer = $1; - } - if ($parameter_file =~ m/kf_complex[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing complex formation to: $1\n"; - $kf_complex = $1; - } - if ($parameter_file =~ m/kr_complex[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing complex breakup to: $1\n"; - $kr_complex = $1; - } - if ($parameter_file =~ m/kf_rep[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing repression binding to: $1\n"; - $kf_rep = $1; - } - if ($parameter_file =~ m/kr_rep[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing repression unbinding to: $1\n"; - $kr_rep = $1; - } - if ($parameter_file =~ m/kf_act[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing activation binding to: $1\n"; - $kf_act = $1; - } - if ($parameter_file =~ m/kr_act[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing activation unbinding to: $1\n"; - $kr_act = $1; - } - if ($parameter_file =~ m/rnap_binding[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing rnap binding to: $1\n"; - $rnap_binding = $1; - } - if ($parameter_file =~ m/rnap_unbinding[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing rnap unbinding to: $1\n"; - $rnap_unbinding = $1; - } - if ($parameter_file =~ m/ocr[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing open complex production reaction binding to: $1\n"; - $ocr = $1; - } - if ($parameter_file =~ m/basal[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing open complex production (basal): $1\n"; - $unactived_production = $1; - } - if ($parameter_file =~ m/activated[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing open complex production (activated): $1\n"; - $activated_production = $1; - } - if ($parameter_file =~ m/dimer_binding[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing dimer binding rate: $1\n"; - $kf_bind_dimer = $1; - } - if ($parameter_file =~ m/dimer_unbinding[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing dimer unbinding rate: $1\n"; - $kr_bind_dimer = $1; - } - if ($parameter_file =~ m/stochiometry[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Stochiometry: $1\n"; - $stochiometry = $1; - } - if ($parameter_file =~ m/promoters[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing number of promoters: $1\n"; - $num_promo = $1; - } - if ($parameter_file =~ m/RNAP[\s]*=[\s]*([0-9]*\.?[0-9]*)/) { - print "Changing RNAP: $1\n"; - $num_RNAP = $1; - } -} - -sub fill_hashes{ - undef %dimers; - undef %proteins; #list of proteins in the soup - undef %promoters; #list of promoters - undef %biochem; #list of biochem reactions - undef %states; #list of states to proteins - undef %spastics; #list of spastic proteins - - $uid = 0; -# print "Try to parse inside bracket of species\n"; - while ($dot_file =~ m/(^|\n) *([^- \n]*) *\[(.*)\]/g){ - my $constant; - my $protein; - my $state = $2; - my $temp = $3; - my @struc; -# print "$temp\n"; - # Parse the label - if ($temp =~ m/label=\"(.*)\"/) { -# print "Protein Name: $1\n"; - $protein = $1; - if ($protein =~ m/spastic/){ - $spastics{$protein} =$protein; - } - } else { - print "Error, must have label for $state\n"; - exit(0); - } -# print "$temp\n"; - if ($temp =~ m/const=([^,|\s]*)/) { -# print "Constant Source: true\n"; - if ($1 eq "true") { - $constant = 1; - } else { - $constant = 0; - } - } else { -# print "Constant Source: false\n"; - $constant = 0; - } - #State name, protein name, is constant?, number of sources - @struct = ($state, $protein, $constant, 0); - - $proteins{$state} = [@struct]; - $states{$protein}=$state; - } -# print "Done parse inside bracket of species\n"; -# print "Try to parse inside bracket of edge\n"; - while ($dot_file =~ m/(^|\n) *([^ \n]*) *\-\> *([^ \n]*) *\[(.*)arrowhead=([^,\]]*)(.*)/g){ - my $start = $2; - my $end = $3; - my $info = $4; - my $arrowhead = $5; - my $extra_info = $6; - my $stoc = 1; - - my $promoter; - my $type="regular"; - - # custom data struct to hold information - my @struct; - - my $total_info = "$info,$extra_info"; - - if ($total_info =~ m/label="*([0-9]+)[,|\"]/){ - #dimerization for first species -# print "Found a dimer for $start in '$total_info'\n"; - $stoc = $1; - if (exists($dimers{$start}) and $dimers{$start} != $stoc){ - print "ERROR: unhandled dimerization. Unable to create different dimerizations, '$dimers{$start}' and '$start'"; - exit(0); - } - else{ - $dimers{$start} = $stoc; - } - } - - if ($total_info =~ m/promoter="(.*)"/){ -# print "Found promoter $1 in '$total_info'\n"; - $promoter = $1; - } else { - # If no promoter name is given, use the default promoter, the end name - #my $uid = getuid(); - $promoter = "Promoter_$end"; - } - if ($total_info =~ m/type=(.*)[,]/){ -# print "Found biochemical reaction\n"; - $type = $1; - } - build_interaction(($promoter, $proteins{$start}[1], $proteins{$end}[1], $arrowhead, $stoc, $type)); -# if ($total_info =~ m/label="*([0-9]+[,|\"])/){ #" -# } - } -# print "Done parse inside bracket of edge\n"; -} - -#Builds the interaction map used to generate the SBML -sub build_interaction { - my @params = @_; -# print "Printing params\n@params\n"; - #TODO: Check for problems if species used in more than 1 reaction - #Check to see if the promoter exists yet, if not, add it - if (not exists($promoters{$params[$PROMOTER]})) { - my @input = [[{$params[$INPUT]=>$params[$STOC]}, $params[$TYPE], $params[$ARROWHEAD]]]; -# print "First promo addr: @input\n"; - $promoters{$params[$PROMOTER]} = [$params[$PROMOTER], @input, {$params[$OUTPUT]=>$params[$OUTPUT]}]; - } - #If it does exist, check to see if it is a biochem reaction, and if so, see if it can be combined - #with an existing input - else { - my $found = 0; - #Check to see if it can be added to any reactions - #by cycling through each promoter and checking the input arrays - #and checking to see if the arrowhead matches - for (my $i = 0; $i <= $#{$promoters{$params[$PROMOTER]}[$INPUT]}; $i++) { - @aref = $promoters{$params[$PROMOTER]}[$INPUT][$i]; - if ($aref[0][2] eq $params[$ARROWHEAD] and $aref[0][1] eq "biochemical") { - $aref[0][0]{$params[$INPUT]} = $params[$STOC]; - $found = 1; - last; - } elsif ($aref[0][2] eq $params[$ARROWHEAD]){ - $aref[0][0]{$params[$INPUT]} = $params[$STOC]; - $found = 1; - last; - } - } - - if ($found == 0) { - my @input = [{$params[$INPUT]=>$params[$STOC]}, $params[$TYPE], $params[$ARROWHEAD]]; - push(@{$promoters{$params[$PROMOTER]}[1]}, @input); - } - $promoters{$params[$PROMOTER]}[$OUTPUT]{$params[$OUTPUT]}=$params[$OUTPUT]; - } -} - -#Removes all duplicate biochemical reactions -#Might not work if there's more than 1 reaction something can go to -#or if one reaction uses the same proteins as another -sub build_reactions { - #cycle through each promoter and check to see if there is a biochemical reaction - foreach $key (keys %promoters) { - for ($i = 0; $i <= $#{$promoters{$key}[$INPUT]}; $i++) { - my @temp = $promoters{$key}[$INPUT][$i]; - if ($temp[0][1] eq "biochemical") { - my @reactants; - foreach $t (keys %{@{$temp[0]}[0]}) {push(@reactants, $t);} - $complex = get_complex(@reactants); - if ($complex eq "") { - $complex = ""; - foreach $ref (@reactants) {$complex = "$complex\_$ref";} - $complex = "Complex$complex"; - push(@{$biochem{$complex}},@reactants); - - } - } - } - } -} - -sub get_num_act { - my $key = $_[0]; - my $num = 0; - for ($i = 0; $i <= $#{$promoters{$key}[$INPUT]}; $i++) { - my @temp = $promoters{$key}[$INPUT][$i]; - if ($temp[0][2] eq "vee") {$num++;} - } - return $num; -} - -sub get_num_rep { - my $key = $_[0]; - my $num = 0; - for ($i = 0; $i <= $#{$promoters{$key}[$INPUT]}; $i++) { - my @temp = $promoters{$key}[$INPUT][$i]; - if ($temp[0][2] eq "tee") {$num++;} - } - return $num; -} - -sub get_complex { - @params = @_; - print "Passed: @params\n"; - foreach $key (keys %biochem) { - my $all_found = 0; - foreach $ref (@params) { - $all_found = 1; - if (not ($key =~ m/$ref/)){ - print "Couldn't find $ref in $key\n"; - $all_found = 0; - last; - } - } - if ($all_found == 1) { - print "Found $key\n"; - return $key; - last; - } - } - print "Couldn't find @params\n"; - return ""; -} - -sub print_all_info { - print "State index: $STATE"; - print "Printing Protein Structure\n"; - foreach $key (keys %proteins) { - print "\nProteins{$key}: @{$proteins{$key}}\n"; - } - - print "Printing Promoter Structure\n"; - foreach $key (keys %promoters) { - print "\nPromoter{$key}:\n"; - for ($i = 0; $i <= $#{$promoters{$key}[$INPUT]}; $i++) { - my @temp = $promoters{$key}[$INPUT][$i]; - print "---------------\n\tInputs:\n"; - foreach $ref (keys %{$temp[0][0]}) {print "\t\t$ref,$temp[0][0]{$ref}\n";}; - print "\tType: $temp[0][1]\n"; - print "\tArrow: $temp[0][2]\n"; - } - print "---------------\n\tOutput:\n"; - foreach $ref (keys %{$promoters{$key}[$OUTPUT]}) { - print "\t\t$ref\n"; - } - } - - print "Printing Reactions\n"; - foreach $key (keys %biochem) { - print "\nReactions{$key}:\n"; - foreach $ref (@{$biochem{$key}}) { - print "\t$ref\n"; - } - } -} - -sub make_input{ - my @params = @_; - - my $binds = ""; - - if ($params[0][1] eq "biochemical") { - my @reactants; - foreach my $t (keys %{@{$params[0]}[0]}) {push(@reactants, $t);} -# print "LOOKING: @reactants\n"; - $binds = get_complex(@reactants); -# print "FOUND: $binds\n"; - } - else { - foreach my $ref (keys %{$params[0][0]}) { - my $stoc = $params[0][0]{$ref}; - if ($stoc > 1) { - $binds = "$ref\_$stoc,$binds"; - } else { - $binds = "$ref,$binds"; - } - } - } - return $binds; -} - -sub create_real_network{ -# print "Building Real Network\n------------------------------------\n"; - - print OUT "\n\n\n\n \n\n\n\n"; - - #Print the proteins in the network - foreach $key (keys %proteins){ - print OUT "\t\n"; - } - - #Print each dimer - foreach $key (keys %dimers) { - print OUT "\t\n"; - } - - if (!$opt_c) { - #Print each biochem reaction - foreach $key (keys %biochem) { - print OUT "\t\n"; - } - } - - #Print each promoter, bound promoter, unbound promoter - foreach my $key (keys %promoters){ - print OUT "\t\n"; - print OUT "\t\n"; - for ($i = 0; $i <= $#{$promoters{$key}[$INPUT]}; $i++) { - my @temp = $promoters{$key}[$INPUT][$i]; - my $binds = make_input(@temp); - my @protein = split (/,/,$binds); - foreach my $ref (@protein) { - if ($temp[0][2] eq "vee") { - print OUT "\t\n"; - } - elsif ($temp[0][2] eq "tee") { - print OUT "\t\n"; - } - } - } - } - - #print the rnap - print OUT "\t\n"; - #begin printing out reactions - print OUT "\n\n\n"; - - #setup the dedgadations - foreach $key (keys %proteins) { - if ($proteins{$key}[$PROTEIN] =~ m/spastic/){ - print OUT < - - - - - - k_deg - - - - - - -END - } - elsif ($proteins{$key}[$CONST] != 1) { - print OUT < - - - - - - - - k_deg - $proteins{$key}[$PROTEIN] - - - - - - - -END - } - } -# If dimers are allowed to degrade, then degrade them - if ($opt_d) { - foreach $key (keys %dimers) { - if ($proteins{$key} [$CONST] != 1) { - print OUT < - - - - - - - - k_deg - $proteins{$key}[1]\_$dimers{$key} - - - - - - - -END - } - } -} - -# #set up degradations for dimers -# foreach $key (keys %dimers) { -# print OUT < -# -# -# -# -# -# -# -# k_deg -# $proteins{$key}[1]\_$dimers{$key} -# -# -# -# -# -# -# -# -# END -# } -# -# #set up degradations for biochem reaction -# foreach $key (keys %biochem) { -# print OUT < -# -# -# -# -# -# -# -# k_deg -# $key -# -# -# -# -# -# -# -# -# END -# } - -#setup the dimers -foreach $key (keys %dimers){ -print OUT < - - - - - - - - - - - - - kf_d - - - $proteins{$key}[1] - 2 - - - - - kr - $proteins{$key}[1]\_$dimers{$key} - - - - - - - - - - -END -} - -#set up biochem reactions, if there is no -#abstraction -if (!$opt_c) { -foreach $key (keys %biochem){ -print OUT < - -END - foreach $ref (@{$biochem{$key}}) { - print OUT " \n"; - } -print OUT < - - - - - - - - - - kf_d -END -foreach $ref (@{$biochem{$key}}) { -print OUT <$ref -END -} -print OUT < - - - kr - $key - - - - - - - - - - -END -}} - -foreach $name (keys %spastics) { -print OUT < - - - - - - kf - - - - - - -END -} - -#cycle through each promoter and build each reaction -foreach $promoter (keys %promoters) { -#setup main gene generation pathways rnap binding and then ocr -print OUT < - - - - - - - - - - - - - - kf - $promoter - rnap - - - - kr - rnap_$promoter - - - - - - - - - - - - - - - - -END -foreach $output (keys %{$promoters{$promoter}[2]}) { -print OUT< -END -} - -#figure out how to handle a promoter that acts and reps -if (get_num_act($promoter) >= get_num_rep($promoter)) { - $my_ocr = $unactived_production; -} else { - $my_ocr = $ocr; -} - -print OUT< - - - - - koc - rnap_$promoter - - - - - - - -END - -#now set up any activated production or repression by cycling through the -#possible inputs - for ($i = 0; $i <= $#{$promoters{$promoter}[$INPUT]}; $i++) { - my @temp = $promoters{$promoter}[$INPUT][$i]; - #First, take care of repression - if ($temp[0][2] eq "tee") { - #Check for biochemical reaction - if ($temp[0][1] eq "biochemical") { - my @reactants; - foreach my $t (keys %{@{$temp[0]}[0]}) {push(@reactants, $t);} - $binds = get_complex(@reactants); -if (!$opt_c) { -print OUT< - - - - - - - - - - - - - - kf - $promoter - $binds - - - - kr - bound_$binds\_$promoter - - - - - - - - - -END -} else { -print OUT< - -END; -foreach my $t (@reactants) { -print OUT< -END; -} -print OUT< - - - - - - - - - - - KComplex - kf - $promoter -END; -foreach my $t (@reactants) { -print OUT<$t -END; -} -my $K = $kf_complex/$kr_complex; -print OUT< - - - kr - bound_$binds\_$promoter - - - - - - - - - - -END -} - } - else { -#Regular repression - foreach $binds (keys %{@{$temp[0]}[0]}) { - my $kf = $kf_rep; - my $kr = $kr_rep; - if ($temp[0][0]{$binds} > 1) { - $binds = "$binds\_$temp[0][0]{$binds}"; - $kf = $kf_bind_dimer; - $kr = $kr_bind_dimer; - } -print OUT< - - - - - - - - - - - - - - kf - $promoter - $binds - - - - kr - bound_$binds\_$promoter - - - - - - - - - -END - } - } - } - else { - #Take care of activation - #Check for biochemical reaction - if ($temp[0][1] eq "biochemical") { - my @reactants; - foreach my $t (keys %{@{$temp[0]}[0]}) {push(@reactants, $t);} - $binds = get_complex(@reactants); -if (!$opt_c) { -print OUT< - - - - - - - - - - - - - - - kf - $promoter - $binds - rnap - - - - kr - rnap_$binds\_a$promoter - - - - - - - - - - - - - - - - -END -foreach $output (keys %{$promoters{$promoter}[2]}) { -print OUT< -END -} -print OUT< - - - - - koc - rnap_$binds\_a$promoter - - - - - - - -END -} else { -print OUT< - -END -foreach my $t (@reactants) { -print OUT< -END -} -print OUT< - - - - - - - - - - - - KComplex - kf - $promoter -END -foreach my $t (@reactants) { -print OUT<$t -END -} -my $K = $kf_complex/$kr_complex; -print OUT<rnap - - - - kr - rnap_$binds\_a$promoter - - - - - - - - - - - - - - - - - -END -foreach $output (keys %{$promoters{$promoter}[2]}) { -print OUT< -END -} -print OUT< - - - - - koc - rnap_$binds\_a$promoter - - - - - - - -END -}} - else { - #Take care of activation, no biochemical -foreach my $binds (keys %{$temp[0][0]}) { -#check to make sure that it's not a dimer - my $kf = $kf_act; - my $kr = $kr_act; - if ($temp[0][0]{$binds} > 1) { - $binds = "$binds\_$temp[0][0]{$binds}"; - $kf = $kf_bind_dimer*$kf; - $kr = $kr_bind_dimer*$kr; - } -print OUT< - - - - - - - - - - - - - - - kf - $promoter - $binds - rnap - - - - kr - rnap_$binds\_a$promoter - - - - - - - - - - - - - - - - -END -foreach $output (keys %{$promoters{$promoter}[2]}) { -print OUT< -END -} -print OUT< - - - - - koc - rnap_$binds\_a$promoter - - - - - - - -END -}} - - - } - } -} - - -print OUT < - - -END -} - - - -main(); \ No newline at end of file diff --git a/bin/genBackgroundGCM.pl b/bin/genBackgroundGCM.pl deleted file mode 100755 index 9a9b2f1fe..000000000 --- a/bin/genBackgroundGCM.pl +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/perl -#******************************************************************************* -# -# This file is part of iBioSim. Please visit -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* - -open (IN, "run-1.tsd"); -@in = ; -close IN; - -open (OUT, ">background.gcm"); -$in = join("",@in); -@in = split (/\),\(/,$in); -$in = $in[0]; -$in =~ s/^(.*?)\),\(/$1/; -$in =~ s/\"//g; -$in =~ s/\(|\)//g; - -print "got '$in'\n"; - -@in = split (/,/,$in); - - - -print OUT "diagraph G {\n"; - -for ($i = 1; $i <= $#in; $i++){ - my $a = $in[$i]; - print OUT "$a [ID=$a,Name=\"$a\",Type=normal,shape=ellipse,label=\"$a\"]\n"; - -} -print OUT "}\nGlobal {\n}\nPromoters {\n}\nSBML file=\"\"\n" diff --git a/bin/gen_GeneNet_report.pl b/bin/gen_GeneNet_report.pl deleted file mode 100755 index a77c56954..000000000 --- a/bin/gen_GeneNet_report.pl +++ /dev/null @@ -1,339 +0,0 @@ -#!/usr/bin/perl -#******************************************************************************* -# -# This file is part of iBioSim. Please visit -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* - - -$genenet_dir = $ARGV[0]; -$reports_dir = $ARGV[1]; -$file_name_to_use = $ARGV[2]; -$out_name = $ARGV[3]; - -#remove the last / in dir -$reports_dir =~ s/\/$//; - - -sub main{ - my @files = `ls $genenet_dir*/*/*/$file_name_to_use`; - - print "Sorting dir\n"; - @files = dir_sort(\@files); - print "Done sorting dir\n"; - if ($#files < 1){ - print "Error, no files found\n"; - exit(1); - } - - open (OUT, ">$reports_dir/$out_name") or die "Cannot open out file '$reports_dir/$out_name'\n"; - print OUT ",,,,,GeneNet,,GeneNet,,,GeneNet Time,,,,,,,\n"; - #print OUT "Name,# Genes,# Experiments,Sample Interval,Exp Duration,R,P,R,P,R/R,P/P, # Correct (R P), Total Arcs (R), Reported Arcs (P), # Correct (R P), Total Arcs (R), Reported Arcs (P),\n"; - print OUT "Name,Genes,#Exp,S Size,Dur,R,P,# Correct (R P), Total Arcs (R), Reported Arcs (P),user,system,elapsed,CPU,major pagefaults,minor pagefaults, swaps\n"; - - my $exp_name = $files[0]; - my @running_genenet; - - for (my $i = 0; $i <= $#files; $i++){ - $file = $files[$i]; - $file =~ s/[.]dot//; - $file =~ s/\n//; - print "Checking $file\n"; - if (not_matching($file,$exp_name)){ - add_overview($exp_name); - $exp_name = $file; - - print OUT ",,,,"; - write_final(@running_genenet); - print OUT "\n"; - undef @running_genenet; - } - - - @tmp1 = check_correctness("$file"); - @running_genenet = addit(\@running_genenet,\@tmp1); - write_out_file($file); - write_initial(@tmp1); - if ($tmp1[0] != $tmp1[2]){ - print OUT ",ERROR: $tmp1[0] $tmp1[2],$tmp1[1],$tmp1[3]"; - } - else{ - print OUT ",$tmp1[0],$tmp1[1],$tmp1[3]"; - } - write_time(); - print OUT "\n"; - #print "Done with $file\n"; - - } - - print "Adding overview\n"; - add_overview($file); - - print OUT ",,,,"; - write_final(@running_genenet); - print OUT "\n\n"; - print OUT "Overview:\n$overview\n\n"; -} - -sub add_overview{ - my $exp_name = shift; - if ($exp_name =~ m/^.*\/([^\/]*)_[0-9]+\/[^\/]*_([0-9]+)_([0-9]+)_([0-9]+)_([0-9]+)\//){ - $overview = "$overview$1,$3,$4,$5,$6"; - } - else{ - $exp_name =~ s/.*?\/(.*?)\/.*/$1/; - $exp_name =~ s/[.]*//; - $overview = "$overview$exp_name,,,,"; - } - -} - - - -sub not_matching{ - my $a = shift; - my $b = shift; - if ($a =~ m/^(.*)_([0-9]+)\/[^\/]*_([0-9]+)_([0-9]+)_([0-9]+)_([0-9]+)\//){ - my @a = ($1,$2, $3, $4, $5,$6); - if ($b =~ m/^(.*)_([0-9]+)\/[^\/]*_([0-9]+)_([0-9]+)_([0-9]+)_([0-9]+)\//){ - my @b = ($1,$2, $3, $4, $5, $6); - if ($a[0] eq $b[0]){ - my @cmp = (5,4,3); - for (my $i = 0; $i <= $#cmp; $i++){ - my $ind = $cmp[$i]; - if (not ($a[$ind] == $b[$ind])){ - return 1; - } - } - return 0; - } - } - } - return 1; -} - -sub addit{ - $a1 = shift; - $a2 = shift; - @a1 = @$a1; - @a2 = @$a2; - for (my $i = 0; $i <= $#a2; $i++){ - $a1[$i] += $a2[$i]; - } - return @a1; -} - -sub write_out_file{ - my $file = shift; - if ($file =~ m/^.*\/(.*_[0-9]+)\/[^\/]*_([0-9]+)_([0-9]+)_([0-9]+)_([0-9]+)\//){ - my $name = $1; - my $num_genes = $2; - my $experiments = $3; - my $interval = $4; - my $ending_time = $5; - print OUT "$name,$num_genes,$experiments,$interval,$ending_time"; - } - else{ - #print "Error matching $file\n"; - $file =~ s/.*?\/(.*?)\/.*/$1/; - print OUT "$file,,,,"; - } -} - -sub write_final{ - my $a0 = shift; - my $a1 = shift; - my $a2 = shift; - my $a3 = shift; - $tmp_name = write_double($a0,$a1); - print OUT $tmp_name; - $overview = "$overview$tmp_name"; - - $tmp_name = write_double($a2,$a3); - print OUT $tmp_name; - $overview = "$overview$tmp_name\n"; - print OUT "\n"; -} - -sub write_initial{ - my $a0 = shift; - my $a1 = shift; - my $a2 = shift; - my $a3 = shift; - - print OUT write_double($a0,$a1); - print OUT write_double($a2,$a3); -} - -sub write_double{ - my $a = shift; - my $b = shift; - if ($b != 0){ - return ",=$a/$b"; - } - else { - return ",u $a/$b"; - } -} - -sub write_time{ -#0.51user 0.00system 0:00.52elapsed 98%CPU (0avgtext+0avgdata 0maxresident)k -#0inputs+0outputs (0major+853minor)pagefaults 0swaps - my $f = "$file"; - my $tmp = $file_name_to_use; - $tmp =~ s/(.*)[.].*/$1\_time.txt/; - $f =~ s/(.*)\/.*/$1\/$tmp/; - if (-e "$f"){ - open (A, "$f") or die "Cannot open time file $f\n"; - my @a = ; - close A; - - my $a = join ("",@a); - $a =~ s/\n//g; - if ($a =~ m/([0-9]+[.]*[0-9]*)user ([0-9]+[.]*[0-9]*)system ([0-9]+[:][0-9]+[.:]*[0-9]*)elapsed ([0-9]+[.]*[0-9]*).CPU .0avgtext.0avgdata 0maxresident.k0inputs.0outputs .([0-9]+)major.([0-9]+)minor.pagefaults ([0-9]+)swaps/){ - my $out = ",$1,$2,$3,$4,$5,$6,$7"; - print OUT $out; - } - else{ - print "Unable to match\n'$a'\n"; - exit(1); - } - - } - else{ - print "ERROR: Cannot find time file '$f'\n"; - exit(1); - } - -} - -sub check_correctness{ - my $filename = shift; - $filename = "$filename.dot"; - - if (not -e "$filename"){ - print "ERROR: unable to check correctness for non exsistant? file '$filename'\n"; - exit(1); - } - open (IN, "$filename") or die "I cannot check correctness for $filename\n"; - - my @in = ; - close IN; - - my $in = join ("",@in); - - $in =~ s/sp_//g; - - my $not_found_arcs = 0; - my $correct_arcs = 0; - my $wrong_influence_arcs = 0; - my $extra_arcs = 0; - - while ($in =~ m/s([0-9]+) -> s([0-9]+) (.*)/g){ - my $left = $3; - #print "Matched with '$left'\n"; - if ($left =~ m/black/){ #should not have been reported - $extra_arcs++; - #$precision_total++; - } - elsif ($left =~ m/dotted/){ #It is there, but not found - $not_found_arcs++; - #$recall_total++; - } - elsif ($left =~ m/dashed/){ #wrong influence type - print "Extra arcs"; - $wrong_influence_arcs++; - #$precision_total++; - #$recall_total++; - } - else{ #there and reported - $correct_arcs++; - #$precision_correct++; - #$precision_total++; - #$recall_correct++; - #$recall_total++; - } - } - - my $num_genes = 0; - while ($in =~ m/s[0-9]+ \[/g){ - $num_genes++; - } - my $total_possible_arcs = $num_genes * ($num_genes-1); - my $total_influence_arcs = $not_found_arcs + $wrong_influence_arcs + $correct_arcs; - my $total_absent_arcs = $total_possible_arcs - $total_influence_arcs; - my $correct_absent_arcs = $total_absent_arcs - $extra_arcs; - - - my $r_c = $correct_arcs; - my $r_t = $total_influence_arcs; - my $p_c = $correct_arcs; - my $p_t = $correct_arcs + $wrong_influence_arcs + $extra_arcs; - return ($r_c,$r_t,$p_c,$p_t); - -} - -sub dir_sort_b{ - my $a = shift; - my $b = shift; - if ($a =~ m/(.*?)_([0-9]+)_/){ - my $n1 = $1; - my $i1 = $2; - if ($b =~ m/(.*?)_([0-9]+)_/){ - my $n2 = $1; - my $i2 = $2; - if ($n1 eq $n2 and $1 != $i2){ - return $i1 <=> $i2; - } - } - } - return $a cmp $b; -} - -sub dir_sort_a{ - #order is 0 5 4 3 1 - 2 is the # genes which should match - # cmp = = = = - my $a = shift; - my $b = shift; - - if ($a =~ m/^(.*)_([0-9]+)\/[^\/]*_([0-9]+)_([0-9]+)_([0-9]+)_([0-9]+)\//){ - my @a = ($1,$2, $3, $4, $5,$6); - if ($b =~ m/^(.*)_([0-9]+)\/[^\/]*_([0-9]+)_([0-9]+)_([0-9]+)_([0-9]+)\//){ - my @b = ($1,$2, $3, $4, $5, $6); - if ($a[0] eq $b[0]){ - my @cmp = (5,4,3,1); - for (my $i = 0; $i <= $#cmp; $i++){ - my $ind = $cmp[$i]; - if (not ($a[$ind] == $b[$ind])){ - return $a[$ind] <=> $b[$ind]; - } - } - return $a[2] <=> $b[2]; - } - else{ - return $a[0] cmp $b[0]; - } - } - } - return dir_sort_b($a,$b); -} - -sub dir_sort{ - my $d = shift; - my @d = @$d; - @d = sort {dir_sort_a($a,$b)} (@d); - return @d; -} - - - -main(); - diff --git a/bin/iBioSim b/bin/iBioSim deleted file mode 100755 index e053a0957..000000000 --- a/bin/iBioSim +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# -# run-time wrapper for BioSim gui -export DYLD_LIBRARY_PATH=$BIOSIM/lib64:$DYLD_LIBRARY_PATH -CLASSPATH=$BIOSIM/gui/dist/classes:$BIOSIM/gui - -for jarFile in $BIOSIM/gui/lib/*.jar -do - CLASSPATH=$CLASSPATH:$jarFile -done - -exec java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath $CLASSPATH -Dapple.laf.useScreenMenuBar=true -Xdock:name="iBioSim" -Xdock:icon=$BIOSIM/gui/icons/iBioSim.jpg main.Gui diff --git a/bin/iBioSim.linux b/bin/iBioSim.linux deleted file mode 100755 index 965ee7201..000000000 --- a/bin/iBioSim.linux +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -export LD_LIBRARY_PATH=$BIOSIM/lib:$LD_LIBRARY_PATH -CLASSPATH=$BIOSIM/gui/dist/classes - -for jarFile in $BIOSIM/gui/lib/*.jar -do - CLASSPATH=$CLASSPATH:$jarFile -done - -exec java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath $CLASSPATH main.Gui diff --git a/bin/iBioSim.linux64 b/bin/iBioSim.linux64 index 34cd4e4d0..2c2085d48 100755 --- a/bin/iBioSim.linux64 +++ b/bin/iBioSim.linux64 @@ -1,10 +1,5 @@ #!/bin/bash # -source detect-java.sh -if [ -z "$JAVA_HOME" ]; then - echo "Must install Java JDK Version 1.7 or later" - exit -fi if [ -z "$BIOSIM" ]; then export BIOSIM=$PWD/.. fi diff --git a/bin/iBioSim.mac b/bin/iBioSim.mac deleted file mode 100755 index c5feafe35..000000000 --- a/bin/iBioSim.mac +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# -# run-time wrapper for BioSim gui -export DYLD_LIBRARY_PATH=$BIOSIM/lib:$DYLD_LIBRARY_PATH -CLASSPATH=$BIOSIM/gui/dist/classes - -for jarFile in $BIOSIM/gui/lib/*.jar -do - CLASSPATH=$CLASSPATH:$jarFile -done - -exec java -Xmx2048M -Xms2048M -XX:+UseSerialGC -classpath $CLASSPATH -Dapple.laf.useScreenMenuBar=true -Xdock:name="iBioSim" -Xdock:icon=$BIOSIM/gui/icons/iBioSim.jpg main.Gui diff --git a/bin/iBioSim.mac64 b/bin/iBioSim.mac64 index 6d6c21dcf..461b73e95 100755 --- a/bin/iBioSim.mac64 +++ b/bin/iBioSim.mac64 @@ -3,11 +3,6 @@ cd -- "$(dirname "$0")" if [ -z "$BIOSIM" ]; then export BIOSIM=$PWD/.. fi -source $BIOSIM/bin/detect-java.sh -if [ -z "$JAVA_HOME" ]; then - echo "Must install Java JDK Version 1.7 or later" - exit -fi export PATH=$BIOSIM/bin:/usr/local/lib:$PATH export DYLD_LIBRARY_PATH=/usr/local/lib:$DYLD_LIBRARY_PATH diff --git a/bin/parg b/bin/parg deleted file mode 100755 index 29be215f2ad37c9e34baf396255ace0463e53b30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60364 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

OVYA70 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]... - or typically - g++ [option]... .c -M | $progname > .d - -Expand prerequisite output from a copiler with the -M (or -MM) flag -such that the .d file will depend only on the prerequisite files that -currently exist, but the object file and the TAGS file will depend on -all the prequisites. This is a modification of the strategy at -info:make\#Automatic_Prerequisites or -http://www.gnu.org/manual/make/html_node/make_47.html#SEC51 - -In the modified form: - -The practice we recommend for automatic prerequisite generation is to -have one makefile corresponding to each source file. For each source -file "name.c" there is a makefile "name.d" which lists what files the -object file "name.o" depends on. That way only the source files that -have changed need to be rescanned to produce the new prerequisites. - -Here is the pattern rule to generate a file of prerequisites (i.e., a -makefile) called "name.d" from a C source file called "name.c": - - - %.d: %.c - \@set -e; rm -f \$@; \ - \$(CC) -M \$(CPPFLAGS) \$< > \$@.\$\$\$\$; \ - $progname \$@.\$\$\$\$ > \$@; \ - rm -f \$@.\$\$\$\$ - -See section 10.5 Defining and Redefining Pattern Rules, for -information on defining pattern rules. The "-e" flag to the shell -causes it to exit immediately if the \$(CC) command (or any other -command) fails (exits with a nonzero status). - -With the GNU C compiler, you may wish to use the "-MM" flag instead of -"-M". This omits prerequisites on system header files. See section -"Options Controlling the Preprocessor" in Using GNU CC, for details. - -The purpose of the $progname command is to translate (for example): - - main.o : main.c defs.h - -into: - - main_prereqs = main.c defs.h - main.o TAGS: \${main_prereqs} - main.d: \$(wildcard $PROGRAM_NAME \${main_prereqs}) - -This makes each ".d" file depend on the currently existing source and -header files that the corresponding ".o" file depends on. make then -knows it must regenerate the prerequisites whenever any of the source -or header files changes. - -Once you havve defined the rule to remake the ".d" files, you then use -the include directive to read them all in. See section 3.3 Including -Other Makefiles. For example: - - - sources = foo.c bar.c - - include \$(sources:.c=.d) - -Options: - - -h, --help Print this usage information and exit. - -Questions? Comments? Bug reports? mailto:peskin\@vlsigroup.ece.utah.edu - -EOH; -############################################################################### -$help = ''; -GetOptions('help' => \$help) or die $usage; -if($help){ - print $usage; - exit; -} - -$OUTPUT_FIELD_SEPARATOR = "\n"; -$OUTPUT_RECORD_SEPARATOR = "\n"; - -local $INPUT_RECORD_SEPARATOR; -<> =~ /(\S+)\.o\s*:\s*/ or die "Could not find prerequisites"; -print "$1_prereqs=$POSTMATCH"; -print "$1.o TAGS: \${$1_prereqs}"; -print "$1.d: \$(wildcard $PROGRAM_NAME \${$1_prereqs})" diff --git a/bin/printnet b/bin/printnet deleted file mode 100755 index df5df3542..000000000 --- a/bin/printnet +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -open $1.dot - diff --git a/bin/remoteATACS.sh b/bin/remoteATACS.sh deleted file mode 100755 index 42bb6a896..000000000 --- a/bin/remoteATACS.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -#******************************************************************************* -# -# This file is part of iBioSim. Please visit -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* -scp $3.lpn $1:/tmp/. -ssh $1 "atacs $2 /tmp/$3.lpn" -scp $1:"atacs.log" . -scp $1:/tmp/$3.prg . -ssh $1 "rm /tmp/$3.prg" diff --git a/bin/remoteTangATACS.sh b/bin/remoteTangATACS.sh deleted file mode 100755 index 1cf6a85d3..000000000 --- a/bin/remoteTangATACS.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -#******************************************************************************* -# -# This file is part of iBioSim. Please visit -# for the latest version of iBioSim. -# -# Copyright (C) 2017 University of Utah -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the Apache License. A copy of the license agreement is provided -# in the file named "LICENSE.txt" included with this software distribution -# and also available online at . -# -#******************************************************************************* -scp $2.lpn tang.ece.utah.edu:/tmp/. -ssh tang.ece.utah.edu "/home/tang/myers/BioSim/bin/atacs $1 /tmp/$2.lpn" -scp tang.ece.utah.edu:"atacs.log" . -scp tang.ece.utah.edu:/tmp/$2.prg . -ssh tang.ece.utah.edu "rm /tmp/$2.prg" diff --git a/gui/src/main/java/edu/utah/ece/async/ibiosim/gui/Gui.java b/gui/src/main/java/edu/utah/ece/async/ibiosim/gui/Gui.java index b990fcdff..7d74a5792 100644 --- a/gui/src/main/java/edu/utah/ece/async/ibiosim/gui/Gui.java +++ b/gui/src/main/java/edu/utah/ece/async/ibiosim/gui/Gui.java @@ -8964,14 +8964,14 @@ public class Gui implements Observer, MouseListener, ActionListener, MouseMotion } if (exitValue != 255 && exitValue != -1) { SBMLutilities.reb2sacFound = false; - System.out.println("ERROR: " + reb2sacExecutable + " not found."); + System.out.println("ERROR: " + reb2sacExecutable + " not functional."); } } catch (IOException e) { SBMLutilities.reb2sacFound = false; - System.out.println("ERROR: " + reb2sacExecutable + " reb2sac not found."); + System.out.println("ERROR: " + reb2sacExecutable + " not found."); } catch (InterruptedException e) { SBMLutilities.reb2sacFound = false; - System.out.println("ERROR: " + reb2sacExecutable + "reb2sac not found."); + System.out.println("ERROR: " + reb2sacExecutable + " throws exception."); } exitValue = 1; try { @@ -9009,14 +9009,14 @@ public class Gui implements Observer, MouseListener, ActionListener, MouseMotion } if (exitValue != 255 && exitValue != 134 && exitValue != -1) { SBMLutilities.geneNetFound = false; - System.out.println("ERROR: " + geneNetExecutable + " not found."); + System.out.println("ERROR: " + geneNetExecutable + " not functional."); } } catch (IOException e) { SBMLutilities.geneNetFound = false; System.out.println("ERROR: " + geneNetExecutable + " not found."); } catch (InterruptedException e) { SBMLutilities.geneNetFound = false; - System.out.println("ERROR: " + geneNetExecutable + " not found."); + System.out.println("ERROR: " + geneNetExecutable + " throws exception."); } new Gui(lemaFlag, atacsFlag, libsbmlFound); }