use super::{
ApplicationError, ApplicationResult, IndustryConstraint, IndustryObjective, IndustrySolution,
OptimizationProblem,
};
use crate::ising::IsingModel;
use crate::qubo::{QuboBuilder, QuboFormulation};
use crate::simulator::{AnnealingParams, ClassicalAnnealingSimulator};
use std::collections::HashMap;
use std::fmt::Write;
#[derive(Debug, Clone)]
pub struct MedicalResourceAllocation {
pub num_facilities: usize,
pub num_departments: usize,
pub num_resource_types: usize,
pub available_resources: Vec<Vec<f64>>,
pub resource_requirements: Vec<Vec<f64>>,
pub patient_demands: Vec<Vec<f64>>,
pub resource_costs: Vec<Vec<f64>>,
pub quality_targets: Vec<f64>,
pub emergency_reserves: Vec<f64>,
pub sharing_constraints: Vec<IndustryConstraint>,
}
impl MedicalResourceAllocation {
pub fn new(
num_facilities: usize,
num_departments: usize,
num_resource_types: usize,
available_resources: Vec<Vec<f64>>,
patient_demands: Vec<Vec<f64>>,
) -> ApplicationResult<Self> {
if available_resources.len() != num_facilities {
return Err(ApplicationError::InvalidConfiguration(
"Available resources must match number of facilities".to_string(),
));
}
if patient_demands.len() != num_facilities {
return Err(ApplicationError::InvalidConfiguration(
"Patient demands must match number of facilities".to_string(),
));
}
Ok(Self {
num_facilities,
num_departments,
num_resource_types,
available_resources,
resource_requirements: vec![vec![1.0; num_resource_types]; num_departments],
patient_demands,
resource_costs: vec![vec![1.0; num_resource_types]; num_facilities],
quality_targets: vec![0.95; num_departments],
emergency_reserves: vec![0.1; num_resource_types], sharing_constraints: Vec::new(),
})
}
pub fn set_resource_requirements(
&mut self,
requirements: Vec<Vec<f64>>,
) -> ApplicationResult<()> {
if requirements.len() != self.num_departments {
return Err(ApplicationError::InvalidConfiguration(
"Resource requirements must match number of departments".to_string(),
));
}
self.resource_requirements = requirements;
Ok(())
}
#[must_use]
pub fn calculate_utilization(&self, allocation: &MedicalResourceSolution) -> Vec<f64> {
let mut utilization = vec![0.0; self.num_resource_types];
for facility in 0..self.num_facilities {
for dept in 0..self.num_departments {
for resource in 0..self.num_resource_types {
let allocated = allocation.allocations[facility][dept][resource];
let available = self.available_resources[facility][resource];
if available > 0.0 {
utilization[resource] += allocated / available;
}
}
}
}
for util in &mut utilization {
*util /= self.num_facilities as f64;
}
utilization
}
#[must_use]
pub fn calculate_patient_satisfaction(&self, allocation: &MedicalResourceSolution) -> f64 {
let mut total_satisfaction = 0.0;
let mut total_patients = 0.0;
for facility in 0..self.num_facilities {
for dept in 0..self.num_departments {
let demand = self.patient_demands[facility][dept];
let capacity = self.calculate_department_capacity(facility, dept, allocation);
let service_ratio = if demand > 0.0 {
(capacity / demand).min(1.0)
} else {
1.0
};
total_satisfaction += service_ratio * demand;
total_patients += demand;
}
}
if total_patients > 0.0 {
total_satisfaction / total_patients
} else {
1.0
}
}
fn calculate_department_capacity(
&self,
facility: usize,
dept: usize,
allocation: &MedicalResourceSolution,
) -> f64 {
let mut capacity = f64::INFINITY;
for resource in 0..self.num_resource_types {
let allocated = allocation.allocations[facility][dept][resource];
let required = self.resource_requirements[dept][resource];
if required > 0.0 {
capacity = capacity.min(allocated / required);
}
}
if capacity == f64::INFINITY {
0.0
} else {
capacity
}
}
}
impl OptimizationProblem for MedicalResourceAllocation {
type Solution = MedicalResourceSolution;
type ObjectiveValue = f64;
fn description(&self) -> String {
format!(
"Medical resource allocation for {} facilities, {} departments, {} resource types",
self.num_facilities, self.num_departments, self.num_resource_types
)
}
fn size_metrics(&self) -> HashMap<String, usize> {
let mut metrics = HashMap::new();
metrics.insert("num_facilities".to_string(), self.num_facilities);
metrics.insert("num_departments".to_string(), self.num_departments);
metrics.insert("num_resource_types".to_string(), self.num_resource_types);
metrics.insert(
"num_constraints".to_string(),
self.sharing_constraints.len(),
);
metrics
}
fn validate(&self) -> ApplicationResult<()> {
if self.num_facilities == 0 {
return Err(ApplicationError::DataValidationError(
"At least one facility required".to_string(),
));
}
if self.num_departments == 0 {
return Err(ApplicationError::DataValidationError(
"At least one department required".to_string(),
));
}
if self.num_resource_types == 0 {
return Err(ApplicationError::DataValidationError(
"At least one resource type required".to_string(),
));
}
for facility in &self.available_resources {
for &resource in facility {
if resource < 0.0 {
return Err(ApplicationError::DataValidationError(
"Available resources must be non-negative".to_string(),
));
}
}
}
for facility in &self.patient_demands {
for &demand in facility {
if demand < 0.0 {
return Err(ApplicationError::DataValidationError(
"Patient demands must be non-negative".to_string(),
));
}
}
}
Ok(())
}
fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
let mut builder = QuboBuilder::new();
let precision = 20;
let mut var_counter = 0;
let mut var_map = HashMap::new();
let mut string_var_map = HashMap::new();
for facility in 0..self.num_facilities {
for dept in 0..self.num_departments {
for resource in 0..self.num_resource_types {
for level in 0..precision {
let var_name = format!("x_{facility}_{dept}_{resource}_{level}");
var_map.insert((facility, dept, resource, level), var_counter);
string_var_map.insert(var_name, var_counter);
var_counter += 1;
}
}
}
}
for facility in 0..self.num_facilities {
for dept in 0..self.num_departments {
let demand = self.patient_demands[facility][dept];
for resource in 0..self.num_resource_types {
for level in 0..precision {
let allocation = f64::from(level) / f64::from(precision);
let var_idx = var_map[&(facility, dept, resource, level)];
let required = self.resource_requirements[dept][resource];
let capacity_contribution = if required > 0.0 {
allocation / required
} else {
0.0
};
let service_benefit = -demand * capacity_contribution * 100.0; builder.add_bias(var_idx, service_benefit);
let cost = allocation
* self.available_resources[facility][resource]
* self.resource_costs[facility][resource];
builder.add_bias(var_idx, cost);
}
}
}
}
let constraint_penalty = 10_000.0;
for facility in 0..self.num_facilities {
for dept in 0..self.num_departments {
for resource in 0..self.num_resource_types {
let mut constraint_vars = Vec::new();
for level in 0..precision {
constraint_vars.push(var_map[&(facility, dept, resource, level)]);
}
for &var1 in &constraint_vars {
builder.add_bias(var1, -constraint_penalty);
for &var2 in &constraint_vars {
if var1 != var2 {
builder.add_coupling(var1, var2, constraint_penalty);
}
}
}
}
}
}
for facility in 0..self.num_facilities {
for resource in 0..self.num_resource_types {
let available = self.available_resources[facility][resource]
* (1.0 - self.emergency_reserves[resource]);
for level1 in 0..precision {
for dept1 in 0..self.num_departments {
for level2 in 0..precision {
for dept2 in (dept1 + 1)..self.num_departments {
let alloc1 = f64::from(level1) / f64::from(precision) * available;
let alloc2 = f64::from(level2) / f64::from(precision) * available;
if alloc1 + alloc2 > available {
let var1 = var_map[&(facility, dept1, resource, level1)];
let var2 = var_map[&(facility, dept2, resource, level2)];
builder.add_coupling(var1, var2, constraint_penalty);
}
}
}
}
}
}
}
Ok((builder.build(), string_var_map))
}
fn evaluate_solution(
&self,
solution: &Self::Solution,
) -> ApplicationResult<Self::ObjectiveValue> {
let satisfaction = self.calculate_patient_satisfaction(solution);
let total_cost = solution.total_cost;
Ok(satisfaction.mul_add(1000.0, -total_cost))
}
fn is_feasible(&self, solution: &Self::Solution) -> bool {
for facility in 0..self.num_facilities {
for resource in 0..self.num_resource_types {
let total_allocated: f64 = (0..self.num_departments)
.map(|dept| solution.allocations[facility][dept][resource])
.sum();
let available = self.available_resources[facility][resource]
* (1.0 - self.emergency_reserves[resource]);
if total_allocated > available + 1e-6 {
return false;
}
}
}
let satisfaction = self.calculate_patient_satisfaction(solution);
if satisfaction < 0.8 {
return false;
}
true
}
}
#[derive(Debug, Clone)]
pub struct MedicalResourceSolution {
pub allocations: Vec<Vec<Vec<f64>>>,
pub total_cost: f64,
pub patient_satisfaction: f64,
pub utilization_rates: Vec<f64>,
pub quality_metrics: HealthcareMetrics,
}
#[derive(Debug, Clone)]
pub struct HealthcareMetrics {
pub avg_wait_time: f64,
pub bed_occupancy: f64,
pub staff_utilization: f64,
pub equipment_utilization: f64,
pub emergency_response_time: f64,
pub quality_score: f64,
}
impl IndustrySolution for MedicalResourceSolution {
type Problem = MedicalResourceAllocation;
fn from_binary(problem: &Self::Problem, binary_solution: &[i8]) -> ApplicationResult<Self> {
let precision = 20;
let mut allocations =
vec![
vec![vec![0.0; problem.num_resource_types]; problem.num_departments];
problem.num_facilities
];
let mut var_idx = 0;
for facility in 0..problem.num_facilities {
for dept in 0..problem.num_departments {
for resource in 0..problem.num_resource_types {
for level in 0..precision {
if var_idx < binary_solution.len() && binary_solution[var_idx] == 1 {
let allocation_fraction = f64::from(level) / f64::from(precision);
let available = problem.available_resources[facility][resource]
* (1.0 - problem.emergency_reserves[resource]);
allocations[facility][dept][resource] = allocation_fraction * available;
break;
}
var_idx += 1;
}
}
}
}
let mut total_cost = 0.0;
for facility in 0..problem.num_facilities {
for dept in 0..problem.num_departments {
for resource in 0..problem.num_resource_types {
total_cost += allocations[facility][dept][resource]
* problem.resource_costs[facility][resource];
}
}
}
let solution = Self {
allocations,
total_cost,
patient_satisfaction: 0.0,
utilization_rates: Vec::new(),
quality_metrics: HealthcareMetrics {
avg_wait_time: 0.0,
bed_occupancy: 0.0,
staff_utilization: 0.0,
equipment_utilization: 0.0,
emergency_response_time: 0.0,
quality_score: 0.0,
},
};
let patient_satisfaction = problem.calculate_patient_satisfaction(&solution);
let utilization_rates = problem.calculate_utilization(&solution);
let quality_metrics = HealthcareMetrics {
avg_wait_time: 25.0, bed_occupancy: utilization_rates.get(0).copied().unwrap_or(0.0),
staff_utilization: utilization_rates.get(1).copied().unwrap_or(0.0),
equipment_utilization: utilization_rates.get(2).copied().unwrap_or(0.0),
emergency_response_time: 8.0, quality_score: patient_satisfaction,
};
Ok(Self {
allocations: solution.allocations,
total_cost,
patient_satisfaction,
utilization_rates,
quality_metrics,
})
}
fn summary(&self) -> HashMap<String, String> {
let mut summary = HashMap::new();
summary.insert(
"type".to_string(),
"Medical Resource Allocation".to_string(),
);
summary.insert("total_cost".to_string(), format!("${:.2}", self.total_cost));
summary.insert(
"patient_satisfaction".to_string(),
format!("{:.1}%", self.patient_satisfaction * 100.0),
);
summary.insert(
"avg_wait_time".to_string(),
format!("{:.1} minutes", self.quality_metrics.avg_wait_time),
);
summary.insert(
"bed_occupancy".to_string(),
format!("{:.1}%", self.quality_metrics.bed_occupancy * 100.0),
);
summary.insert(
"staff_utilization".to_string(),
format!("{:.1}%", self.quality_metrics.staff_utilization * 100.0),
);
summary.insert(
"quality_score".to_string(),
format!("{:.3}", self.quality_metrics.quality_score),
);
summary
}
fn metrics(&self) -> HashMap<String, f64> {
let mut metrics = HashMap::new();
metrics.insert("total_cost".to_string(), self.total_cost);
metrics.insert(
"patient_satisfaction".to_string(),
self.patient_satisfaction,
);
metrics.insert(
"avg_wait_time".to_string(),
self.quality_metrics.avg_wait_time,
);
metrics.insert(
"bed_occupancy".to_string(),
self.quality_metrics.bed_occupancy,
);
metrics.insert(
"staff_utilization".to_string(),
self.quality_metrics.staff_utilization,
);
metrics.insert(
"equipment_utilization".to_string(),
self.quality_metrics.equipment_utilization,
);
metrics.insert(
"emergency_response_time".to_string(),
self.quality_metrics.emergency_response_time,
);
metrics.insert(
"quality_score".to_string(),
self.quality_metrics.quality_score,
);
for (i, &util) in self.utilization_rates.iter().enumerate() {
metrics.insert(format!("resource_{i}_utilization"), util);
}
metrics
}
fn export_format(&self) -> ApplicationResult<String> {
let mut output = String::new();
output.push_str("# Medical Resource Allocation Report\n\n");
output.push_str("## Allocation Summary\n");
let _ = writeln!(output, "Total Cost: ${:.2}", self.total_cost);
let _ = write!(
output,
"Patient Satisfaction: {:.1}%\n",
self.patient_satisfaction * 100.0
);
let _ = write!(
output,
"Quality Score: {:.3}\n",
self.quality_metrics.quality_score
);
output.push_str("\n## Resource Utilization\n");
let _ = write!(
output,
"Bed Occupancy: {:.1}%\n",
self.quality_metrics.bed_occupancy * 100.0
);
let _ = write!(
output,
"Staff Utilization: {:.1}%\n",
self.quality_metrics.staff_utilization * 100.0
);
let _ = write!(
output,
"Equipment Utilization: {:.1}%\n",
self.quality_metrics.equipment_utilization * 100.0
);
output.push_str("\n## Performance Metrics\n");
let _ = write!(
output,
"Average Wait Time: {:.1} minutes\n",
self.quality_metrics.avg_wait_time
);
let _ = write!(
output,
"Emergency Response Time: {:.1} minutes\n",
self.quality_metrics.emergency_response_time
);
output.push_str("\n## Resource Allocations\n");
for (facility, facility_allocs) in self.allocations.iter().enumerate() {
let _ = writeln!(output, "### Facility {}", facility + 1);
for (dept, dept_allocs) in facility_allocs.iter().enumerate() {
let _ = write!(output, "Department {}: ", dept + 1);
for (resource, &allocation) in dept_allocs.iter().enumerate() {
let _ = write!(output, "R{resource}: {allocation:.1} ");
}
output.push_str("\n");
}
}
Ok(output)
}
}
#[derive(Debug, Clone)]
pub struct TreatmentPlanningOptimization {
pub num_patients: usize,
pub num_treatments: usize,
pub treatment_efficacy: Vec<Vec<f64>>,
pub treatment_costs: Vec<f64>,
pub treatment_durations: Vec<f64>,
pub patient_priorities: Vec<f64>,
pub compatibility_constraints: Vec<Vec<bool>>,
pub resource_requirements: Vec<HashMap<String, f64>>,
pub available_resources: HashMap<String, f64>,
}
impl TreatmentPlanningOptimization {
pub fn new(
num_patients: usize,
num_treatments: usize,
treatment_efficacy: Vec<Vec<f64>>,
treatment_costs: Vec<f64>,
) -> ApplicationResult<Self> {
if treatment_efficacy.len() != num_patients {
return Err(ApplicationError::InvalidConfiguration(
"Treatment efficacy matrix dimension mismatch".to_string(),
));
}
Ok(Self {
num_patients,
num_treatments,
treatment_efficacy,
treatment_costs,
treatment_durations: vec![1.0; num_treatments],
patient_priorities: vec![1.0; num_patients],
compatibility_constraints: vec![vec![true; num_treatments]; num_patients],
resource_requirements: vec![HashMap::new(); num_treatments],
available_resources: HashMap::new(),
})
}
#[must_use]
pub fn calculate_benefit(&self, plan: &TreatmentPlan) -> f64 {
let mut total_benefit = 0.0;
for (patient, assignments) in plan.patient_treatments.iter().enumerate() {
for (treatment, &assigned) in assignments.iter().enumerate() {
if assigned {
let efficacy = self.treatment_efficacy[patient][treatment];
let priority = self.patient_priorities[patient];
total_benefit += efficacy * priority;
}
}
}
total_benefit
}
}
#[derive(Debug, Clone)]
pub struct TreatmentPlan {
pub patient_treatments: Vec<Vec<bool>>,
pub total_cost: f64,
pub total_benefit: f64,
pub resource_utilization: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct BinaryMedicalResourceAllocation {
inner: MedicalResourceAllocation,
}
impl BinaryMedicalResourceAllocation {
#[must_use]
pub const fn new(inner: MedicalResourceAllocation) -> Self {
Self { inner }
}
}
impl OptimizationProblem for BinaryMedicalResourceAllocation {
type Solution = Vec<i8>;
type ObjectiveValue = f64;
fn description(&self) -> String {
self.inner.description()
}
fn size_metrics(&self) -> HashMap<String, usize> {
self.inner.size_metrics()
}
fn validate(&self) -> ApplicationResult<()> {
self.inner.validate()
}
fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
self.inner.to_qubo()
}
fn evaluate_solution(
&self,
solution: &Self::Solution,
) -> ApplicationResult<Self::ObjectiveValue> {
let resource_solution = MedicalResourceSolution::from_binary(&self.inner, solution)?;
self.inner.evaluate_solution(&resource_solution)
}
fn is_feasible(&self, solution: &Self::Solution) -> bool {
if let Ok(resource_solution) = MedicalResourceSolution::from_binary(&self.inner, solution) {
self.inner.is_feasible(&resource_solution)
} else {
false
}
}
}
pub fn create_benchmark_problems(
size: usize,
) -> ApplicationResult<Vec<Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>>>
{
let mut problems = Vec::new();
let available_resources = vec![
vec![50.0, 20.0, 10.0], vec![30.0, 15.0, 8.0], ];
let patient_demands = vec![
vec![40.0, 25.0, 15.0], vec![20.0, 18.0, 12.0], ];
let hospital_allocation = MedicalResourceAllocation::new(
2, 3, 3, available_resources,
patient_demands,
)?;
problems.push(
Box::new(BinaryMedicalResourceAllocation::new(hospital_allocation))
as Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>,
);
if size >= 4 {
let large_resources = vec![vec![100.0, 50.0, 25.0, 15.0]; size];
let large_demands = vec![vec![80.0, 40.0, 30.0, 20.0]; size];
let large_allocation = MedicalResourceAllocation::new(
size,
4, 4, large_resources,
large_demands,
)?;
problems.push(
Box::new(BinaryMedicalResourceAllocation::new(large_allocation))
as Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>,
);
}
Ok(problems)
}
pub fn solve_medical_resource_allocation(
problem: &MedicalResourceAllocation,
params: Option<AnnealingParams>,
) -> ApplicationResult<MedicalResourceSolution> {
let (qubo, _var_map) = problem.to_qubo()?;
let ising = IsingModel::from_qubo(&qubo);
let annealing_params = params.unwrap_or_else(|| {
let mut p = AnnealingParams::default();
p.num_sweeps = 20_000;
p.num_repetitions = 30;
p.initial_temperature = 4.0;
p.final_temperature = 0.01;
p
});
let simulator = ClassicalAnnealingSimulator::new(annealing_params)
.map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
let result = simulator
.solve(&ising)
.map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
MedicalResourceSolution::from_binary(problem, &result.best_spins)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_medical_resource_allocation_creation() {
let available_resources = vec![vec![50.0, 20.0, 10.0], vec![30.0, 15.0, 8.0]];
let patient_demands = vec![vec![40.0, 25.0, 15.0], vec![20.0, 18.0, 12.0]];
let allocation =
MedicalResourceAllocation::new(2, 3, 3, available_resources, patient_demands)
.expect("Failed to create medical resource allocation");
assert_eq!(allocation.num_facilities, 2);
assert_eq!(allocation.num_departments, 3);
assert_eq!(allocation.num_resource_types, 3);
}
#[test]
fn test_patient_satisfaction_calculation() {
let available_resources = vec![vec![100.0, 50.0]];
let patient_demands = vec![vec![80.0, 40.0]];
let problem = MedicalResourceAllocation::new(1, 2, 2, available_resources, patient_demands)
.expect("Failed to create medical resource allocation");
let solution = MedicalResourceSolution {
allocations: vec![vec![vec![80.0, 20.0], vec![20.0, 30.0]]],
total_cost: 1000.0,
patient_satisfaction: 0.0,
utilization_rates: Vec::new(),
quality_metrics: HealthcareMetrics {
avg_wait_time: 0.0,
bed_occupancy: 0.0,
staff_utilization: 0.0,
equipment_utilization: 0.0,
emergency_response_time: 0.0,
quality_score: 0.0,
},
};
let satisfaction = problem.calculate_patient_satisfaction(&solution);
assert!(satisfaction >= 0.0 && satisfaction <= 1.0);
}
#[test]
fn test_treatment_planning_creation() {
let efficacy = vec![vec![0.8, 0.6, 0.9], vec![0.7, 0.9, 0.5]];
let costs = vec![1000.0, 1500.0, 800.0];
let planning = TreatmentPlanningOptimization::new(2, 3, efficacy, costs)
.expect("Failed to create treatment planning optimization");
assert_eq!(planning.num_patients, 2);
assert_eq!(planning.num_treatments, 3);
}
#[test]
fn test_benchmark_problems() {
let problems = create_benchmark_problems(4).expect("Failed to create benchmark problems");
assert_eq!(problems.len(), 2);
for problem in &problems {
assert!(problem.validate().is_ok());
}
}
}