Migrating from 1.x to 2.0#
This guide takes a 1.x script to the 2.0 API. The checklist below covers the edits most scripts need, the sections after it give the details, and the Complete symbol map lists every renamed, replaced and removed name. For what 2.0 adds, see What’s new in PySTRA 2.0.
The outstanding feature PRs remain unmerged in 1.x and are reserved for 2.0. Their functionality is new in this release, including the Strong Maximum Test, copula/joint-distribution support, DDO/LQI refinements, and the SORM prerequisite fix. Active learning is also new in 2.0. Earlier feature-branch interfaces are development history, not released 1.x APIs to migrate from.
Upgrading a 1.x script#
Most 1.x scripts need the same few edits. Start with
python -m pystra.migrate, which makes the unambiguous ones and reports the
rest (Migration tools); each item below links to its details.
Imports.
import pystra as rastill works. Update imports of moved modules (Moved modules) and renamed classes, such asFormtoFORM(Renamed classes). An old name raises an error that names its replacement (Signposts for old names).Method names. Methods use snake_case:
addVariablebecomesadd_variableandsetCorrelationbecomesset_correlation.Distributions.
stdvbecomesstdandstartpointbecomesstart_point.input_type=Truegives way to native keywords, as inGumbel("Q", loc=8.9, scale=1.56)(Models and distributions).Options.
AnalysisOptionsbecomesFORMOptions,SORMOptionsorSimulationOptions, passed asoptions=(Options and constructors).Results.
run()returns a result record:form.getBeta()becomesresult.betaandform.showResults()becomesprint(result.summary())(Result records and Getters and printing).Randomness. Pass
rng=seedto a simulation instead of seeding NumPy’s global random state (Randomness).Correlation. Add every random variable before calling
set_correlation; the matrix is now validated (Correlation matrices).Failures. A nonconverged analysis raises
AnalysisError; passon_failure="return"to keep its record instead (Failure handling).Calibration. Explicit operations replace the
Calibrationclass (Code calibration using normalized reliability).
Results can differ from 1.x where 2.0 corrects a numerical error, and seeded simulations differ within sampling error; the Changelog lists the corrections.
Naming conventions in 2.0#
PySTRA 2.0 adopts PEP 8 throughout the public API. Three rules cover every public name, and they are applied uniformly rather than case by case:
Kind of name |
Convention |
Example |
|---|---|---|
Functions, methods, parameters, attributes, modules |
|
|
Classes |
|
|
Constants |
|
|
The acronym rule follows PEP 8’s Descriptive: Naming Styles, which states:
“When using acronyms in CapWords, capitalize all the letters of the acronym.
Thus HTTPServerError is better than HttpServerError.” Because PySTRA’s
domain is dense with acronyms — FORM, SORM, PCE, PC-Kriging, FBR, DDO, LQI,
SWTP — applying this consistently is what makes the API predictable: if you
know the acronym, you know how it is spelled in the code.
Note that the rule governs CapWords only. Functions, methods and module
names keep their acronyms lowercase, so run_form and pc_kriging are
correct and are not affected.
ScipyDist keeps its existing name. In 2.0, GEVmax remains available as
an alias for GEV; it is the one deliberate alias in the new API.
Extreme-value distributions are named by family rather than by type number.
The literature labels the three Fisher–Tippett limits with Roman numerals —
Type I (Gumbel), Type II (Fréchet) and Type III (Weibull), all special cases of
the generalized extreme value (GEV) distribution — and PySTRA 1.x followed it
with names such as TypeIlargestValue. In 2.0 the class takes the family
name. The bare name is the conventional flavour, maxima for Gumbel and
Frechet and minima for Weibull, and the opposite flavour takes a
suffix, as in GumbelMin and GEVMin. Frechet is the ASCII spelling of Fréchet.
Implemented naming changes#
Functions and methods now use snake_case. Inconsistent class names follow the
project’s CapWords convention. Apart from GEVmax, there are no aliases for the replaced names; old names
and module paths raise errors that name their replacements (see Signposts for old names).
Modules moved into subpackages (see Moved modules). Calibration is now a package
with explicit exports; its structural changes are described below.
Renamed classes#
The classes below were renamed in 2.0. Apart from GEVmax, which remains an
alias for GEV, there are no aliases; an old name raises an error that names its replacement
(see Signposts for old names). TypeIlargestValue and TypeIIIsmallestValue duplicated
Gumbel and Weibull exactly, so those classes are removed and the
existing names used instead.
1.x |
2.0 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Classes added during 2.0 development follow the same rule: FORMResult,
PCESurrogate, PCECandidate, PCEFitResult, PCKrigingSurrogate,
PCKrigingFitResult and FBRLearning. Earlier development snapshots
spelled them in title case (FormResult, PceSurrogate and so on) and
numbered the extreme-value classes (Type1LargestValue and so on); those
spellings were never released.
Moved modules#
Modules are grouped into subpackages in 2.0. Code that imports from the
top-level pystra namespace is unaffected; code that imports a module path
directly needs the new path.
1.x module |
2.0 module |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Private numerical helpers in |
Modules added during 2.0 development moved with them: copula and joint
to pystra.dependence; system_form and strong_maximum to
pystra.reliability; system to pystra.systems; ddo to
pystra.decision; and fbc into pystra.loads. Distribution modules with compound names now use
snake_case, for example pystra.distributions.shifted_lognormal; import
the classes themselves from pystra or pystra.distributions.
Top-level namespace#
import pystra gives the everyday modeling classes, distributions,
dependence models, reliability methods, systems, load processes and result
types, options and the error classes: 70 names, listed in pystra.__all__. Every module declares its own
__all__, and nothing else leaks into the namespace. Specialized workflow
tools are imported from their subpackage:
code calibration:
pystra.calibration(CodeCalibration,solve_designs,derive_factorsand so on);design decisions and target reliability:
pystra.decision(DDO,LQI,SWTP,TargetReliabilityand so on);active-learning components:
pystra.active_learning.ActiveLearningitself is also available aspystra.ActiveLearning.
These tools are new in 2.0. Using one of their names at the top level raises an error that names the subpackage to import it from.
Signposts for old names#
Apart from GEVmax, 2.0 keeps no aliases for replaced names, but it tells you
where each one went:
pystra.Formand every other 1.x name raise anAttributeErrorthat names the replacement, for example “PySTRA 2.0 renamed Form to FORM: use pystra.FORM”.import pystra.formandfrom pystra.form import ...raise anImportErrorthat names the new module.from pystra import Formraises Python’s ownImportError: Python replaces a package’s message with its generic one for this form of import. It adds a “Did you mean” suggestion when the new name is similar, as it is forFORM.
The messages are generated from the migration records by
scripts/generate_signposts.py. They will be removed in 3.0.
Errors#
PySTRA raises its own exception types. An invalid model, distribution or input
specification raises pystra.ModelError, which is also a ValueError; in 1.x
these checks raised a bare Exception. An analysis that cannot produce a valid
result raises pystra.AnalysisError, which is also a RuntimeError. Both
derive from pystra.PystraError.
1.x |
2.0 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The class names SystemFORM, FBCProcess,
ScipyDist, DDO, DDOCriterion, DDOObjective, LQI, SWTP,
SWTPRecord, and SWTPIndexRecord retain their original spellings in the
source and feature contributions. This does not imply that every class was
available in a published 1.x release. Acronyms in
CapWords retain their capitals, as recommended by
PEP 8.
Form and Sorm became FORM and SORM under the same rule, and
the extreme-value classes are now named by family (see Renamed classes).
The naming map records the
reviewed spelling changes and retained class names. The definition manifest
maps all 645 inventoried definitions, including private helpers. The
baseline inventory records
signatures, assigned attributes, dependency versions, and observed exports.
Inventory inclusion does not imply that an internal helper or an incidental
third-party export is a supported public API.
The baseline includes unmerged feature PRs; its 1.6.0 version label does
not make their interfaces part of the released 1.x API. For new joint
distributions, use joint.make_transformation(...) directly.
The distribution package now explicitly exports PySTRA classes. Import NumPy
and SciPy helpers directly from their own packages. Previously leaked SciPy
objects no longer shadow the beta, gamma, gumbel, uniform, and
weibull distribution modules.
Current example#
This example uses the current API and is executed by the documentation checks.
import pystra as ra
model = ra.StochasticModel()
model.add_variable(ra.Normal("R", mean=10, std=1))
model.add_variable(ra.Normal("S", mean=5, std=1))
limit_state = ra.LimitState(lambda R, S: R - S)
form = ra.FORM(
model=model,
limit_state=limit_state,
)
result = form.run()
print(f"beta = {result.beta:.4f}; converged = {result.converged}")
beta = 3.5355; converged = True
Options and constructors#
AnalysisOptions is removed. Each analysis takes one frozen settings object
through its options keyword:
Settings |
Used by |
|---|---|
|
|
|
|
|
|
Unknown fields are rejected, values are validated when the object is created,
and the defaults are those of 1.x. Derive modified settings with
dataclasses.replace(). A simulation method rejects a setting that it
does not use, such as target_cov for line sampling.
Earlier API (including intermediate development spellings) |
2.0 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Removed: analyses do not print; use |
|
|
transf_type, Ro_method, flag_sens, Recorded_u, Recorded_x,
ffdpara_thetag, sim_point, multi_proc and random_generator had
no effect and are removed.
Constructors take the model and limit state first, then keyword-only
settings: FORM(model, limit_state, *, options=None).
# Earlier development API (removed)
options = ra.AnalysisOptions()
options.set_imax(50)
form = ra.FORM(stochastic_model=model, limit_state=limit_state,
analysis_options=options)
The equivalent current constructor is checked with the example above:
form = ra.FORM(model, limit_state, options=ra.FORMOptions(max_iterations=50))
SensitivityAnalysis(model, limit_state, *, options=None, method="numerical",
delta=0.01) takes the method and step that were run() arguments; use
method="closed_form" for run(numerical=False). SystemFORM(model,
system, ...) takes the model first. Both reverse the 1.x argument order, and
run_form() is removed. The Monte Carlo classes no longer take
analysis_options first.
SORM, importance sampling and line sampling no longer run FORM when
constructed; run() does. Pass a completed analysis as form= to reuse its
design point. SORM then uses that analysis’s settings and rejects a
non-default SORMOptions.form. When importance or line sampling runs FORM
itself, it uses the default FORM settings with its own block size and
transformation; to use others, such as direct differentiation, run FORM first
and pass it.
An internally generated FORM analysis is recomputed on every run. A supplied FORM must have converged for the same model and limit-state expression, with unchanged distribution parameters, dependence and FORM options. Rerun FORM after changing those inputs. Importance and line sampling must also use the same reference transformation and conditioning order as the supplied FORM. Unknown custom distribution state causes FORM to be recomputed before reuse. Changes inside an external solver, a callable closure or other external data cannot be detected automatically: rerun FORM after those changes too.
Assigning analysis.form = completed_form after construction selects that
FORM analysis for SORM, LineSampling and ImportanceSampling;
assigning None restores a freshly computed FORM on each run. A supplied
analysis must still match the model, limit-state expression and coordinates.
LimitState.evaluate_lsf(x, model, *, differentiation="no",
ffd_parameter=1000, block_size=1000) takes explicit settings instead of an
options object.
Randomness#
Crude Monte Carlo, importance sampling, distribution analysis, line sampling
and subset simulation drew from NumPy’s global random state, so
np.random.seed controlled them. They now take rng, as do
StrongMaximumTest and ActiveLearning, where it replaces seed:
# 1.x
np.random.seed(2026)
analysis = ra.CrudeMonteCarlo(...)
# 2.0
analysis = ra.CrudeMonteCarlo(model, limit_state, rng=2026)
rng accepts a seed, a numpy.random.Generator or None. An integer
seed recreates the same stream on every run(); a generator advances its own
state, so successive runs differ; None draws fresh entropy. The global
random state is neither used nor changed. The streams differ from 1.x, so a
seeded 1.x estimate is not reproduced exactly, but agrees within its sampling
error. A StrongMaximumTest given an integer seed now repeats its sphere
sample on each run instead of advancing.
Maximum and MaxParent now compute their moments deterministically, by
Gauss-Hermite quadrature of the quantile function in standard normal space,
with exact identities for Fréchet and uniform parents. They no longer consume
100 global random draws on construction. Successive quadrature estimates are
compared at relative tolerance 1e-7 and absolute tolerance 1e-9 in
standardized mean and variance. A missed tolerance emits RuntimeWarning and
keeps the finest finite estimate, which serves for start points and
finite-difference steps; it does not reject an otherwise valid distribution.
Nonfinite estimates and nonpositive variance remain errors. Moment values and
default start points can therefore change from the old random estimates. Normal CDF and quantile calculations now
retain lower-tail probabilities that the former error-function subtraction
rounded to zero.
Marginal transformations now evaluate each tail on the side where its
probability is small, and use log probabilities where probabilities underflow
(Probability transformations and copulas). Transformed points up to \(u = 3\) are
unchanged. Beyond it they can differ in the last digits, and they are finite
where 1.x returned infinite values. Every distribution also provides sf,
isf, logcdf, logsf and logpdf. GEVMin (GEVmin in 1.x)
was transformed from quantiles of \(-X\) and converted its moments with the
relations for maxima, so results for models that use it change.
Result records#
Every analysis’s run() now returns an immutable record from
pystra.results: FORMResult, SORMResult, SimulationResult,
SystemFORMResult, SensitivityResult, StrongMaximumResult or
DistributionAnalysisResult. In 1.x only sensitivity analysis returned a
value. A later run cannot change an earlier record. Every record has
method, status, message, n_limit_state_evaluations and
variable_names, the converged property and summary(); estimates
add failure_probability and beta. See Interpreting results and convergence.
FORMResult.beta is the normal-equivalent index, and design_index the
signed distance of the design point in standard_space. These differ in
Student-t standard space. A normal-space index remains finite even when its
very small probability underflows to zero. Iteration exhaustion raises
AnalysisError, which carries the unconverged record, with no probability,
index or design point, in .result; see Failure handling. The design point and direction are
read-only arrays in variable_names order. Records compare equal by value
but, holding arrays, are not hashable.
If you used the 2.0 development FORMResult, its fields are renamed:
Before |
Now |
|---|---|
|
|
|
|
|
|
|
|
tuples |
read-only NumPy arrays |
SensitivityAnalysis.run() returns a SensitivityResult instead of a
dictionary:
# 1.x
result = analysis.run(numerical=True)
result["R"]["mean"]
closed = analysis.run(numerical=False)
closed["marginal"]["R"]["mean"], closed["correlation"]
The current constructors select the differentiation method:
result = ra.SensitivityAnalysis(model, limit_state, method="numerical").run()
result.marginal["R"]["mean"]
closed = ra.SensitivityAnalysis(model, limit_state, method="closed_form").run()
closed.marginal["R"]["mean"], closed.correlation
result.to_dataframe() gives the table that analysis.summary(result)
gave.
Two outcomes are reported differently in the records. A simulation that
observed no failures reports an infinite coefficient of variation, rather
than crude Monte Carlo’s placeholder of 1.0, and its status is
"precision_not_met" when a positive precision target was requested.
target_cov=0 instead requests a fixed budget and reports "completed"
when that budget is used, retaining the measured coefficient of variation.
A SORM fit whose curvatures make Breitung’s formula
undefined printed a message and reported a probability and index of 0.0; its
record now has status "not_converged" and no estimate, and run()
raises it in an AnalysisError unless on_failure="return". Likewise, an
undefined modified Breitung probability is None.
Models and distributions#
Distributions take std and start_point in place of stdv and
startpoint, and mean, std and start_point are read-only
properties.
1.x |
2.0 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Distributions, constants and models print informatively, for example
Normal('R', mean=10, std=1).
Native parameters are keyword arguments of the same constructor, replacing
input_type: give either the mean and standard deviation or every native
parameter. The names follow SciPy. GumbelMin takes its scale where
input_type took the inverse, and the Weibull scale is measured from the
lower bound where input_type took the characteristic value \(u_1\). The
bounds of Weibull and Beta are the keywords lower and upper, and
start_point is keyword-only for every distribution.
1.x |
2.0 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Correlation matrices#
CorrelationMatrix is checked when it is created: it must be square, finite,
symmetric, with a unit diagonal and positive definite, or ModelError is
raised. set_correlation checks an array in the same way; in 1.x any array
was accepted, and an invalid one failed later, if at all. The matrix is held as
a read-only array, so element assignment is removed: build a new matrix. The
unused attributes mu, sigma and p1 to p4 are removed.
Set all random variables before setting explicit correlation or a copula.
Adding another marginal afterwards raises an error and leaves the model
unchanged, rather than replacing its dependence with an identity matrix.
Constants can still be added. The matrix dimensions must match the existing
random variables when set_correlation is called.
cholesky() returns the lower Cholesky factor; nataf(model) returns the
standard-normal correlation that the Nataf transformation uses for the model’s
marginals; and CorrelationMatrix.nearest_positive_definite(matrix) repairs
an estimate that is not positive definite, using Higham’s (2002) method.
Failure handling#
In 1.x, FORM warned when it exhausted its iterations and kept its last,
unconverged values. Nonconvergence now raises AnalysisError (a
RuntimeError and a PystraError), which carries the unconverged record
in .result. FORM, SORM, SystemFORM and SensitivityAnalysis
take on_failure="return" to return that record instead, with status
"not_converged" and no probability, index or estimate; FORM also warns.
Use it for batch studies:
try:
result = ra.FORM(model, limit_state).run()
except ra.AnalysisError as error:
if error.result is not None:
print(error.result.limit_state_error)
else:
print(error)
result = ra.FORM(model, limit_state, on_failure="return").run()
if not result.converged:
...
SORM also fails when Breitung’s formula is undefined for the fitted
curvatures, and system FORM when a component does not converge or its
integration fails. Code calibration returns its unconverged cases, as before.
Valid simulations report an unmet positive precision target as
"precision_not_met" with their estimate. Invalid limit-state values or
external-solver exceptions raise AnalysisError; the originating exception
is retained as its cause. An evaluation error may have no result record;
SORM can wrap it in a failed record under on_failure="return". Importance and line sampling
require a converged FORM prerequisite. Line sampling raises when evaluation
or root refinement fails, with the affected line identified; failed evaluations
are never treated as safe samples or replaced with a bracket midpoint.
A limit state whose argument names do not match the model raises
ModelError; an exception inside a correctly bound evaluator raises
AnalysisError with the original exception as its cause.
SORM evaluates its correction formulas and Mills ratio in log space.
Importance sampling accumulates log likelihood ratios and log moment sums, and
line sampling normalizes its probabilities before computing their dispersion.
A reported probability can therefore underflow to zero while its reliability
index and relative uncertainty stay finite. No observed failures still gives an
infinite reliability index and uncertainty. Line sampling restricts its scan to
standard normal coordinates in [-37, 37], including the correlated
coordinates of a Nataf transformation. If that truncates the scan and no
intersection is found, AnalysisError reports the unresolved line rather
than assigning it the probability of the truncated endpoint.
Getters and printing#
The analysis getters and printing methods are removed; read the record that
run() returns. The analyses’ remaining numerical state (beta, Pf,
u, kappa, cov_q_bar and so on) is private, and limitstate is
renamed limit_state.
1.x |
2.0 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SORM |
|
SORM |
|
Monte Carlo |
|
Subset simulation |
|
Strong Maximum Test |
|
|
|
|
|
FORM’s getters returned the geometric index; in normal space it equals
result.beta, and in Student-t space use result.design_index.
plot_strong_maximum takes the Strong Maximum Test’s record. The
distribution analysis’s samples are result.samples_x and
result.limit_state_values; crude Monte Carlo no longer exposes its stored
samples. To test a system component’s design point, pass the component limit
state and result.component_results[name].design_point_u to
StrongMaximumTest explicitly.
Code calibration using normalized reliability#
GenericCalibration is renamed CodeCalibration, and GenericModel
becomes NormalizedReliabilityModel. Their implementation is in
pystra.calibration.normalized. These names describe the engineering
workflow and probability model; no compatibility aliases are retained.
CodeCalibration is the primary code-calibration workflow. It evaluates
candidate factors against a probability model over a normalized G/P/Q load-ratio
grid. It does not silently choose a fitting objective or optimize the factors.
Replace model setters and the 14-position GenericModel.get() tuple with
explicit constructor fields. NominalValues holds characteristic values;
CodeFactors holds a separate candidate factor set. Both validate positive,
finite values. NormalizedReliabilityModel copies distributions/constants and optionally
accepts a copula. The copula follows the random-variable order
resistance_error, resistance, load_error, dead_load, permanent_load, live_load,
omitting constants. Independence is the default.
For example:
from dataclasses import replace
import pystra as ra
model = ra.calibration.NormalizedReliabilityModel(
resistance=ra.Lognormal("R", 1, 0.08),
dead_load=ra.Normal("G", 1, 0.08),
permanent_load=ra.Normal("P", 1, 0.10),
live_load=ra.Normal("Q", 1, 0.10),
resistance_error=ra.Lognormal("w_R", 1, 0.05),
load_error=ra.Lognormal("w_S", 1, 0.10),
nominal_values=ra.calibration.NominalValues(1, 1, 1, 1),
)
factors = ra.calibration.CodeFactors(phi=0.8, gamma_g=1.2, gamma_p=1.5, gamma_q=1.6)
study = ra.calibration.CodeCalibration(
live_load_ratios=[0.2, 0.5, 0.8], dead_load_ratios=[0, 0.5, 1]
)
current = study.run(model, factors, target_beta=3.8)
proposed = study.run(model, replace(factors, phi=0.9), target_beta=3.8)
print(current.to_frame())
fig, ax = ra.calibration.plot_calibration(
{"Current": current, "Proposed": proposed}, target_beta=3.8
)
This replaces add_model(...), analyse() and the study’s mutable result
cache. Each run(model, factors, *, options=None, target_beta=None) returns a
new CodeCalibrationResult with the analyzed model snapshot, factors, designs,
and a FORMResult at every grid point. beta is a fresh array of shape
(n_dead_load_ratios, n_live_load_ratios). Failed points are retained as NaN;
their target margins are unavailable. to_frame() returns a reporting copy.
plot_calibration accepts completed results and refuses to draw an envelope
that would hide failed points. Labels, colors and range annotations belong to
this plotting function, replacing styling in model registration.
Grids must be nonempty, one-dimensional, and within [0, 1], including endpoints. FORM options can be supplied explicitly; the built-in normalized limit state currently requires finite-difference derivatives and rejects DDM mode.
Explicit load cases#
LoadCombination now stores inspectable probabilistic cases and metadata;
analysis is explicit through analyze_case(cases, case_name, options=...).
Its constructor accepts cases, constants, roles, leading_actions,
limit_state and labeled Pearson correlation. It validates inputs but
performs no reliability analysis. Case/model access returns independent copies.
Unknown variable overrides raise instead of being silently ignored.
Use VariableRoles(resistance=(...), other=(...), variable=(...)) when case
roles matter. leading_actions maps each case name to its leading variable
action names. Explicit-case construction preserves these roles; case insertion
order never determines a leading action.
Replace the legacy nested dict_dist_comb/action_distributions constructor
with LoadCombination.from_actions(maxima=..., companions=..., resistance=...,
other=..., constants=..., limit_state=...). Maxima and companions are separate
mappings from action names to distributions. The default generates one
<action>_max case per action. Use LoadCombination.turkstra for the retained
FBC reference-period/companion-duration construction.
The old lsf, corr, opt and positional constructor arguments are
removed: use limit_state, correlation and per-evaluation options.
run_reliability_case is replaced by analyze_case. Duplicate label groups,
mutable case-distribution attributes and special zero-filled limit-state
helpers are removed. Ordinary mappings remain appropriate for named cases,
nominal values and overrides; fixed records express roles and results.
Partial and combination factor calibration#
The old Calibration object is removed, without an alias. Its hidden sequence
of mutable DataFrame attributes is replaced by independently usable operations:
problem = ra.calibration.FactorCalibrationProblem(
cases, nominal_values=nominal_values, design_parameter="scale"
)
targets = ra.calibration.solve_designs(problem, target_beta=4.3, method="root")
factors = ra.calibration.derive_factors(targets, method="matrix")
selected = ra.calibration.select_factors(
factors, resistance="minimum", loads="maximum", combinations="maximum"
)
designs = ra.calibration.design_with_factors(problem, selected)
checks = ra.calibration.verify_designs(problem, max(designs.values), target_beta=4.3)
Here cases is a LoadCombination with explicit roles, leading actions,
a limit state and a common Constant("scale", ...). nominal_values maps
exactly the random-variable role names to positive characteristic values.
See the factor-calibration tutorial for complete linear and nonlinear examples.
solve_designs supports method="root" (fsolve, or Brent with a supplied
bracket) and method="alpha". The default tolerance is 1e-4 in beta and
max_evaluations=100 limits FORM runs per case, including final verification.
Each CalibratedDesign carries the design value, reliability, target residual,
convergence, evaluation count and message. A failed inner or outer solve remains
in the result and cannot enter factor derivation. Solver success alone is
insufficient: the final beta residual must also satisfy the tolerance.
derive_factors supports the original matrix and coeff methods.
These specialist methods require normal standard space and exactly one leading
case per variable action. Alpha projection also requires normal space. The
design rule assumes a separable resistance scale with multiplicative model
errors; designs are checked against the full limit-state residual. Arbitrary
coupled nonlinear functions are not supported by term isolation. The matrix
method retains the reference convention of evaluating each action coefficient
in its own leading case, then assembling by names. Singular systems raise.
FactorSet contains immutable numeric rows with explicit case/variable axes.
to_frame("resistance"), to_frame("loads") and
to_frame("combinations") return fresh tables. Selection defaults to
"per_case"; extrema must be requested explicitly, and governing-case ties
are retained. design_with_factors returns named design values. Selecting
their maximum is explicit in the example, followed by verification of every
case; extrema alone do not establish that targets are met.
Saved objects and extensions#
Objects pickled with 1.x, such as models, distributions and analyses, cannot be
loaded in 2.0: their classes, modules and internal state have changed. Rebuild
them from their definitions, and keep results as plain data, for example with
result.to_dataframe().
A custom subclass of Distribution keeps working if it passes a SciPy frozen
distribution as dist_obj or overrides u_to_x, x_to_u and
jacobian; rename stdv to std and startpoint to start_point.
A subclass that overrides cdf, ppf or pdf should also provide
sf, isf and logpdf to keep small probabilities accurate; see
Working at high reliability. Sensitivity analysis rebuilds a distribution
with with_parameters, which passes the parameters mapping, with any
changes, back to the constructor. A subclass whose constructor takes further
arguments overrides parameters to include them; see
Adding a distribution.
A limit-state function receives one keyword argument for each variable and
constant, so its argument names must match the model’s names; otherwise
ModelError is raised.
To evaluate a limit state directly, LimitState.evaluate(x, model) takes a
point (n_variables,) or rows (n_samples, n_variables) in the model’s
variable order; the column-oriented evaluate_lsf is private. Transformations
name the direction of their Jacobians: jacobian_u_wrt_x and
jacobian_x_wrt_u replace jacobian.
Migration records#
The calibration naming map
records the intermediate naming-only stage. It is historical: several of those
intermediate names have since been removed. The
calibration structure map
records replacement operations and current definitions, signatures and result
fields. The definition manifest retains a disposition for every baseline
entry; removed/replaced APIs have no misleading one-to-one current name.
These maps are not global text-substitution recipes for user scripts.
The calibration review records the original
reproductions and their resolution. Readable local df names, statistical
degrees of freedom and conversion methods such as to_dict() retain their
meanings; pseudo-Hungarian container prefixes are removed.
Integration and validation#
The outstanding Strong Maximum Test, copula/joint distribution, DDO/LQI,
formatting, and SORM prerequisite PRs remain unmerged in 1.x. Their commits
are included on v2.0 and their changes are reserved for this release.
System FORM was included earlier. Contributor attribution is retained in
the integration records and source history.
The corrected reference point is the Git tag baseline/v2.0-before-naming.
All 478 tests and all 13 indexed tutorials pass before and after the naming
pass. Canonicalizing the mapped identifiers gives matching Python syntax trees
across all 45 package modules, apart from documentation and version metadata.
No numerical formulas or tolerances changed in the naming commit.
A follow-up replaces wildcard imports in the distribution package to correct the module collisions described above. Five import regression cases bring the suite to 483 tests at that checkpoint. The calibration replacement adds direct analytic and failure regressions while preserving all nine specialist reference tests. Both original 10-by-10 generic tutorial grids match to floating-point precision. See the migration plan for the latest validation record.
The SORM prerequisite fix is included. Monte Carlo also now reports an infinite estimated reliability index when no failures are sampled, consistent with its zero probability estimate. This is not evidence of zero true failure probability; finite-sample uncertainty reporting remains further work.
Migration tools#
python -m pystra.migrate PATH... converts 1.x scripts and notebooks. It
rewrites only unambiguous code: imports and module paths, renamed classes and
functions reached through the pystra module or imported from it, and renamed
keywords of PySTRA constructors such as stdv. It prints the changes as a diff
and writes nothing unless you add --write, and a second run changes nothing.
What needs judgment is reported with its location instead: result getters,
AnalysisOptions, input_type, printing methods, the Calibration class
and global NumPy seeding of simulations. Review each report before running the
script, and use the checklist above and the Complete symbol map for the rest.
The converter was tried on the 1.6.0 examples and tutorials. After the reported
edits they reproduce their 1.6.0 results within stated tolerances; see the
migration trials.
The scripts below maintain this repository.
scripts/migrate_names.py applies the reviewed map to tracked repository
code and notebook source, with a dry run by default. It preserves notebook
outputs and ignores untracked files. It is a repository maintenance tool,
not a type-aware converter for arbitrary user scripts.
scripts/api_inventory.py --check-names checks package function/method names
and the migrated class names. --check-migration verifies baseline manifest
coverage and current calibration definition/signature records.
scripts/execute_notebooks.py --output-dir DIR
executes the tutorials listed in the tutorial index in fresh kernels. CI runs
these checks, the test suite, formatting, documentation, and package builds.
Active learning: new in 2.0#
Active learning is a new 2.0 capability, developed from the al contribution
and the literature-guided extensions. It remains unmerged in 1.x. Import from
pystra.active_learning. PCESurrogate, learning_u and
learning_eff follow the 2.0 naming conventions. Surrogates now consume
independent normal coordinates, not physical points.
ActiveLearning.run() returns an immutable ActiveLearningResult with
failure_probability, beta, conditional sampling diagnostics and explicit
convergence status. Its n_limit_state_evaluations counts true-model calls,
the name every result record uses; SubsetRun.n_samples counts surrogate
samples. Settings are keyword-only; use lowercase u or eff
for the named learning functions. Stopping policies are explicit components.
The four components are now Surrogate, LearningFunction,
ReliabilityEstimator and StoppingCriterion. The import path remains
pystra.active_learning, now a package. Existing concise calls retain the
same default workflow and numerical settings: learning_threshold configures
the named U/EFF policy, n_estimation configures final independent MC, and
target_cov configures LearningThreshold. For explicit components use
UFunction(threshold=...), MonteCarloEstimator(n_samples=...) and
LearningThreshold(target_cov=...) instead. Combining a component with its
shortcut raises ValueError. Configure these objects directly rather than
assigning former runner attributes learning_threshold, n_estimation
or target_cov. analysis.learning_function now holds a policy object.
BetaBounds, BetaStability and AllCriteria are explicit alternatives.
History entries add probability_band, beta_band and
learning_satisfied. The result now stores a single immutable estimate
record. Attribute access to failure_probability, beta, sampling_cov,
sampling_interval and n_estimation is unchanged, but these are properties;
dataclasses.asdict(result) nests sampling data under estimate. Use named
fields when constructing result records; their positional constructor has
changed. A custom estimator may return no sampling interval (None), and
result.estimate identifies its method, dependence and confidence level.
seed controls run-owned randomness. Explicit surrogate instances retain
their own optimizer/bootstrap configuration; set their seed at construction.
See Active-learning methods and literature for composition and extension contracts, and
Active learning for reliability analysis for scope and limitations.
PCE now defaults to adaptive sparse fitting: method="lars" with candidate
degrees 1 through 5. An integer degree fits a single candidate. Pass
method="ols" for dense fitting. q_norm accepts one or more hyperbolic
truncations and max_interaction limits interaction order. Sparse PCE’s
default initial design is max(30, 5*n_variables). fit_result returns
immutable selection diagnostics. Both PCE methods use core NumPy/SciPy;
Kriging retains the optional scikit-learn dependency.
Estimator-driven subset enrichment#
SubsetSimulationEstimator implements the new EnrichmentEstimator
contract. Pass it to ActiveLearning(estimator=...) to resample conditional
populations after each fit, then run independent final sampling. Existing
ReliabilityEstimator subclasses remain final-only and retain fixed MC
pools. Their implementations need not change. EnrichmentResult separates
selection points from estimator-computed probabilities; never average its
pooled conditional samples to estimate Pf.
For adaptive estimators, n_candidates caps the selectable pool, while
SubsetSimulationEstimator(n_samples=...) sets the per-level sample size.
The default initial design is max(30, 5*n_variables). Explicit n_initial
still takes precedence. Exploration uses a repeated separate random stream
for comparisons; final estimation is independent. Fixed MC seeds and results
retain their prior behavior.
ReliabilityEstimate adds converged, status and diagnostics with
backward-compatible defaults for existing component constructors. Completion
of the sampling algorithm is distinct from adequate CoV. LearningStep adds
estimation_converged and optional sampling_cov for exploration. Built-in
stopping rules reject incomplete exploratory estimates. The active result may
have status estimation_failed if final subset sampling cannot finish.
Subset diagnostics contain SubsetRun and SubsetLevel snapshots, with
no IID binomial confidence interval. The standalone pystra.SubsetSimulation
API and numerical implementation are unchanged in this increment.
Additional active-learning contracts#
The public import path remains pystra.active_learning. The new named
surrogate is "pc_kriging" and the new named learning function is "fbr".
PCKrigingSurrogate and PCKrigingFitResult expose sequential PC-Kriging;
ImportanceSamplingEstimator and ImportanceSamplingDiagnostics supply
explicit weighted sampling. Both use independent normal coordinates.
EnsembleSurrogate.predict_replicates and
EnsembleLearningFunction.select_replicates extend the existing scalar
contracts without changing U/EFF. PCESurrogate implements the ensemble
interface. FBRLearning requires it; incompatible combinations fail before
true-model evaluation. Named FBR defaults to BootstrapBounds; existing
U/EFF defaults remain unchanged. Bootstrap probability stopping is restricted
to fixed IID normal enrichment and rejected for adaptive weighted/conditional
pools, including when nested inside AllCriteria.
LearningStep.bootstrap_probability_band is an optional immutable pair of
actual bootstrap Pf extremes, separate from probability_band (the
mean/spread sensitivity diagnostic). None means it was not calculated.
Neither field is a confidence interval for total surrogate error. See
Active-learning methods and literature for formulas, limitations and the worked tutorial.