The Creation of Puffin, the Automatic Uncertainty Compiler

An uncertainty compiler is a tool that automatically translates original computer source code lacking explicit uncertainty analysis into code containing appropriate uncertainty representations and uncertainty propagation algorithms. We have developed an prototype uncertainty compiler along with an associated object-oriented uncertainty language in the form of a stand-alone Python library. It handles the specifications of input uncertainties and inserts calls to intrusive uncertainty quantification algorithms in the library. The uncertainty compiler can apply intrusive uncertainty propagation methods to codes or parts of codes and therefore more comprehensively and flexibly address both epistemic and aleatory uncertainties.


Introduction
Modern science and engineering is all about numerical calculation. With the inexorable growth of computer power, more of these calculations are being undertaken with ever more complex computer simulations. These developments mean that new computation-intensive technologies are being explored, such as digital twins (see [1,Sec. 2.2.3.3] or [2]). Scientists and engineers need to make calculations even when there is uncertainty about the quantities involved, yet the tools they are commonly using do not allow this to be done intrusively. As a result many analysts work with computer codes that do not take full account of uncertainties.
Within the numerical calculations essential to engineering, there are two types of uncertainty: aleatory and epistemic. Aleatory uncertainty arises from the natural variability in changing environments and material properties, errors in manufacturing processes or inconsistencies in the realisations of systems. Aleatory uncertainty cannot be reduced by empirical effort. Epistemic uncertainty is caused by measurement imperfections or lack of perfect knowledge about a system. This could be due to not knowing the full specification of a system in the early phases of engineering design or ignorance about the expected manufacturing variations or deployment conditions. Imperfect scientific understanding of the underlying physics or biology involved causes uncertainty in predictions about the future performance of a system even after the design specifications have been decided. If uncertainties are small they can

The Problem with Monte Carlo
The most common approach to deal with uncertainty is to wrap code within a Monte Carlo shell. In this approach the calculations are repeated with random values for selected input variables. This is done for a large number of iterations, and the distribution of resulting outputs can be analysed. Such tools exists in many programming langages: DAKOTA for C++ [3], COSSAN [4] and UQLab [5] for MATLAB or UQpy for Python [6]. Olivier et al. give an excellent overview of many more software packages that are availible for non-intrusive uncertainty quantification [6]. Under such an approach random values are chosen and then the calculations are performed and the output stored, this is done for a number of iterations and total outputs can be analysed after the process has been completed.
In order to demonstrate the potential problems with such an approach we can consider a simple example. Suppose we have five variables 1 , . . . , 5 which are known to all have a value between 0 and 1 but no further information is known about the values. Suppose we need to perform the calculation with the knowledge that some bad thing will happen if ≥ 4.5. A number can be randomly generated for 1 , 2 , etc and these can be used in order to calculate the value of for iterations. After this is complete we can plot a histogram to show the distribution for . Since we do not have any information about the distribution for 1 , . . . , 5 it seems sensible to assume that all values are equally likely and use a uniform distribution. Figure 1 shows these histograms for various . From this we can see that as → ∞ the histogram resembles a normal distribution. Whatever the number of replications used in the Monte Carlo simulation, we can estimate that the probability of the bad thing happening. With 10 6 replications, this estimate is Pr( ≥ 4.5) = 2.53 × 10 −4 . However, it seems reasonable to consider whether we have confidence that the event is so rare. We had no information about the distributions of the five values except that they were between 0 and 1. Nor did we have knowledge about what dependencies there might be between the variables. From this information we cannot rule out the possibility that each value is much more likely to be closer to 1 than 0, or that there is some dependence between the values such that if 1 is high then all the others are also likely to be high. Thus, the way that the uncertainty has been characterised may be significantly underestimating the risk [7]. There have been several engineering failures that were due in part to underestimating risks in ways similar to this example [7,8]. Before the 1986 Challenger Disaster, NASA management had predicted the probability of failure with loss of vehicle and crew as 1 in 10 5 flights [9]. This turned out to be a gross underestimation of the true risk, which after the retirement of the fleet stood at 2 in 135. The Fukushima Daiichi nuclear disaster was due in part to underestimating the risk of a tsunami of the magnitude that caused the disaster and in failing to understand that collocating the backup generators created dependence that destroyed the planned engineered redundancy when the site was flooded during the event [10, p. 48]. The probabilities of satellites colliding in orbit can be underestimated through the use of probabilities [11], leading to false confidence that they are not going to hit each other.
Performing uncertainty analysis by simply wrapping a simulation code in a Monte Carlo loop may not give a full account of the uncertainties that are present within a simulation. The probabilities of extreme events are especially difficult to correctly estimate when either the distributions of input variables are not known or any inter-variable dependencies are not known. There are other limitations of this simplistic Monte Carlo approach, including false confidence [11], and problems arising from confounding epistemic and aleatory uncertainties [12].

