Skip to main content

problemreductions/registry/
mod.rs

1//! Problem registry and metadata types.
2//!
3//! This module provides types for problem introspection and discovery.
4//!
5//! # Overview
6//!
7//! - [`ProblemInfo`] - Rich metadata (name, description, complexity, reductions)
8//! - [`ProblemMetadata`] - Trait for problems to provide their own metadata
9//! - [`ComplexityClass`] - Computational complexity classification
10//!
11//! # Example
12//!
13//! ```rust
14//! use problemreductions::registry::{ProblemInfo, ComplexityClass};
15//!
16//! // Create problem metadata
17//! let info = ProblemInfo::new("Independent Set", "Find maximum non-adjacent vertices")
18//!     .with_aliases(&["MIS", "Stable Set"])
19//!     .with_complexity(ComplexityClass::NpComplete)
20//!     .with_reduction_from("3-SAT");
21//!
22//! assert!(info.is_np_complete());
23//! ```
24//!
25//! # Implementing for Custom Problems
26//!
27//! Problems can implement [`ProblemMetadata`] to provide introspection:
28//!
29//! ```rust
30//! use problemreductions::registry::{
31//!     ProblemMetadata, ProblemInfo, ComplexityClass
32//! };
33//!
34//! struct MyProblem;
35//!
36//! impl ProblemMetadata for MyProblem {
37//!     fn problem_info() -> ProblemInfo {
38//!         ProblemInfo::new("My Problem", "Description")
39//!             .with_complexity(ComplexityClass::NpComplete)
40//!     }
41//! }
42//!
43//! let info = MyProblem::problem_info();
44//! println!("Problem: {}", info.name);
45//! ```
46
47mod dyn_problem;
48mod info;
49pub mod problem_ref;
50pub mod problem_type;
51mod schema;
52pub mod variant;
53
54pub use dyn_problem::{format_metric, DynProblem, LoadedDynProblem};
55pub use info::{ComplexityClass, FieldInfo, ProblemInfo, ProblemMetadata};
56pub use problem_ref::{parse_catalog_problem_ref, require_graph_variant, ProblemRef};
57pub use problem_type::{find_problem_type, find_problem_type_by_alias, problem_types, ProblemType};
58pub use schema::{
59    collect_schemas, FieldInfoJson, ParseProblemCategoryError, ProblemCategory, ProblemSchemaEntry,
60    ProblemSchemaJson, VariantDimension,
61};
62pub use variant::{
63    find_variant_by_alias, find_variant_entry, validate_create_inputs,
64    validate_direct_create_inputs, validate_variant_aliases, validate_variant_parameter_schemas,
65    variant_entries, ConstructProblemFn, ConstructionError, CreateInputCodec, CreateInputInfo,
66    CreateSpec, RandomGenerate, RandomRegistration, VariantEntry,
67};
68
69/// Construct a problem from normalized construction inputs using the exact
70/// registered problem name and variant.
71pub fn construct_dyn(
72    name: &str,
73    variant: &BTreeMap<String, String>,
74    data: serde_json::Value,
75) -> Result<Box<dyn DynProblem>, ConstructionError> {
76    let entry = find_variant_entry(name, variant).ok_or_else(|| {
77        ConstructionError::UnregisteredVariant {
78            name: name.to_string(),
79            variant: variant.clone(),
80        }
81    })?;
82    (entry.construct_fn)(data)
83}
84
85use std::any::Any;
86use std::collections::BTreeMap;
87
88/// Load a problem from JSON by exact problem name and exact variant map.
89///
90/// No alias resolution or default fallback. Returns `Err` if the entry is not found.
91pub fn load_dyn(
92    name: &str,
93    variant: &BTreeMap<String, String>,
94    data: serde_json::Value,
95) -> Result<LoadedDynProblem, ConstructionError> {
96    let entry = find_variant_entry(name, variant).ok_or_else(|| {
97        ConstructionError::UnregisteredVariant {
98            name: name.to_string(),
99            variant: variant.clone(),
100        }
101    })?;
102
103    let inner = (entry.factory)(data)
104        .map_err(|error| ConstructionError::InvalidInput(error.to_string()))?;
105    Ok(LoadedDynProblem::new(inner))
106}
107
108/// Serialize a `&dyn Any` by exact problem name and exact variant map.
109///
110/// Returns `None` if the entry is not found or the downcast fails.
111pub fn serialize_any(
112    name: &str,
113    variant: &BTreeMap<String, String>,
114    any: &dyn Any,
115) -> Option<serde_json::Value> {
116    let entry = find_variant_entry(name, variant)?;
117    (entry.serialize_fn)(any)
118}
119
120#[cfg(test)]
121#[path = "../unit_tests/registry/dispatch.rs"]
122mod dispatch_tests;