///|
/// One contributor in a tolerance-tightening recommendation.
pub struct TighteningItem {
name : String
current_tolerance : Double
proposed_tolerance : Double
expected_reduction : Double
priority : Double
} derive(Debug, Eq)
///|
/// A ranked plan for reducing a chain's RSS variation.
pub struct TighteningPlan {
current_rss : Double
target_rss : Double
projected_rss : Double
items : Array[TighteningItem]
} derive(Debug, Eq)
///|
fn tightening_priority(current : Double, proposed : Double) -> Double {
if current == 0.0 {
0.0
} else {
(current - proposed) / current
}
}
///|
fn sort_tightening_items(
items : Array[TighteningItem],
) -> Array[TighteningItem] {
for index in 1.. 0 &&
items[position - 1].expected_reduction < current.expected_reduction {
items[position] = items[position - 1]
position -= 1
}
items[position] = current
}
items
}
///|
/// Recommend the contributors that most efficiently reduce RSS variation.
pub fn propose_tightening(
chain : Chain,
target_rss : Double,
max_changes : Int,
) -> Array[TighteningItem] {
if target_rss < 0.0 {
abort("target RSS must be non-negative")
}
if max_changes <= 0 {
abort("maximum tightening changes must be positive")
}
let current_rss = chain.rss().standard_deviation
let scale = if current_rss == 0.0 || target_rss >= current_rss {
1.0
} else {
target_rss / current_rss
}
let all_items = chain.dimensions.map(dimension => {
let current = dimension.tolerance
let proposed = current * scale
{
name: dimension.name,
current_tolerance: current,
proposed_tolerance: proposed,
expected_reduction: current - proposed,
priority: tightening_priority(current, proposed),
}
})
let ranked = sort_tightening_items(all_items)
let count = if max_changes < ranked.length() {
max_changes
} else {
ranked.length()
}
let result = []
for index in 0.. TighteningPlan {
let current_rss = chain.rss().standard_deviation
let scale = if current_rss == 0.0 || target_rss >= current_rss {
1.0
} else {
target_rss / current_rss
}
let items = chain.dimensions.map(dimension => {
let proposed = dimension.tolerance * scale
{
name: dimension.name,
current_tolerance: dimension.tolerance,
proposed_tolerance: proposed,
expected_reduction: dimension.tolerance - proposed,
priority: tightening_priority(dimension.tolerance, proposed),
}
})
{
current_rss,
target_rss,
projected_rss: current_rss * scale,
items: sort_tightening_items(items),
}
}
///|
/// Return the sum of squared proposed tolerances in a plan.
pub fn TighteningPlan::projected_variance(self : TighteningPlan) -> Double {
let mut variance = 0.0
for item in self.items {
variance += item.proposed_tolerance * item.proposed_tolerance
}
variance
}
///|
/// Return the largest proposed tolerance change.
pub fn TighteningPlan::largest_reduction(
self : TighteningPlan,
) -> TighteningItem {
self.items[0]
}