Puffin
Strategies are needed that automatically translate original source code into code with appropriate uncertainty representations and propagation algorithms. Perez et al. introduced a MATLAB toolbox to perform automatic uncertainty propagation using unscented transform, however more general approaches are needed [13]. In this paper we describe an uncertainty compiler for this purpose, named Puffin, along with an associated language. It handles the specifications of input uncertainties and inserts calls to an object-oriented library of intrusive uncertainty quantification (UQ) algorithms. In theory, the approach could work with any computer language and any flavour of uncertainty propogation. There are several components that are needed for the creation of Puffin as shown in Figure 2.
Puffin needs a language of uncertainty, "Puffin Language", that allows users to specify what uncertainties should be associated with the variables within the source code. This language should be simple and independent of the source language. For every source language that Puffin is to work with, there has to be an intrusive UQ library that Puffin Language can be translated into. Puffin Language does not need to be a textual programming language, it could instead be a visual language as part of a graphical user interface. Every type of uncertainty that is expressible in Puffin Language must be supported by an object constructor in the uncertainty library written in the source language. Section 4 discusses this Puffin Language component.
The other component that Puffin needs is a transcompiler [14] that translates a user's source code into an UQ enriched code expressed in the same language. There are three subcomponents to this: a Reader that is able to read the source language, a Translator that is able to read the specified uncertainties in Puffin Language, and a Writer that The different components of Puffin and the UQ library it depends on. For each source language that Puffin is able to read, the parts highlighted in red need to be created. The intrusive uncertainty quantification library and Puffin Language need to mirror each other with a direct translation being available for all uncertainty specifications.
combines the results of both to output a new script with the specified uncertainties. ANTLR, a parser/lexer generator, can be used to generate the Reader and Writer [15]. ANTLR requires a grammar specification for the source language. Fortunately, ANTLR grammar files have been defined for many popular programming languages [16] and these can be used as a starting point. The Reader scans the input script and identifies the assignment operators within that may have uncertainties that need to be specified. The Translator reads Puffin Language specifications created by the user and translates the uncertainties into the source language. For each source language these conversions need to be specified. The Writer reproduces the script with the required uncertainties and necessarily changes required for the analysis to run without issue. We have designed these components for Python.

Puffin Language
Puffin depends on an uncertainty language to express what uncertainties are present within their scripts. This language enables users to specify the uncertainties about the variables involved in their code before compiling it into a new script with UQ enriched code. The language currently enables calculations with five types of uncertain objects that have relevance in engineering [12]: • Interval: unknown value or values for which sure bounds are known [17], • Probability distribution: random values varying according to specified law such as normal, uniform, binomial, etc., with known parameters [18], • Probability box: random values for which the probability distribution cannot be specified exactly but can be bounded [19], • Confidence structure: inferential uncertainty about a constant parameter compatible with both Bayesian and frequentest paradigms [20], and • Natural language expressions: uncertain values indicated by linguistic hedge words such as 'about 7.2' or 'at most 12' [21,22,23].

