use serde::{Deserialize, Serialize};
fn clamp01(value: f64) -> f64 {
if value.is_finite() {
value.clamp(0.0, 1.0)
} else {
0.0
}
}
pub(crate) fn finite_or_zero(value: f64) -> f64 {
if value.is_finite() { value } else { 0.0 }
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ProbabilityLikeScore(f64);
impl ProbabilityLikeScore {
pub fn new(value: f64) -> Self {
Self(clamp01(value))
}
pub fn value(&self) -> f64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ConfidenceScore(f64);
impl ConfidenceScore {
pub fn new(value: f64) -> Self {
Self(clamp01(value))
}
pub fn value(&self) -> f64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct AtomIndex(pub u32);
impl From<chematic::core::AtomIdx> for AtomIndex {
fn from(idx: chematic::core::AtomIdx) -> Self {
AtomIndex(idx.0)
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FindingCode {
RingBridgedComplexity,
RingSpiro,
RingFusedDense,
RingMacrocycle,
SizeLargeMolecularWeight,
SizeHighRotatableBondCount,
StereoCenterCount,
StereoDensityHigh,
StereoAnalysisSkipped,
FunctionalGroupReactive,
FunctionalGroupDense,
FragmentPrecedentWeak,
FragmentPrecedentStrong,
InputUnsupportedElement,
InputDisconnected,
InputUnusualValence,
InputTooLarge,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Severity {
Low,
Medium,
High,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
pub struct FindingEvidence {
pub value: Option<f64>,
pub threshold: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Finding {
pub code: FindingCode,
pub severity: Severity,
pub confidence: ProbabilityLikeScore,
pub atoms: Vec<AtomIndex>,
pub evidence: FindingEvidence,
pub explanation: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct FindingRef(pub usize);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Contribution {
pub code: FindingCode,
pub name: String,
pub contribution: ProbabilityLikeScore,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ComponentScore {
pub raw: f64,
pub normalized: ProbabilityLikeScore,
pub confidence: ProbabilityLikeScore,
pub contribution: f64,
pub findings: Vec<FindingRef>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct ComponentScores {
pub size_topology: Option<ComponentScore>,
pub ring_topology: Option<ComponentScore>,
pub stereochemical_burden: Option<ComponentScore>,
pub functional_group_liability: Option<ComponentScore>,
pub input_quality: Option<ComponentScore>,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Verdict {
LikelyAccessible,
ModeratelyAccessible,
Challenging,
HighlyChallenging,
Indeterminate,
OutOfDomain,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OverallAssessment {
pub synthesizability: ProbabilityLikeScore,
pub difficulty: ProbabilityLikeScore,
pub confidence: ConfidenceScore,
pub verdict: Verdict,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicabilityReport {
pub supported_elements: bool,
pub sanitized: bool,
pub stereo_complete: bool,
pub stereo_uncheckable: bool,
pub disconnected: bool,
pub unusual_valence: bool,
pub domain_distance: Option<f64>,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum SuggestionCode {
ReduceStereocenterDensity,
ReplaceBridgedRingWithMonocyclicAnalog,
ReduceAdjacentQuaternaryCenters,
RemoveSimilarReactiveGroup,
IncreaseFragmentPrecedent,
SimplifyMacrocyclicClosure,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ExpectedEffect {
LikelyReducesDifficulty,
MayReduceDifficulty,
Uncertain,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SimplificationSuggestion {
pub code: SuggestionCode,
pub target_atoms: Vec<AtomIndex>,
pub rationale: String,
pub expected_effect: ExpectedEffect,
pub confidence: ProbabilityLikeScore,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Provenance {
pub schema_version: String,
pub yomitoki_version: String,
pub chematic_version: String,
pub ruleset_version: String,
pub fragment_corpus: Option<FragmentCorpusProvenance>,
pub config_hash: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FragmentCorpusProvenance {
pub version: String,
pub source_name: String,
pub domain: String,
pub synthesis_focused: bool,
pub description: String,
pub fragment_definition_version: String,
pub reference_distribution_version: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FragmentPrecedentEvidence {
pub signed_signal: f64,
pub precedent_penalty: f64,
pub precedent_support: f64,
pub confidence: ProbabilityLikeScore,
pub findings: Vec<FindingRef>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SynthesizabilityReport {
pub overall: OverallAssessment,
pub components: ComponentScores,
pub findings: Vec<Finding>,
pub dominant_penalties: Vec<Contribution>,
pub dominant_supports: Vec<Contribution>,
pub fragment_precedent: Option<FragmentPrecedentEvidence>,
pub suggestions: Vec<SimplificationSuggestion>,
pub applicability: ApplicabilityReport,
pub provenance: Provenance,
}
impl SynthesizabilityReport {
pub fn dominant_penalties(&self) -> &[Contribution] {
&self.dominant_penalties
}
pub fn dominant_supports(&self) -> &[Contribution] {
&self.dominant_supports
}
}