qualia_core_db/specialized_libs/computational_economics/
mechanism.rs1pub const MAX_AGENTS: usize = 32;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum MechanismError {
16 InvalidInput,
17 NonFinite,
18 BufferTooSmall,
19 PropertyViolated,
20}
21
22#[derive(Debug, Clone, Copy)]
24#[repr(C)]
25pub struct MechanismReport {
26 pub individual_rationality: bool,
27 pub budget_balance: bool,
28 pub strategy_proof: bool,
29 pub total_payment: f64,
30 pub total_surplus: f64,
31}
32
33fn require_finite(x: f64) -> Result<(), MechanismError> {
34 if x.is_finite() {
35 Ok(())
36 } else {
37 Err(MechanismError::NonFinite)
38 }
39}
40
41pub fn check_individual_rationality(
44 valuations: &[f64],
45 payments: &[f64],
46) -> Result<bool, MechanismError> {
47 if valuations.is_empty() || valuations.len() != payments.len() {
48 return Err(MechanismError::InvalidInput);
49 }
50 if valuations.len() > MAX_AGENTS {
51 return Err(MechanismError::BufferTooSmall);
52 }
53 for i in 0..valuations.len() {
54 require_finite(valuations[i])?;
55 require_finite(payments[i])?;
56 if payments[i] > valuations[i] {
57 return Ok(false);
58 }
59 }
60 Ok(true)
61}
62
63pub fn check_budget_balance(payments: &[f64]) -> Result<(bool, f64), MechanismError> {
68 if payments.is_empty() {
69 return Err(MechanismError::InvalidInput);
70 }
71 if payments.len() > MAX_AGENTS {
72 return Err(MechanismError::BufferTooSmall);
73 }
74 let mut total = 0.0;
75 for p in payments {
76 require_finite(*p)?;
77 total += p;
78 }
79 Ok((total >= -1e-10, total))
80}
81
82pub fn vickrey_clarke_groves_payment_into(
87 valuations: &[f64],
88 out: &mut [f64],
89) -> Result<f64, MechanismError> {
90 if valuations.is_empty() || out.len() < valuations.len() {
91 return Err(MechanismError::BufferTooSmall);
92 }
93 if valuations.len() > MAX_AGENTS {
94 return Err(MechanismError::BufferTooSmall);
95 }
96 for v in valuations {
97 require_finite(*v)?;
98 if *v < 0.0 {
99 return Err(MechanismError::InvalidInput);
100 }
101 }
102 let n = valuations.len();
103 let mut winner = 0;
105 let mut highest = valuations[0];
106 for i in 1..n {
107 if valuations[i] > highest {
108 highest = valuations[i];
109 winner = i;
110 }
111 }
112 let mut second = 0.0;
114 for i in 0..n {
115 if i == winner {
116 continue;
117 }
118 if valuations[i] > second {
119 second = valuations[i];
120 }
121 }
122 for i in 0..n {
123 out[i] = if i == winner { second } else { 0.0 };
124 }
125 Ok(second)
126}
127
128pub fn check_strategy_proofness_2x2(
140 valuation_matrix: &[f64], allocation_rule: &[bool], payment_rule: &[f64], ) -> Result<bool, MechanismError> {
144 if valuation_matrix.len() < 4 || allocation_rule.len() < 4 || payment_rule.len() < 4 {
145 return Err(MechanismError::InvalidInput);
146 }
147 for v in valuation_matrix {
148 require_finite(*v)?;
149 }
150 for p in payment_rule {
151 require_finite(*p)?;
152 }
153 for true_type in 0..2 {
156 for opponent_type in 0..2 {
157 let truthful_alloc = allocation_rule[true_type * 2 + opponent_type];
159 let truthful_payment = payment_rule[true_type * 2 + opponent_type];
160 let truthful_utility = if truthful_alloc {
161 valuation_matrix[0 * 2 + true_type] - truthful_payment
162 } else {
163 -truthful_payment
164 };
165 let misreport_type = 1 - true_type;
167 let misreport_alloc = allocation_rule[misreport_type * 2 + opponent_type];
168 let misreport_payment = payment_rule[misreport_type * 2 + opponent_type];
169 let misreport_utility = if misreport_alloc {
170 valuation_matrix[0 * 2 + true_type] - misreport_payment
171 } else {
172 -misreport_payment
173 };
174 if misreport_utility > truthful_utility + 1e-10 {
175 return Ok(false);
176 }
177 }
178 }
179 Ok(true)
180}
181
182pub fn mechanism_report(
188 valuations: &[f64],
189 payments: &[f64],
190 allocations: &[bool],
191) -> Result<MechanismReport, MechanismError> {
192 if valuations.is_empty()
193 || valuations.len() != payments.len()
194 || valuations.len() != allocations.len()
195 {
196 return Err(MechanismError::InvalidInput);
197 }
198 if valuations.len() > MAX_AGENTS {
199 return Err(MechanismError::BufferTooSmall);
200 }
201 let ir = check_individual_rationality(valuations, payments)?;
202 let (bb, total_payment) = check_budget_balance(payments)?;
203 let mut total_surplus = 0.0;
204 for i in 0..valuations.len() {
205 if allocations[i] {
206 total_surplus += valuations[i];
207 }
208 total_surplus -= payments[i];
209 }
210 Ok(MechanismReport {
211 individual_rationality: ir,
212 budget_balance: bb,
213 strategy_proof: false,
214 total_payment,
215 total_surplus,
216 })
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 fn approx(a: f64, b: f64, tol: f64) -> bool {
224 (a - b).abs() < tol
225 }
226
227 #[test]
228 fn ir_holds_when_payments_leq_valuations() {
229 let v = [10.0, 20.0, 15.0];
230 let p = [5.0, 15.0, 10.0];
231 assert!(check_individual_rationality(&v, &p).unwrap());
232 }
233
234 #[test]
235 fn ir_violated_when_payment_exceeds_valuation() {
236 let v = [10.0, 20.0, 15.0];
237 let p = [5.0, 25.0, 10.0]; assert!(!check_individual_rationality(&v, &p).unwrap());
239 }
240
241 #[test]
242 fn budget_balance_balanced() {
243 let p = [10.0, -10.0];
245 let (balanced, net) = check_budget_balance(&p).unwrap();
246 assert!(balanced);
247 assert!(approx(net, 0.0, 1e-9));
248 }
249
250 #[test]
251 fn budget_balance_no_deficit() {
252 let p = [15.0, 0.0, 0.0]; let (balanced, net) = check_budget_balance(&p).unwrap();
254 assert!(balanced);
255 assert!(approx(net, 15.0, 1e-9));
256 }
257
258 #[test]
259 fn budget_balance_deficit() {
260 let p = [-5.0, -5.0]; let (balanced, net) = check_budget_balance(&p).unwrap();
262 assert!(!balanced);
263 assert!(approx(net, -10.0, 1e-9));
264 }
265
266 #[test]
267 fn vcg_second_price() {
268 let v = [10.0, 20.0, 15.0];
270 let mut p = [0.0f64; 3];
271 let revenue = vickrey_clarke_groves_payment_into(&v, &mut p).unwrap();
272 assert!(approx(p[1], 15.0, 1e-9));
273 assert!(approx(p[0], 0.0, 1e-9));
274 assert!(approx(p[2], 0.0, 1e-9));
275 assert!(approx(revenue, 15.0, 1e-9));
276 }
277
278 #[test]
279 fn vcg_two_bidders() {
280 let v = [10.0, 20.0];
281 let mut p = [0.0f64; 2];
282 let revenue = vickrey_clarke_groves_payment_into(&v, &mut p).unwrap();
283 assert!(approx(p[1], 10.0, 1e-9));
284 assert!(approx(revenue, 10.0, 1e-9));
285 }
286
287 #[test]
288 fn strategy_proof_vickrey() {
289 let val_matrix = [10.0, 20.0, 15.0, 15.0]; let alloc = [false, false, true, true];
302 let payment = [0.0, 0.0, 15.0, 15.0];
306 let sp = check_strategy_proofness_2x2(&val_matrix, &alloc, &payment).unwrap();
307 assert!(sp, "Vickrey should be strategy-proof");
308 }
309
310 #[test]
311 fn strategy_proof_first_price_not() {
312 let val_matrix = [10.0, 20.0, 5.0, 5.0];
316 let alloc = [true, true, true, true]; let payment = [10.0, 10.0, 20.0, 20.0]; let sp = check_strategy_proofness_2x2(&val_matrix, &alloc, &payment).unwrap();
323 assert!(!sp, "First-price should not be strategy-proof");
324 }
325
326 #[test]
327 fn mechanism_report_vickrey() {
328 let v = [10.0, 20.0, 15.0];
329 let mut p = [0.0f64; 3];
330 vickrey_clarke_groves_payment_into(&v, &mut p).unwrap();
331 let alloc = [false, true, false]; let report = mechanism_report(&v, &p, &alloc).unwrap();
333 assert!(report.individual_rationality); assert!(report.budget_balance); assert!(approx(report.total_payment, 15.0, 1e-9));
336 assert!(approx(report.total_surplus, 20.0 - 15.0, 1e-9)); }
338
339 #[test]
340 fn empty_rejected() {
341 assert_eq!(
342 check_individual_rationality(&[], &[]).unwrap_err(),
343 MechanismError::InvalidInput
344 );
345 }
346}