Intervals
An interval is an uncertain number representing values from an unknown distribution over a specified range, or perhaps a single value that is imprecisely known even though it may in fact be fixed and unchanging. Intervals thus embody epistemic uncertainty. Intervals can be specified by a pair of scalars corresponding to the lower and upper bounds of the interval, such as [0, 1] or [4,5]. They can also be expressed as a value plus or minus some error, such as [5 ± 2] which is equivalent to [3,7].
Interval arithmetic computes with ranges of possible values, as if many separate calculations were made under different scenarios. However, the actual computations made by the software are done all at once, so they are very efficient. Basic binary operations (+, −, ×, ÷) can be performed using interval arithmetic: and Intervals can be propagated through all common mathematical functions such as exp, sin, log, etc. This is relatively straightforward if the function is monotonic as this implies that the endpoints of the input interval correspond to the endpoints of the output interval. For example, when calculating the exponential of an interval, For a non-monotonic function such as sine it is not necessarily the case that the endpoints of the interval correspond the the endpoints of the output function. For example, it is not the case that

Probability Distributions and Probability Boxes
A probability distribution is a mathematical function that gives the probabilities of occurrence for different possible values of a random variable.
Probability bounds analysis integrates interval analysis and probability distributions using probability boxes (pboxes) [19]. They can be considered as interval bounds on a probability distribution, therefore one can think of a probability distribution as a special case of a p-box. Figure 3 shows an example of a probability distribution. P-boxes characterise both epistemic and aleatory uncertainty. A p-box can be expressed mathematically as where ( ) is the function that defines the left bound of the p-box (the blue in Figure 3) and ( ) defines that right bound of the p-box (the orange line in Figure 3) As with intervals, standard arithmetic operations can be performed on p-boxes (and therefore probability distribu- where Within a programming language a p-box can be expressed by using the name of the probability distribution, or some shorthand for the name, as the function and the arguments as intervals. For example, the p-box shown in Figure 3 could be generated using [1,2]).
P-boxes can be defined in situations where the shape of the distribution is unknown but some empirical evidence about the data is known, such as the minimum, maximum, mean, standard deviation, etc. In this situation bounds can be created such that they are consistent with all the available information [37].

Confidence Boxes
Confidence boxes (c-boxes) are imprecise generalisations of traditional confidence distributions, which, like Student's -distribution, encode frequentist confidence intervals for parameters of interest at every confidence level [20,38]. They are analogous to Bayesian posterior distributions in that they characterise the inferential uncertainty about distribution parameters estimated from sparse or imprecise sample data, but they have a purely frequentist interpretation that makes them useful in engineering because they offer a guarantee of statistical performance through repeated use.
Unlike confidence intervals which cannot usually be used in mathematical calculations, c-boxes can be propagated through mathematical expressions using the ordinary machinery of probability bounds analysis, and this allows analysts to compute with confidence, both figuratively and literally, because the results also have the same confidence interpretation [39]. For instance, they can be used to compute probability boxes for both prediction and tolerance distributions.
Confidence boxes can be computed in a variety of ways directly from random sample data. There are c-boxes both for parametric problems (where the family of the underlying distribution from which the data was randomly generated is known to be normal, binomial, Poisson, etc.), and for non-parametric problems in which the shape of the underlying distribution is unknown. C-boxes account for the uncertainty about a parameter that comes from the inference about observations, including the effect of small sample size, but also the effects of imprecision in the data and demographic uncertainty which arises from trying to characterise a continuous parameter from discrete data observations. For example, it is possible to specify a c-box in the binomial case of having successes out of trials, based upon Clopper-Pearson confidence intervals [34,40,41]. This -out-of-c-box is specified as

Natural Language Uncertainty
In order to make uncertainty analysis as simple as possible, users should be able to input their uncertainties using natural language expressions such as about or almost. Humans are more likely to express their uncertainties in terms of hedged expressions around a round number, rather than as a percentage or probability. Table 1 lists some hedge words and their possible interpretations. Hedge words can be interpreted as intervals, p-boxes [21], or consonant c-boxes [42].

