///|
pub enum Direction {
Positive
Negative
} derive(Debug, Eq)
///|
pub fn negative_direction() -> Direction {
Negative
}
///|
pub struct Dimension {
name : String
nominal : Double
tolerance : Double
direction : Direction
} derive(Debug, Eq)
///|
pub fn Dimension::new(
name : String,
nominal : Double,
tolerance : Double,
direction? : Direction = Positive,
) -> Dimension {
if tolerance < 0.0 {
abort("tolerance must be non-negative")
}
{ name, nominal, tolerance, direction }
}
///|
pub struct Chain {
name : String
dimensions : Array[Dimension]
} derive(Debug)
///|
pub fn Chain::new(name : String, dimensions : Array[Dimension]) -> Chain {
if dimensions.length() == 0 {
abort("a chain must contain at least one dimension")
}
{ name, dimensions }
}
///|
pub struct AnalysisResult {
nominal : Double
lower : Double
upper : Double
mean : Double
standard_deviation : Double
yield_rate : Double
sensitivity : Array[(String, Double)]
} derive(Debug)
///|
fn signed_nominal(d : Dimension) -> Double {
match d.direction {
Positive => d.nominal
Negative => -d.nominal
}
}
///|
pub fn Chain::nominal(self : Chain) -> Double {
self.dimensions.fold(init=0.0, (sum, d) => sum + signed_nominal(d))
}
///|
pub fn Chain::worst_case(self : Chain) -> AnalysisResult {
let mut total = 0.0
let mut span = 0.0
for d in self.dimensions {
total += signed_nominal(d)
span += d.tolerance
}
let sensitivities = self.dimensions.map(d => (d.name, d.tolerance))
{
nominal: total,
lower: total - span,
upper: total + span,
mean: total,
standard_deviation: span,
yield_rate: 1.0,
sensitivity: sensitivities,
}
}
///|
pub fn Chain::rss(self : Chain) -> AnalysisResult {
let mut total = 0.0
let mut variance = 0.0
for d in self.dimensions {
total += signed_nominal(d)
variance += d.tolerance * d.tolerance
}
let sigma = variance.sqrt()
let sensitivities = self.dimensions.map(d => {
(d.name, d.tolerance * d.tolerance)
})
{
nominal: total,
lower: total - 3.0 * sigma,
upper: total + 3.0 * sigma,
mean: total,
standard_deviation: sigma,
yield_rate: 0.9973,
sensitivity: sensitivities,
}
}
///|
pub fn Chain::monte_carlo(
self : Chain,
samples : Int,
seed? : UInt = 1U,
) -> AnalysisResult {
if samples <= 0 {
abort("samples must be positive")
}
let mut state = seed
let mut total = 0.0
let mut square_total = 0.0
let mut pass = 0
let worst = self.worst_case()
for _ in 0..= worst.lower && value <= worst.upper {
pass += 1
}
}
let mean = total / samples.to_double()
let variance = square_total / samples.to_double() - mean * mean
{
nominal: worst.nominal,
lower: worst.lower,
upper: worst.upper,
mean,
standard_deviation: variance.sqrt(),
yield_rate: pass.to_double() / samples.to_double(),
sensitivity: worst.sensitivity,
}
}