use serde::Serialize;
#[derive(Clone, Debug)]
pub struct DominanceChoice {
pub options: Vec<f64>,
pub chosen: usize,
}
impl DominanceChoice {
pub fn is_dominated(&self) -> bool {
match self.options.get(self.chosen) {
Some(&picked) => self.options.iter().any(|&v| v > picked),
None => false,
}
}
}
pub fn rationality_score(choices: &[DominanceChoice]) -> f64 {
if choices.is_empty() {
return 1.0;
}
let bad = choices.iter().filter(|c| c.is_dominated()).count();
1.0 - bad as f64 / choices.len() as f64
}
pub fn has_money_pump(prefs: &[(usize, usize)], n_items: usize) -> bool {
let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n_items];
for &(a, b) in prefs {
if a < n_items && b < n_items {
adj[a].push(b);
}
}
fn visit(u: usize, adj: &[Vec<usize>], color: &mut [u8]) -> bool {
color[u] = 1;
for &v in &adj[u] {
if color[v] == 1 || (color[v] == 0 && visit(v, adj, color)) {
return true;
}
}
color[u] = 2;
false
}
let mut color = vec![0u8; n_items];
(0..n_items).any(|u| color[u] == 0 && visit(u, &adj, &mut color))
}
#[derive(Clone, Debug, Serialize)]
pub struct EconRationalityReport {
pub rationality_score: f64,
pub dominance_violations: usize,
pub has_money_pump: bool,
}
pub fn assess_rationality(
choices: &[DominanceChoice],
prefs: &[(usize, usize)],
n_items: usize,
) -> EconRationalityReport {
EconRationalityReport {
rationality_score: rationality_score(choices),
dominance_violations: choices.iter().filter(|c| c.is_dominated()).count(),
has_money_pump: has_money_pump(prefs, n_items),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn choice(options: &[f64], chosen: usize) -> DominanceChoice {
DominanceChoice {
options: options.to_vec(),
chosen,
}
}
#[test]
fn always_picking_the_best_is_rational() {
let choices = vec![choice(&[0.1, 0.3, 0.2], 1), choice(&[0.5, 0.4], 0)];
assert_eq!(rationality_score(&choices), 1.0);
assert!(!choices[0].is_dominated());
}
#[test]
fn leaving_value_on_the_table_is_a_violation() {
let choices = vec![
choice(&[0.1, 0.3, 0.2], 0), choice(&[0.5, 0.4], 0), ];
assert_eq!(rationality_score(&choices), 0.5);
assert!(choices[0].is_dominated());
}
#[test]
fn intransitive_preferences_are_a_money_pump() {
assert!(has_money_pump(&[(0, 1), (1, 2), (2, 0)], 3));
}
#[test]
fn transitive_preferences_have_no_pump() {
assert!(!has_money_pump(&[(0, 1), (1, 2), (0, 2)], 3));
}
#[test]
fn combined_report() {
let choices = vec![choice(&[0.2, 0.1], 1)]; let r = assess_rationality(&choices, &[(0, 1), (1, 0)], 2);
assert_eq!(r.dominance_violations, 1);
assert!(r.has_money_pump);
assert_eq!(r.rationality_score, 0.0);
}
}