Logical Operations with Uncertain Objects
When making decisions it is often the case that two values need to be compared with each other. Asking whether an observed value is greater than, equal to, or less than some threshold value is fundamental. For example, if a decision relies on some observed value being less than 1, when we know the value of accurately then it is easy to make such a comparison. However, if there is some uncertainty about the value of then this comparison may not be so easy. and with 0 and 1 denoting true and false respectively, and [0,1] being the Boolean equivalent of "I don't know". We can call [0,1] the dunno interval. This implies that we cannot say whether an uncertain value characterised by an interval is larger or smaller than another unless the interval is entirely greater or less than the other interval. For the equality comparison, when asking for equivalence between intervals it is never possible to say that one value is equal to another. We can introduce a new Boolean operator (===) to test for whether two uncertain numbers are equivalent in form, The dunno interval can be converted into a true Boolean using operators such as or so that we can get There are methods that are able to deal with more nuanced ways of using logical operations with intervals, see [43] as an example. There are also different logic systems such as fuzzy logic that could be used in order to make logical operations with uncertain numbers [44].

Repeated Variables and Dependency
When performing intrusive uncertainty analysis it would be ideal to always obtain best possible results that are guaranteed to bound the true value without overestimating the uncertainty. The uncertainty can be inflated or artifactually high if careful consideration of the dependence between, and repetition of, uncertain numbers is not undertaken. This problem appears to be ubiquitous to many, if not all, uncertainty calculi [12].
For example, if = [2, 3] and = [4,5], then × = [8,15]. However, if it were the case that and were oppositely dependent on each other, such that a low value of is always matched with a high value of , then × is the much narrower interval [10,12].
Repetition of variables can also artifactually inflate the amount of uncertainty present within the output. For but Although algebraically these two expressions should be equal, the uncertainty of + is greater than the uncertainty about ( + ). This is due to the fact that the uncertain variable is repeated within the former but appears only once in the latter. In essence the uncertainty about has been considered twice when performing the first calculation. For distributions and p-boxes, significant artifactual uncertainty reduction can be made if the dependence between the variables is known [36]. Figure 4 shows the result of adding two separate p-boxes, 4,6], [5,7]), together with different dependencies between and . The Fréchet bounds are used when the dependence between and is unknown, thus it is the most general case and is guaranteed to bound the correct answer. As such in Figure 4 the Fréchet bounds cover all the other dependencies, as it is the operation that is defined in equations 10, 11 and 12. Perfect, or comonotonic, is where there is perfect positive dependence between the two variables, with the highest possible correlation coefficient. Opposite, or countermonotonic, is perfect negative dependence between the two variable with the lowest possible correlation coefficient. Independence is where there is no dependence between the two variables. It should not be assumed that variables are independent unless this is known because wrongly assuming independence can lead to incorrectly reducing the amount of uncertainty and understating tail risks.
In general, dependence between uncertain quantities can be expressed through the use of correlation coefficients or copulas or bounds on copulas more generally [49,50,51,52]. This can include named copulas such as independence, opposite and perfect, as shown in Figure 4, or other copula families parameterised by a numerical correlation coefficient. Independence implies the correlation is zero, although zero correlation does not imply independence. Likewise, a correlation of one implies perfect dependence, but, depending on the copula family, perfect dependence may not imply correlation one. The symbol ≡ can be used to indicate that the variables are equal in value, i.e., equal in distribution and perfectly positively correlated. These dependencies can be stored within a matrix, such as that shown in Table 2. This matrix can be checked for feasibility by checking that it is positive semi-definite and that there are no conflicting dependencies within the table.
For example the matrix shown in Table 3 is not logically consistent for continuous variables. This is because a high value of x implies a high value of z, since they are positively dependent on each other. Meanwhile, a high z implies y must be low, due to their opposite dependence. This means there must also be dependence between x and y, not the independence as has been specified within the table.

