impl PopperScore {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new() -> Self {
Self {
raw_score: 0.0,
max_available: 100.0,
normalized_score: 0.0,
grade: PopperGrade::F,
gateway_passed: false,
categories: PopperCategoryScores::default(),
recommendations: Vec::new(),
metadata: PopperMetadata::new("unknown".to_string()),
analysis: PopperAnalysis::default(),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn calculate(&mut self) {
let falsifiability_score = self.categories.falsifiability.earned;
self.gateway_passed = falsifiability_score >= 15.0;
if !self.gateway_passed {
self.raw_score = 0.0;
self.normalized_score = 0.0;
self.grade = PopperGrade::InsufficientFalsifiability;
return;
}
self.raw_score = self.categories.total_earned();
self.max_available = self.categories.total_available();
if self.max_available > 0.0 {
self.normalized_score = (self.raw_score / self.max_available) * 100.0;
} else {
self.normalized_score = 0.0;
}
self.grade = PopperGrade::from_normalized_score(self.normalized_score);
}
}
impl Default for PopperScore {
fn default() -> Self {
Self::new()
}
}
const GRADE_THRESHOLDS: &[(f64, PopperGrade)] = &[
(95.0, PopperGrade::APlus),
(90.0, PopperGrade::A),
(85.0, PopperGrade::AMinus),
(80.0, PopperGrade::BPlus),
(70.0, PopperGrade::B),
(60.0, PopperGrade::C),
(50.0, PopperGrade::D),
];
impl PopperGrade {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "score_range")]
pub fn from_normalized_score(score: f64) -> Self {
GRADE_THRESHOLDS
.iter()
.find(|(threshold, _)| score >= *threshold)
.map(|(_, grade)| *grade)
.unwrap_or(PopperGrade::F)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn meets_standards(&self) -> bool {
matches!(
self,
PopperGrade::APlus | PopperGrade::A | PopperGrade::AMinus
)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn interpretation(&self) -> &'static str {
match self {
PopperGrade::APlus => "Exemplary Popperian Science",
PopperGrade::A => "Strong Scientific Standards",
PopperGrade::AMinus => "Meets Reproducibility Requirements",
PopperGrade::BPlus => "Good Practices, Minor Gaps",
PopperGrade::B => "Acceptable, Improvement Needed",
PopperGrade::C => "Significant Reproducibility Gaps",
PopperGrade::D => "Major Falsifiability Issues",
PopperGrade::F => "Insufficient Rigor for Independent Verification",
PopperGrade::InsufficientFalsifiability => "GATEWAY FAILED - Not Evaluable as Science",
}
}
}
impl fmt::Display for PopperGrade {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PopperGrade::APlus => write!(f, "A+"),
PopperGrade::A => write!(f, "A"),
PopperGrade::AMinus => write!(f, "A-"),
PopperGrade::BPlus => write!(f, "B+"),
PopperGrade::B => write!(f, "B"),
PopperGrade::C => write!(f, "C"),
PopperGrade::D => write!(f, "D"),
PopperGrade::F => write!(f, "F"),
PopperGrade::InsufficientFalsifiability => write!(f, "GATEWAY FAILED"),
}
}
}
impl PopperCategoryScores {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn total_earned(&self) -> f64 {
let mut total = self.falsifiability.earned
+ self.reproducibility.earned
+ self.transparency.earned
+ self.statistical_rigor.earned
+ self.historical_integrity.earned;
if !self.ml_reproducibility.is_not_applicable {
total += self.ml_reproducibility.earned;
}
total
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn total_available(&self) -> f64 {
let mut total = self.falsifiability.max
+ self.reproducibility.max
+ self.transparency.max
+ self.statistical_rigor.max
+ self.historical_integrity.max;
if !self.ml_reproducibility.is_not_applicable {
total += self.ml_reproducibility.max;
}
total
}
}
impl Default for PopperCategoryScores {
fn default() -> Self {
Self {
falsifiability: PopperCategoryScore::new("Falsifiability & Testability", 0.0, 25.0),
reproducibility: PopperCategoryScore::new("Reproducibility Infrastructure", 0.0, 25.0),
transparency: PopperCategoryScore::new("Transparency & Openness", 0.0, 20.0),
statistical_rigor: PopperCategoryScore::new("Statistical Rigor", 0.0, 15.0),
historical_integrity: PopperCategoryScore::new("Historical Integrity", 0.0, 10.0),
ml_reproducibility: PopperCategoryScore::new_na("ML/AI Reproducibility", 5.0),
}
}
}
impl PopperCategoryScore {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new(name: &str, earned: f64, max: f64) -> Self {
Self {
name: name.to_string(),
earned,
max,
is_applicable: true,
is_not_applicable: false,
sub_scores: Vec::new(),
findings: Vec::new(),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new_na(name: &str, max: f64) -> Self {
Self {
name: name.to_string(),
earned: 0.0,
max,
is_applicable: false,
is_not_applicable: true,
sub_scores: Vec::new(),
findings: Vec::new(),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn percentage(&self) -> f64 {
if self.is_not_applicable || self.max == 0.0 {
0.0
} else {
(self.earned / self.max) * 100.0
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn mark_applicable(&mut self) {
self.is_applicable = true;
self.is_not_applicable = false;
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn mark_not_applicable(&mut self) {
self.is_applicable = false;
self.is_not_applicable = true;
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "score_range")]
pub fn add_sub_score(&mut self, sub: PopperSubScore) {
self.earned += sub.earned;
self.sub_scores.push(sub);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn add_finding(&mut self, finding: PopperFinding) {
self.findings.push(finding);
}
}
impl PopperSubScore {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new(id: &str, name: &str, earned: f64, max: f64, description: &str) -> Self {
Self {
id: id.to_string(),
name: name.to_string(),
earned,
max,
description: description.to_string(),
}
}
}