use crate::error::PhomoError;
use crate::solvers::Solve;
use crate::solvers::SolverConfig;
use crate::DistanceMatrix;
use super::error::AuctionError;
use super::error::SolverError;
#[derive(Debug)]
pub struct Auction {
epsilon: i64,
config: SolverConfig,
}
impl Default for Auction {
fn default() -> Self {
Self {
epsilon: 1,
config: SolverConfig::default(),
}
}
}
impl Auction {
pub fn new(epsilon: i64, config: SolverConfig) -> Self {
Self { epsilon, config }
}
}
fn find_best_and_second_best(
agent: usize,
distance_matrix: &DistanceMatrix,
prices: &[i64],
task_assigned: &[usize],
max_tile_occurrences: usize,
) -> Option<(usize, i64, i64)> {
let mut best_task = usize::MAX;
let mut best_value = i64::MIN;
let mut second_best_value = i64::MIN;
for (task, &assigned_count) in task_assigned.iter().enumerate() {
if assigned_count < max_tile_occurrences {
let value = -distance_matrix.get(agent, task) - prices[task];
if value > best_value {
second_best_value = best_value;
best_value = value;
best_task = task;
} else if value > second_best_value {
second_best_value = value;
}
}
}
if best_task == usize::MAX || second_best_value == i64::MIN {
return None; }
Some((best_task, best_value, second_best_value))
}
impl Solve for Auction {
fn solve(&mut self, distance_matrix: &DistanceMatrix) -> Result<Vec<usize>, PhomoError> {
if distance_matrix.columns * self.config.max_tile_occurrences < distance_matrix.rows {
return Err(SolverError::TooFewColumns {
rows: distance_matrix.rows,
columns: distance_matrix.columns,
}
.into());
}
let num_agents = distance_matrix.rows;
let num_tasks = distance_matrix.columns;
let mut prices = vec![0; num_tasks]; let mut assignment = vec![None; num_agents]; let mut task_assigned = vec![0; num_tasks];
while assignment.iter().any(|&a| a.is_none()) {
for (agent, assigned_task) in assignment.iter_mut().enumerate().take(num_agents) {
if assigned_task.is_some() {
continue; }
if let Some((best_task, best_value, second_best_value)) = find_best_and_second_best(
agent,
distance_matrix,
&prices,
&task_assigned,
self.config.max_tile_occurrences,
) {
let bid = best_value - second_best_value + self.epsilon;
prices[best_task] += bid; *assigned_task = Some(best_task); task_assigned[best_task] += 1; } else {
return Err(SolverError::from(AuctionError::UnassignedAgents).into());
}
}
}
Ok(assignment.into_iter().map(|a| a.unwrap()).collect())
}
}