Other issues
Aside from dependencies and sensitivity to repeated variables, there are other issues that distinguish simple deterministic calculations from uncertainty quantifications. For example, in uncertainty quantification analysts may need to consider ensembles and backcalculations.
Probability distributions describe properties or behaviours across a population of entities. Statisticians call such a population the "reference class" or "ensemble". Uncertainty quantification implicitly represents many calculations over interacting ensembles, and it can be extremely important to keep in mind what the values in a probability distribution represent. For instance, if the post-operative risks of prostatectomy is fifty percent erectile dysfunction, it would make a huge difference to a patient whether this means that 50% of his future attempts at sex fail or that 50% of patients are permanently impotent. Does a system reach 10% of criticality or does it reach criticality 10% of the time? Uncertainty quantifications that do not explicitly define what the distributions in an analysis represent in terms of their respective ensembles may be meaningless. Puffin allows users to annotate their codes to specify and document the ensemble described by any distribution or other uncertain quantity, although, in general, it is the responsibility of the analyst to ensure that the calculations used make sense.
Another wrinkle that makes uncertainty quantification different from its analogous deterministic calculations is the importance of backcalculation. Backcalculation is a mathematical operation for finding solutions to equations involving variability or uncertainty that guarantee some desired performance. Such problems are ubiquitous in engineering design. Backcalculation solves questions such as 1. What dimensional constraints on a component are necessary to ensure that it fits in its place in a machine given spatial tolerances?
2. How much propellant is needed to guarantee sufficient fuel given the mission contingencies and unforeseen variabilities?
3. How much shielding is needed on a spacecraft to ensure that the total ionizing radiation experienced inside the craft does not exceed some tolerable threshold, given that radiation in space varies over time in an imperfectly known way?
The Puffin UQ library has algorithms to solve backcalculations that involve intervals, distributions and p-boxes (when solutions exist), but it is the responsibility of the analyst to ensure that they are deployed appropriately to yield calculations that make sense in the engineering context.

Compiler
Puffin consists of its intrusive UQ library, a code inspector/editor, and an uncertainty compiler. Puffin's uncertainty compiler does five things: 1. Parses the input source code into expression tree(s),

Identifies the variables in any assignment operations,
3. Replaces or modifies some or all of these assignments in the expression trees according to options and specifications provided by the user, 4. Translates the expression trees, with amended assignments, into the target language equipped with its intrusive UQ library, and 5. Analyses the output code to detect repeated variables and other functional dependencies that affect calculations and suggests improvements for computing uncertainties.
In order to explain what is happening in these steps it is useful to consider a simple pseudocode script, shown in the top left corner of Figure 5. For step 1, the simple script has then been broken into a parse tree which can be seen in Figure 6. From this tree Puffin detects the assignment operators which define a variable. These include lines 1 and 2, the leaves highlighted magenta on the parse tree, but not those that assign a value based upon a mathematical expression (line 5), a function or directly from another variable (line 3). In theory, such variables could also be edited by the user, but Puffin assumes that only explicit assignments will have uncertainty. Once the assignments have been found they can be displayed in the Puffin language, shown in the top right panel of Figure 5 where the assignments can then be edited with the appropriate user-specified uncertainties. These uncertainties need to then be translated to the source language, along with the rest of the parse tree. This translation may include altering any functions that depend the amended variables. In this case, the infix operators in the definition of d in line 5 (+, ) have been identified within the parse tree and replaced with an explicit call to the UQ library functions (add, mul) which also have as an argument the dependence operation that is to be used.
The lower panel of Figure 5, the value 'f' of this argument corresponds to making no assumption about the intervariable dependence between a and b, and perfect dependence (comonotonity) between their product and the variable c.
Puffin should only highlight numeric objects, not characters, strings or other non-numeric classes. In strongly typed programming languages like C, FORTRAN, and Pascal, the problem of distinguishing numeric from other types of objects is easy. In Python, R or Julia, the type of any object is not detectable until runtime and can even change during execution. Puffin will also need to be able to recognise objects that are collections of numeric values such as lists or 6. Parse tree for the simple pseudocode script. Abbreviations: stmt -statement, asgmt -assignment, func -function, expr -expression. dictionaries. It will also need to be able to look at what is inside the lists and highlight those which have numeric objects within the list and allow of the individual objects to have uncertainties added. Alternatively it could be the case that the whole of the list has the same uncertainty, something which should be possible. Puffin can be run automatically without any user input at all. Under default settings, automatic uncertainty compilation replaces floating-point constants with intervals interpreted from the significant figures used in the source code assignments and uses that information as a proxy for the uncertainty (for an example see Figure 7). In this mode all the steps of the steps happen concurrently without requiring any further input from an end user. When using this mode the compiler will need to tread carefully around mathematical constants such as or for which there is no uncertainty. Ideally it would allow users to minimally specify what values are precise constants.

