Skip to main content

converge_optimization/packs/assignment_pack/
solver.rs

1//! Solver for Task Assignment pack
2//!
3//! Delegates to the Hungarian algorithm from `crate::assignment::hungarian`
4//! for optimal O(n³) assignment.
5
6use super::types::*;
7use crate::assignment::{self, AssignmentProblem};
8use converge_pack::gate::GateResult as Result;
9use converge_pack::gate::{ProblemSpec, ReplayEnvelope, SolverReport};
10
11/// Optimal assignment solver (Hungarian algorithm wrapper)
12pub struct HungarianAssignmentSolver;
13
14impl HungarianAssignmentSolver {
15    pub fn solve(
16        &self,
17        input: &AssignmentInput,
18        spec: &ProblemSpec,
19    ) -> Result<(AssignmentOutput, SolverReport)> {
20        let n = input.cost_matrix.len();
21
22        // Convert f64 cost matrix to i64 for the Hungarian solver.
23        // Scale by 1000 to preserve 3 decimal places of precision.
24        let scale = 1000i64;
25        let int_costs: Vec<Vec<i64>> = input
26            .cost_matrix
27            .iter()
28            .map(|row| {
29                row.iter()
30                    .map(|&c| (c * scale as f64).round() as i64)
31                    .collect()
32            })
33            .collect();
34
35        let problem = AssignmentProblem::from_costs(int_costs);
36        let solution = assignment::solve(&problem)
37            .map_err(|e| converge_pack::GateError::invalid_input(e.to_string()))?;
38
39        // Convert back: assignments[agent] = task → Vec<(agent, task)>
40        let assignments: Vec<(usize, usize)> = solution
41            .assignments
42            .iter()
43            .enumerate()
44            .filter(|&(agent, _)| agent < n)
45            .map(|(agent, &task)| (agent, task))
46            .collect();
47
48        let total_cost: f64 = assignments
49            .iter()
50            .map(|&(a, t)| input.cost_matrix[a][t])
51            .sum();
52
53        let output = AssignmentOutput {
54            assignments,
55            total_cost,
56        };
57
58        let replay = ReplayEnvelope::minimal(spec.seed());
59        let report = SolverReport::optimal("hungarian-assignment-v1", total_cost, replay);
60
61        Ok((output, report))
62    }
63}