Pystra Timing#
This notebook examines the speed up possible when using the built-in distribution function implementations, rather than the generic (but more comprehensive) ones provided by SciPy. Where performance is critical, implementation of a user-defined distribution object that avoids error checks and user input validation on each call could be beneficial.
A typical output is (the results will differ by machine):
```
Total time taken (s)
Built-in: 1.8919757150579244; Scipy: 5.2964311360847205
Average time per call (s):
Built-in: 0.018919757150579242; Scipy: 0.052964311360847206
Built-in speed-up: 2.80
```
Import the relevant packages (some may need to be installed as they are not part of the Pystra
installation)
[1]:
import pystra as pr
from scipy.stats import norm, lognorm, uniform
import numpy as np
import timeit
Define a reasonably complex limit state function
[2]:
def lsf(r, X1, X2, X3):
g = r - X2 * (1000 * X3) ** (-1) - (X1 * (200 * X3) ** (-1)) ** 2
return g
Define some objects common to both sets of analyes:
[3]:
limit_state = pr.LimitState(lsf)
options = pr.AnalysisOptions()
options.setPrintOutput(False)
The analysis using the distributions built in to Pystra:
[4]:
def run_builtin():
"""
The basic example using built-in distributions
"""
stochastic_model = pr.StochasticModel()
stochastic_model.addVariable(pr.Lognormal("X1", 500, 100))
stochastic_model.addVariable(pr.Normal("X2", 2000, 400))
stochastic_model.addVariable(pr.Uniform("X3", 5, 0.5))
stochastic_model.addVariable(pr.Constant("r", 1))
stochastic_model.setCorrelation(
pr.CorrelationMatrix([[1.0, 0.3, 0.2], [0.3, 1.0, 0.2], [0.2, 0.2, 1.0]])
)
sorm = pr.Sorm(
stochastic_model=stochastic_model,
limit_state=limit_state,
analysis_options=options
)
sorm.run()
The analysis using the equivalent distributions in SciPy
[5]:
def run_scipy():
"""
The basic example using Scipy distributions
"""
stochastic_model = pr.StochasticModel()
# Lognormal
zeta = (np.log(1 + (100 / 500) ** 2)) ** 0.5
lamb = np.log(500) - 0.5 * zeta ** 2
stochastic_model.addVariable(
pr.ScipyDist("X1", lognorm(s=zeta, scale=np.exp(lamb)))
)
# Normal
stochastic_model.addVariable(pr.ScipyDist("X2", norm(loc=2000, scale=400)))
## Uniform
a_b = (0.5 ** 2 * 12) ** (1 / 2)
a = (2 * 5 - a_b) / 2
stochastic_model.addVariable(pr.ScipyDist("X3", uniform(loc=a, scale=a_b)))
# Constant
stochastic_model.addVariable(pr.Constant("r", 1))
stochastic_model.setCorrelation(
pr.CorrelationMatrix([[1.0, 0.3, 0.2], [0.3, 1.0, 0.2], [0.2, 0.2, 1.0]])
)
sorm = pr.Sorm(
stochastic_model=stochastic_model,
limit_state=limit_state,
analysis_options=options
)
sorm.run()
Now call and compare the timing results
[6]:
number = 100
time_builtin = timeit.timeit(stmt=run_builtin, number=number)
time_scipy = timeit.timeit(stmt=run_scipy, number=number)
print("Total time taken (s)")
print(f"Built-in: {time_builtin}; Scipy: {time_scipy}")
print("Average time per call (s):")
print(f"Built-in: {time_builtin/number}; Scipy: {time_scipy/number}")
print(f"Built-in speed-up: {time_scipy/time_builtin:.2f}")
Total time taken (s)
Built-in: 1.7366494107991457; Scipy: 6.116523395990953
Average time per call (s):
Built-in: 0.017366494107991456; Scipy: 0.061165233959909526
Built-in speed-up: 3.52