Control Flow and Functions
For loops and functions here are potential stumbling blocks for Puffin. Figure 8 shows a simple pseudocode script with a function and a for loop. The first for loop each i is simply just a control variable with start and end variable.
The individual value of i is irrelevant and as such would have no uncertainty about it. The second for loop is a 'for each' loop implying that the code needs to do something for each value within some iterable object, under this scenario it may be the case that there is uncertainty about the object within the list. In this case the code is setting the value of initial_velocity as each value within the the list, for each iteration of the for loop. In this case it may be the case that there is uncertainty within the object, in which case Puffin should recognise this and all users to change the code such that the objects within the list can have uncertainty added to them.
Puffin will also need to have a way of dealing with local variables within functions. For example, the function in Figure 8 has two local variables, s for the distance that the object travels and g for the acceleration due to gravity. It is conceivable that both of these variables have some uncertainty associated with them and as such Puffin should be able to detect the variables and offer the ability to edit them so that uncertainty is handled. This could be done using a dot notation, meaning that the local g can be accessed using calculateVelocity.g.
If statements and other logical control structures may also pose issues for Puffin. In line 16 of Figure  where an uncertain result should be handled differently to a certain true or false. All the code and variables are not necessarily contained within one script. For example, classes and functions are often placed in other files in order to improve readability or to avoid repetitions. Ideally Puffin would be able to parse several scripts at the same time and remember the context for all the individual objects. It is also often the case that scripts read data from other files when running. Under this scenario it may be difficult to use Puffin to express the uncertainty directly within the script, although import functions could be modified to add in the uncertainties. For instance, anytime a floating-point number is read from the file, its significant digits could be interpreted to specify an interval around the value. So, for example, the value '3.56' would be understood as the interval [3.555, 3.565].
Another approach might be to get Puffin to parse the data file and add the uncertainties in to the file directly. This would require changing the import function to be able to handle uncertain datafiles.
Many computers languages that are not purely functional support functions that specify their parameters with "call by reference" which means that the memory location of a value is passed to the function rather than a copy of the true value. This convention can allow the function to change the values of those parameters in the calling routine not just locally within the function. Python does this by default with objects more complicated than integers, floating-point number, and strings such as lists, dataframes and numpy arrays. Puffin will need to be careful in handling functions that use the call by reference method of passing argument.
The presence of uncertainty implies multiple function definitions might be useful. For instance sqrt applied to ranges that might include negative numbers could have three possible behaviours. Abnormal termination, for example Python's math.sqrt returns a domain error if passed a negative number; yielding imaginary results, such as Python's

