Skip to main content

converge_optimization/packs/assignment_pack/
mod.rs

1//! Task Assignment Pack
2//!
3//! Assign N agents to N tasks minimizing total cost using the Hungarian algorithm.
4
5mod solver;
6mod types;
7
8pub use solver::*;
9pub use types::*;
10
11use crate::packs::{InvariantDef, InvariantResult, Pack, PackSolveResult, default_gate_evaluation};
12use converge_pack::gate::GateResult as Result;
13use converge_pack::gate::{KernelTraceLink, ProblemSpec, PromotionGate, ProposedPlan};
14
15pub struct AssignmentPack;
16
17impl Pack for AssignmentPack {
18    fn name(&self) -> &'static str {
19        "task-assignment"
20    }
21
22    fn version(&self) -> &'static str {
23        "1.0.0"
24    }
25
26    fn validate_inputs(&self, inputs: &serde_json::Value) -> Result<()> {
27        let input: AssignmentInput = serde_json::from_value(inputs.clone())
28            .map_err(|e| converge_pack::GateError::invalid_input(format!("Invalid input: {e}")))?;
29        input.validate()
30    }
31
32    fn invariants(&self) -> &[InvariantDef] {
33        &[]
34    }
35
36    fn solve(&self, spec: &ProblemSpec) -> Result<PackSolveResult> {
37        let input: AssignmentInput = spec.inputs_as()?;
38        input.validate()?;
39
40        let solver = HungarianAssignmentSolver;
41        let (output, report) = solver.solve(&input, spec)?;
42
43        let trace = KernelTraceLink::audit_only(format!("trace-{}", spec.problem_id));
44        let confidence = if output.assignments.len() == input.cost_matrix.len() {
45            0.8
46        } else {
47            0.4
48        };
49
50        let plan = ProposedPlan::from_payload(
51            format!("plan-{}", spec.problem_id),
52            self.name(),
53            output.summary(),
54            &output,
55            confidence,
56            trace,
57        )?;
58
59        Ok(PackSolveResult::new(plan, report))
60    }
61
62    fn check_invariants(&self, _plan: &ProposedPlan) -> Result<Vec<InvariantResult>> {
63        Ok(vec![])
64    }
65
66    fn evaluate_gate(
67        &self,
68        _plan: &ProposedPlan,
69        invariant_results: &[InvariantResult],
70    ) -> PromotionGate {
71        default_gate_evaluation(invariant_results, self.invariants())
72    }
73}