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_two_max_values<I>(iter: I) -> Option<(i64, i64)>
where
I: IntoIterator<Item = i64>,
{
let mut iter = iter.into_iter();
let first = iter.next()?;
let second = iter.next()?;
let (mut max1, mut max2) = if first > second {
(first, second)
} else {
(second, first)
};
for value in iter {
if value > max1 {
max2 = max1;
max1 = value;
} else if value > max2 {
max2 = value;
}
}
Some((max1, max2))
}
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 values = Vec::new();
for (task, &assigned_count) in task_assigned.iter().enumerate() {
if assigned_count < max_tile_occurrences {
let value = -distance_matrix.get(agent, task) - prices[task];
values.push((task, value));
}
}
if values.len() < 2 {
return None; }
let (max1, max2) = find_two_max_values(values.iter().map(|&(_, value)| value))?;
let best_task = values.iter().find(|&&(_, value)| value == max1)?.0;
Some((best_task, max1, max2))
}
impl Solve for Auction {
fn solve(&mut self, distance_matrix: &DistanceMatrix) -> Result<Vec<usize>, PhomoError> {
if distance_matrix.columns < 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());
}
}
}
if assignment.iter().any(|&a| a.is_none()) {
return Err(SolverError::from(AuctionError::UnassignedAgents).into());
}
Ok(assignment.into_iter().map(|a| a.unwrap()).collect())
}
}