Coping with Dependency and Repeated Variable Problems
When it comes to dealing with the issues of dependency and repeated variables there are a couple of approaches that could be used in order to help reduced the problems discussed in Section 4.6. The simplest approach from a Puffin perspective would be for the libraries within each language to be able to handle the dependencies directly. This could be done if each object kept track of what other objects it depends on and in what way. For the example that has been used in Figure 9-Encoding #1, the variable c would remember that it is dependent on the variables a and b and therefore on line 4 it would know what the correct arithmetic would be to ensure as little artifactual uncertainty as possible. Puffin would insert this dependence directly in the translation as shown with the grey text in the Encoding #1, then at run time the / and * operators would automatically invoke the correct algorithms that respect the dependencies between the variables detected. This approach would have demands on memory. It requires initialised variables to have dependencies specified or a default dependence if they are unspecified.
The other way of treating the dependence would be for Puffin to parse over the script in order to detect the dependencies directly at compile time. These dependencies can then be stored within a matrix as discussed in Section 4.6. This matrix would need to be accessible for an analyst to add in assumptions about dependencies not observable from the code. For example, if variables a and b are independent this cannot be directly inferred from the code and therefore Fréchet would be assumed unless the analyst stated otherwise by editing the matrix. In this scenario Puffin would have to replace any infix operators with function calls with the appropriate dependence. In Figure 9-Encoding #2, the multiplication a * b has been replaced by the multiply function specifying that Fréchet should be used for the dependencies. Similarly the division operator has been replaced by a function with the method defined as opposite.
Finally where it is possible Puffin should be able to rearrange the equations such that any repeated variables are removed. Such a method would require Puffin to have a directory of multi-use to single-use rearrangements as well as a way of matching the written code to the mathematical expression. Another, smarter, approach would be to have a symbolic algebra system that is able to rearrange to a single-use expression on the fly. The simplest version of this is for it to happen just across one line, for instance replacing c = a b+a c with c = a (b+c).
A more complex approach is to consider repetitions globally to try to reduce the repetitions by detecting repetitions that happen over multiple lines. For instance in the example code in Figure 9, there is a hidden repetition across lines 3 and 4 because = + . So This expression can be rearranged into the single-use expression = tan (arctan( ) + arctan( )) (24) which is the change made in Encoding #3. Such a transformation would be in the directory mentioned above.
Care would need to be taken to ensure that the right variable gets the rearrangement. Take the following kinematics equation to find the position of a particle at time where is the initial velocity and is the acceleration of the particle. This equation has a single repetition for both and but is repeated. If there is uncertainty associated with then this equation can be rearranged into a single-use This equation contains repetitions of and and as such may only be preferred if there is no uncertainty associated with either or . If there is uncertainty associated with either then it may be best not to perform the rearrangement or to intersect possible rearrangements to obtain the best possible expression.
There are additional issues that Puffin could face when rearranging equations. For example if the uncertainty about includes negative numbers then √ 2 is likely to be problematic. Alternatively, there could be problems if straddles 0 because this would result in a division by zero. A strategy for dealing with this may be to perform the calculations in using both Equation 25 and 26 and intersect them.

Hermeneutic Problems
There are several problems that could occur when it comes to translating a script because it is difficult to understand the intent of the programmer from the code. An example of this can be found in line 3 of the psuedocode example in Figure 5. There is potential for confusion when it comes to the assignment c = a as there are a couple of different interpretations as to what such a command implies when it comes to the uncertainty. The first is that we are implying that c and a are the same object but have ˜ Ê ò¢ á #˜ò-ûï õ ™ ûÍ ò˜Õè ïûèãäï ÄõÍ # -Ô™ õ ›òÍÔ™ AEá²µ- been given different names for some reason, under this scenario they should be considered equivalent to each other and therefore the calculation a + c could be rearranged to the single use expression 2 a. A second interpretation is to consider that the line could have been written as c = 1 a and the 1 has been dropped as it would have had no mathematical impact on the calculation, this implies that they are perfectly dependent on each other in the same way that c = -1 a implies negative dependence. The calculation a + c would therefore need to be performed using perfect dependence. A third interpretation would be to consider that it is saying that c is a copy of a, they have the same uncertainty but their realisations are not necessarily related to each other but they have the same distribution shape. In the third scenario it would be sensible to make no assumptions about the dependencies between a and c and therefore Fréchet should be used. Knowledge of which of these scenarios is correct depends on the context of the script, something which Puffin is unable to make an assumption about by itself. Another potential interpretation problem can occur because when creating code people naturally favour making their code readable. For example, the equation of motion for a damped harmonic oscillator can be given by where is a damping constant, is the mass of the oscillator and is the spring constant. This equation can be solved analytically to find where is a constant and 0 is the initial angle. In Figure 10 the equation has been coded in two different ways. In 10a the equation has been coded on a single line, as the equation is quite complicated it is likely that the programmer who is coding the equation would want to split it into multiple parts as has been done in 10b. There are no mathematical difference between the two approaches as they will lead to the same value. This paper has described several strategies to mitigate the complexities arising from these issues. It is also likely to be the case that Puffin would not be able to introduce the most perfect uncertainty translations. Manually editing code or creating a new uncertainty aware script from scatch will likely outperform the automatic changes made by Puffin. This problem would not be unique to Puffin however, in general hand coding is always likely to outperform source-to-source translation [58].
Puffin is almost the exact opposite of an optimising compiler, which aims to minimise a programme's execution time and memory requirements. By replacing objects with uncertain equivalents both of these will almost certainly increase.
For instance, if intervalising calculations increases the computational time 5-to 20-fold, clearly a simulation limited by computational time would need to be scaled back. Distributions or p-boxes would be still more burdensome. Of course, efficiency is not always a critical issue and this extra computational effort does pay for global uncertainty propagation and what computer scientists call automatic result verification [59]. Moreover, Puffin's implementation of modern uncertainty quantification could be more efficient and more comprehensive than simply embedding a deterministic computation inside a Monte Carlo shell with millions of replications.
It is unrealistic to expect that simply replacing computations involving integer and floating-point variables with analogous imprecise computations involving corresponding intervals, distributions and p-boxes will yield correct and useful results. The repeated variable problem and, more generally, the dependency problem, discussed in sections 4.6 and 5.2, can artifactually inflate the uncertainty of a complex computation. Even if the calculations are technically correct in the sense that they enclose the true uncertainty, the naive application of intrusive uncertainty quantification can sometimes yield results with massively inflated uncertainty that renders them practically useless. Having massive uncertainty is not the problem itself; the true uncertainty may actually be large. The problem is when it the uncertainty artifactually depends on the way the analysis was structured and does not reflect the features of the underlying computational problem.
The analysts who most need Puffin are those who have never heard of a p-box and who aren't sure what normal distributions or intervals are. Puffin offers multiple ways of specifying uncertainties in order to cater to the needs of such analysts. Puffin offers multiple ways of specifying uncertain inputs for user unfamiliar with the details that they require: • Significant-digit intervals • Measurement intervals (manufacturer or GUM conventions) • English-language hedge words ('about', 'less than', etc.)

• Poisson model counts
• Moment or moment-range specifications • Equivalent binomial count (k out of n confidence box) • Single-sample confidence intervals • Mean normal range (n & range normal distribution) • Fermi strategies • Distribution-free specification of p-boxes It is also possible to run Puffin in a way that doesn't require any inputs, transforming assignments into significant digit intervals. Irrespective of how the uncertainties are expressed within Puffin, it is worth remembering that uncertain garbage in will lead to uncertain garbage out.
The general problem of uncertainty analysis is hard and it is difficult to create software that comprehensively solves all these problems and the development if Puffin is likely to be difficult. However, many practical problems are simpler than the most general problem and when this is the case it would be extremely useful to use a tool like Puffin which is able to handle uncertainty analysis intrusively within code. Puffin is intended to be open source and to be continuously co-developed by an interested community, adding in functionality and extending it as fashionable programming languages change and uncertainty quantification techniques develop.

Code Availability
Puffin is currently in development, and the current version can be found on GitHub[60]. We welcome suggestions and collaborators via GitHub.