1use rbp_cards::*;
2use rbp_clustering::*;
3use rbp_core::*;
4use rbp_gameplay::*;
5use rbp_mccfr::Decision;
6use rbp_nlhe::*;
7use rbp_database::*;
8use rbp_transport::*;
9use std::sync::Arc;
10use tokio_postgres::Client;
11
12const N_NEIGHBORS: i64 = 6;
13
14fn api_sample_from_row(row: tokio_postgres::Row) -> ApiSample {
19 ApiSample {
20 obs: Observation::from(row.get::<_, i64>("obs")).to_string(),
21 abs: Abstraction::from(row.get::<_, i16>("abs")).to_string(),
22 equity: row.get::<_, f32>("equity"),
23 density: row.get::<_, f32>("density"),
24 distance: row.try_get::<_, f32>("distance").unwrap_or_default(),
25 }
26}
27
28fn decision_from_row(row: tokio_postgres::Row) -> Decision<NlheEdge> {
29 Decision {
30 edge: NlheEdge::from(row.get::<_, i64>("edge") as u64),
31 mass: Probability::from(row.get::<_, f32>("weight")),
32 counts: row.get::<_, i32>("counts") as u32,
33 }
34}
35
36fn api_strategy_from(strategy: Strategy) -> ApiStrategy {
37 let history = strategy.info().subgame();
38 let present = strategy.info().bucket();
39 let choices = strategy.info().choices();
40 ApiStrategy {
41 history: i64::from(history),
42 present: i16::from(present),
43 choices: i64::from(choices),
44 accumulated: strategy
45 .accumulated()
46 .iter()
47 .map(|(edge, policy)| (edge.to_string(), *policy))
48 .collect(),
49 counts: strategy
50 .counts()
51 .iter()
52 .map(|(edge, count)| (edge.to_string(), *count))
53 .collect(),
54 }
55}
56
57pub struct API(Arc<Client>);
58
59impl From<Arc<Client>> for API {
60 fn from(client: Arc<Client>) -> Self {
61 Self(client)
62 }
63}
64
65impl API {
66 pub fn new(client: Arc<Client>) -> Self {
67 Self(client)
68 }
69 pub fn client(&self) -> &Arc<Client> {
70 &self.0
71 }
72}
73
74impl API {
76 pub async fn obs_to_abs(&self, obs: Observation) -> anyhow::Result<Abstraction> {
77 let iso = Isomorphism::from(obs);
78 let idx = i64::from(iso);
79 let sql = const_format::concatcp!(
80 "SELECT abs FROM ",
81 ISOMORPHISM,
82 " WHERE obs = $1"
83 );
84 self.0
85 .query_one(sql, &[&idx])
86 .await
87 .map(|row| Abstraction::from(row.get::<_, i16>(0)))
88 .map_err(|e| anyhow::anyhow!("fetch abstraction: {}", e))
89 }
90 pub async fn metric(&self, street: Street) -> anyhow::Result<Metric> {
91 let s = street as i16;
92 let sql = const_format::concatcp!(
93 "SELECT tri, dx FROM ",
94 METRIC,
95 " WHERE street = $1"
96 );
97 let rows = self
98 .0
99 .query(sql, &[&s])
100 .await
101 .map_err(|e| anyhow::anyhow!("fetch metric: {}", e))?;
102 let mut metric = Metric::new(street);
103 for row in rows {
104 let tri = row.get::<_, i32>(0);
105 let dx = row.get::<_, Energy>(1);
106 metric.set(Pair::from(tri), dx);
107 }
108 Ok(metric)
109 }
110}
111
112impl API {
114 pub async fn abs_equity(&self, abs: Abstraction) -> anyhow::Result<Probability> {
115 let abs = i16::from(abs);
116 let sql = const_format::concatcp!(
117 "SELECT equity FROM ",
118 ABSTRACTION,
119 " WHERE abs = $1"
120 );
121 self.0
122 .query_one(sql, &[&abs])
123 .await
124 .map(|row| Probability::from(row.get::<_, f32>(0)))
125 .map_err(|e| anyhow::anyhow!("fetch abstraction equity: {}", e))
126 }
127 pub async fn obs_equity(&self, obs: Observation) -> anyhow::Result<Probability> {
128 let iso = i64::from(Isomorphism::from(obs));
129 let river: &str = const_format::concatcp!(
130 "SELECT equity ",
131 "FROM ",
132 ISOMORPHISM,
133 " ",
134 "WHERE obs = $1"
135 );
136 let other: &str = const_format::concatcp!(
137 "SELECT SUM(t.dx * a.equity) ",
138 "FROM ",
139 TRANSITIONS,
140 " t ",
141 "JOIN ",
142 ISOMORPHISM,
143 " e ON e.abs = t.prev ",
144 "JOIN ",
145 ABSTRACTION,
146 " a ON a.abs = t.next ",
147 "WHERE e.obs = $1"
148 );
149 let sql = if obs.street() == Street::Rive {
150 river
151 } else {
152 other
153 };
154 Ok(self
155 .0
156 .query_one(sql, &[&iso])
157 .await
158 .map_err(|e| anyhow::anyhow!("fetch observation equity: {}", e))?
159 .get::<_, f32>(0)
160 .into())
161 }
162}
163
164impl API {
166 pub async fn abs_distance(
167 &self,
168 abs1: Abstraction,
169 abs2: Abstraction,
170 ) -> anyhow::Result<Energy> {
171 if abs1.street() != abs2.street() {
172 return Err(anyhow::anyhow!(
173 "abstractions must be from the same street"
174 ));
175 }
176 if abs1 == abs2 {
177 return Ok(0 as Energy);
178 }
179 let pair = Pair::from((&abs1, &abs2));
180 let tri = i32::from(pair);
181 let sql = const_format::concatcp!("SELECT dx FROM ", METRIC, " WHERE tri = $1");
182 self.0
183 .query_one(sql, &[&tri])
184 .await
185 .map(|row| row.get::<_, Energy>(0))
186 .map_err(|e| anyhow::anyhow!("fetch distance: {}", e))
187 }
188 pub async fn obs_distance(
189 &self,
190 obs1: Observation,
191 obs2: Observation,
192 ) -> anyhow::Result<Energy> {
193 if obs1.street() != obs2.street() {
194 return Err(anyhow::anyhow!(
195 "observations must be from the same street"
196 ));
197 }
198 let (ref hx, ref hy, ref metric) = tokio::try_join!(
199 self.obs_histogram(obs1),
200 self.obs_histogram(obs2),
201 self.metric(obs1.street().next())
202 )?;
203 Ok(Sinkhorn::from((hx, hy, metric)).minimize().cost())
204 }
205}
206
207impl API {
209 pub async fn abs_population(&self, abs: Abstraction) -> anyhow::Result<usize> {
210 let abs = i16::from(abs);
211 let sql = const_format::concatcp!(
212 "SELECT population FROM ",
213 ABSTRACTION,
214 " WHERE abs = $1"
215 );
216 self.0
217 .query_one(sql, &[&abs])
218 .await
219 .map(|row| row.get::<_, i64>(0) as usize)
220 .map_err(|e| anyhow::anyhow!("fetch abstraction population: {}", e))
221 }
222 pub async fn obs_population(&self, obs: Observation) -> anyhow::Result<usize> {
223 let iso = i64::from(Isomorphism::from(obs));
224 let sql: &str = const_format::concatcp!(
225 "SELECT population ",
226 "FROM ",
227 ABSTRACTION,
228 " a ",
229 "JOIN ",
230 ISOMORPHISM,
231 " e ON e.abs = a.abs ",
232 "WHERE e.obs = $1"
233 );
234 Ok(self
235 .0
236 .query_one(sql, &[&iso])
237 .await
238 .map_err(|e| anyhow::anyhow!("fetch observation population: {}", e))?
239 .get::<_, i64>(0) as usize)
240 }
241}
242
243impl API {
245 pub async fn abs_histogram(&self, abs: Abstraction) -> anyhow::Result<Histogram> {
246 let abs_i = i16::from(abs);
247 let sql = const_format::concatcp!(
248 "SELECT next, dx FROM ",
249 TRANSITIONS,
250 " WHERE prev = $1"
251 );
252 let street = abs.street().next();
253 let rows = self
254 .0
255 .query(sql, &[&abs_i])
256 .await
257 .map_err(|e| anyhow::anyhow!("fetch abstraction histogram: {}", e))?;
258 Ok(rows
259 .iter()
260 .map(|row| (row.get::<_, i16>(0), row.get::<_, Energy>(1)))
261 .map(|(next, dx)| (Abstraction::from(next), (dx * 1000.0) as usize))
262 .fold(Histogram::empty(street), |mut h, (next, dx)| {
263 h.set(next, dx);
264 h
265 }))
266 }
267 pub async fn obs_histogram(&self, obs: Observation) -> anyhow::Result<Histogram> {
268 let idx = i64::from(Isomorphism::from(obs));
269 let mass = obs.street().n_children() as f32;
270 let sql: &str = const_format::concatcp!(
271 "SELECT next, ",
272 "dx ",
273 "FROM ",
274 TRANSITIONS,
275 " t ",
276 "JOIN ",
277 ISOMORPHISM,
278 " e ON e.abs = t.prev ",
279 "WHERE e.obs = $1"
280 );
281 let street = obs.street().next();
282 Ok(self
283 .0
284 .query(sql, &[&idx])
285 .await
286 .map_err(|e| anyhow::anyhow!("fetch observation histogram: {}", e))?
287 .iter()
288 .map(|row| (row.get::<_, i16>(0), row.get::<_, Energy>(1)))
289 .map(|(next, dx)| (next, (dx * mass).round() as usize))
290 .map(|(next, dx)| (Abstraction::from(next), dx))
291 .fold(Histogram::empty(street), |mut h, (next, dx)| {
292 h.set(next, dx);
293 h
294 }))
295 }
296}
297
298impl API {
300 pub async fn exp_wrt_str(&self, str: Street) -> anyhow::Result<ApiSample> {
301 self.exp_wrt_obs(Observation::from(str)).await
302 }
303 pub async fn exp_wrt_obs(&self, obs: Observation) -> anyhow::Result<ApiSample> {
304 let sql: &str = const_format::concatcp!(
305 "SELECT e.obs, ",
306 "a.abs, ",
307 "a.equity::REAL, ",
308 "a.population::REAL / $2 AS density ",
309 "FROM ",
310 ISOMORPHISM,
311 " e ",
312 "JOIN ",
313 ABSTRACTION,
314 " a ON a.abs = e.abs ",
315 "WHERE e.obs = $1"
316 );
317 let n = obs.street().n_observations() as f32;
318 let iso = i64::from(Isomorphism::from(obs));
319 let row = self
320 .0
321 .query_one(sql, &[&iso, &n])
322 .await
323 .map_err(|e| anyhow::anyhow!("explore with respect to observation: {}", e))?;
324 Ok(api_sample_from_row(row))
325 }
326 pub async fn exp_wrt_abs(&self, abs: Abstraction) -> anyhow::Result<ApiSample> {
327 let sql: &str = const_format::concatcp!(
328 "WITH sample AS ( ",
329 "SELECT a.abs, ",
330 "a.population, ",
331 "a.equity, ",
332 "FLOOR(RANDOM() * a.population)::INTEGER AS i ",
333 "FROM ",
334 ABSTRACTION,
335 " a ",
336 "WHERE a.abs = $1 ",
337 ") ",
338 "SELECT e.obs, ",
339 "s.abs, ",
340 "s.equity::REAL, ",
341 "s.population::REAL / $2 AS density ",
342 "FROM sample s ",
343 "JOIN ",
344 ISOMORPHISM,
345 " e ON e.abs = s.abs AND e.position = s.i ",
346 "LIMIT 1"
347 );
348 let n = abs.street().n_isomorphisms() as f32;
349 let abs = i16::from(abs);
350 let row = self
351 .0
352 .query_one(sql, &[&abs, &n])
353 .await
354 .map_err(|e| anyhow::anyhow!("explore with respect to abstraction: {}", e))?;
355 Ok(api_sample_from_row(row))
356 }
357}
358
359impl API {
361 pub async fn abs_nearby(&self, abs: Abstraction) -> anyhow::Result<Vec<(Abstraction, Energy)>> {
362 let abs = i16::from(abs);
363 let sql: &str = const_format::concatcp!(
364 "SELECT a.abs, ",
365 "m.dx ",
366 "FROM ",
367 ABSTRACTION,
368 " a ",
369 "JOIN ",
370 METRIC,
371 " m ON m.tri = get_pair_tri(a.abs, $1) ",
372 "WHERE a.abs != $1 ",
373 "ORDER BY m.dx ASC ",
374 "LIMIT $2"
375 );
376 Ok(self
377 .0
378 .query(sql, &[&abs, &N_NEIGHBORS])
379 .await
380 .map_err(|e| anyhow::anyhow!("fetch nearby abstractions: {}", e))?
381 .iter()
382 .map(|row| (row.get::<_, i16>(0), row.get::<_, Energy>(1)))
383 .map(|(abs, distance)| (Abstraction::from(abs), distance))
384 .collect())
385 }
386 pub async fn obs_nearby(&self, obs: Observation) -> anyhow::Result<Vec<(Abstraction, Energy)>> {
387 let iso = i64::from(Isomorphism::from(obs));
388 let sql: &str = const_format::concatcp!(
389 "SELECT a.abs, ",
390 "m.dx ",
391 "FROM ",
392 ISOMORPHISM,
393 " e ",
394 "JOIN ",
395 ABSTRACTION,
396 " a ON a.street = get_street_abs(e.abs) ",
397 "JOIN ",
398 METRIC,
399 " m ON m.tri = get_pair_tri(a.abs, e.abs) ",
400 "WHERE e.obs = $1 ",
401 "AND a.abs != e.abs ",
402 "ORDER BY m.dx ASC ",
403 "LIMIT $2"
404 );
405 Ok(self
406 .0
407 .query(sql, &[&iso, &N_NEIGHBORS])
408 .await
409 .map_err(|e| anyhow::anyhow!("fetch nearby abstractions for observation: {}", e))?
410 .iter()
411 .map(|row| (row.get::<_, i16>(0), row.get::<_, Energy>(1)))
412 .map(|(abs, distance)| (Abstraction::from(abs), distance))
413 .collect())
414 }
415}
416
417impl API {
419 pub async fn obs_similar(&self, obs: Observation) -> anyhow::Result<Vec<Observation>> {
420 let iso = i64::from(Isomorphism::from(obs));
421 let sql: &str = const_format::concatcp!(
422 "WITH target AS ( ",
423 "SELECT abs, population ",
424 "FROM ",
425 ISOMORPHISM,
426 " e ",
427 "JOIN ",
428 ABSTRACTION,
429 " a ON a.abs = e.abs ",
430 "WHERE e.obs = $1 ",
431 ") ",
432 "SELECT e.obs ",
433 "FROM ",
434 ISOMORPHISM,
435 " e ",
436 "JOIN target t ON t.abs = e.abs ",
437 "WHERE e.obs != $1 ",
438 "AND e.position < LEAST($2, t.population) ",
439 "AND e.position >= FLOOR(RANDOM() * GREATEST(t.population - $2, 1)) ",
440 "LIMIT $2"
441 );
442 Ok(self
443 .0
444 .query(sql, &[&iso, &N_NEIGHBORS])
445 .await
446 .map_err(|e| anyhow::anyhow!("fetch similar observations: {}", e))?
447 .iter()
448 .map(|row| row.get::<_, i64>(0))
449 .map(Observation::from)
450 .collect())
451 }
452 pub async fn abs_similar(&self, abs: Abstraction) -> anyhow::Result<Vec<Observation>> {
453 let abs = i16::from(abs);
454 let sql: &str = const_format::concatcp!(
455 "WITH target AS ( ",
456 "SELECT population ",
457 "FROM ",
458 ABSTRACTION,
459 " ",
460 "WHERE abs = $1 ",
461 ") ",
462 "SELECT obs ",
463 "FROM ",
464 ISOMORPHISM,
465 " e, target t ",
466 "WHERE e.abs = $1 ",
467 "AND e.position < LEAST($2, t.population) ",
468 "AND e.position >= FLOOR(RANDOM() * GREATEST(t.population - $2, 1)) ",
469 "LIMIT $2"
470 );
471 Ok(self
472 .0
473 .query(sql, &[&abs, &N_NEIGHBORS])
474 .await
475 .map_err(|e| anyhow::anyhow!("fetch observations similar to abstraction: {}", e))?
476 .iter()
477 .map(|row| row.get::<_, i64>(0))
478 .map(Observation::from)
479 .collect())
480 }
481 pub async fn replace_obs(&self, obs: Observation) -> anyhow::Result<Observation> {
482 let sql: &str = const_format::concatcp!(
483 "WITH sample AS ( ",
484 "SELECT e.abs, ",
485 "a.population, ",
486 "FLOOR(RANDOM() * a.population)::INTEGER AS i ",
487 "FROM ",
488 ISOMORPHISM,
489 " e ",
490 "JOIN ",
491 ABSTRACTION,
492 " a ON a.abs = e.abs ",
493 "WHERE e.obs = $1 ",
494 ") ",
495 "SELECT e.obs ",
496 "FROM sample s ",
497 "JOIN ",
498 ISOMORPHISM,
499 " e ON e.abs = s.abs AND e.position = s.i ",
500 "LIMIT 1"
501 );
502 let iso = i64::from(Isomorphism::from(obs));
503 let row = self
504 .0
505 .query_one(sql, &[&iso])
506 .await
507 .map_err(|e| anyhow::anyhow!("replace observation: {}", e))?;
508 Ok(Observation::from(row.get::<_, i64>(0)))
509 }
510}
511
512impl API {
514 pub async fn nbr_any_wrt_abs(&self, wrt: Abstraction) -> anyhow::Result<ApiSample> {
515 use rand::prelude::IndexedRandom;
516 let ref mut rng = rand::rng();
517 let abs = Abstraction::all(wrt.street())
518 .into_iter()
519 .filter(|&x| x != wrt)
520 .collect::<Vec<_>>()
521 .choose(rng)
522 .copied()
523 .expect("more than one abstraction option");
524 self.nbr_abs_wrt_abs(wrt, abs).await
525 }
526 pub async fn nbr_abs_wrt_abs(
527 &self,
528 wrt: Abstraction,
529 abs: Abstraction,
530 ) -> anyhow::Result<ApiSample> {
531 let sql: &str = const_format::concatcp!(
532 "WITH sample AS ( ",
533 "SELECT r.abs, ",
534 "r.population, ",
535 "r.equity, ",
536 "FLOOR(RANDOM() * r.population)::INTEGER AS i, ",
537 "COALESCE(m.dx, 0) AS distance ",
538 "FROM ",
539 ABSTRACTION,
540 " r ",
541 "LEFT JOIN ",
542 METRIC,
543 " m ON m.tri = get_pair_tri($1, $3) ",
544 "WHERE r.abs = $1 ",
545 "), ",
546 "random_iso AS ( ",
547 "SELECT e.obs, ",
548 "e.abs, ",
549 "s.equity, ",
550 "s.population, ",
551 "s.distance ",
552 "FROM sample s ",
553 "JOIN ",
554 ISOMORPHISM,
555 " e ON e.abs = s.abs AND e.position = s.i ",
556 "LIMIT 1 ",
557 ") ",
558 "SELECT obs, ",
559 "abs, ",
560 "equity::REAL, ",
561 "population::REAL / $2 AS density, ",
562 "distance::REAL ",
563 "FROM random_iso"
564 );
565 let n = wrt.street().n_isomorphisms() as f32;
566 let abs = i16::from(abs);
567 let wrt = i16::from(wrt);
568 let row = self
569 .0
570 .query_one(sql, &[&abs, &n, &wrt])
571 .await
572 .map_err(|e| anyhow::anyhow!("fetch neighbor abstraction: {}", e))?;
573 Ok(api_sample_from_row(row))
574 }
575 pub async fn nbr_obs_wrt_abs(
576 &self,
577 wrt: Abstraction,
578 obs: Observation,
579 ) -> anyhow::Result<ApiSample> {
580 let sql: &str = const_format::concatcp!(
581 "WITH given AS ( ",
582 "SELECT obs, abs, get_pair_tri(abs, $3) AS tri ",
583 "FROM ",
584 ISOMORPHISM,
585 " ",
586 "WHERE obs = $1 ",
587 ") ",
588 "SELECT g.obs, ",
589 "g.abs, ",
590 "a.equity::REAL, ",
591 "a.population::REAL / $2 AS density, ",
592 "COALESCE(m.dx, 0)::REAL AS distance ",
593 "FROM given g ",
594 "JOIN ",
595 METRIC,
596 " m ON m.tri = g.tri ",
597 "JOIN ",
598 ABSTRACTION,
599 " a ON a.abs = g.abs ",
600 "LIMIT 1"
601 );
602 let n = wrt.street().n_isomorphisms() as f32;
603 let iso = i64::from(Isomorphism::from(obs));
604 let wrt = i16::from(wrt);
605 let row = self
606 .0
607 .query_one(sql, &[&iso, &n, &wrt])
608 .await
609 .map_err(|e| anyhow::anyhow!("fetch neighbor observation: {}", e))?;
610 Ok(api_sample_from_row(row))
611 }
612}
613
614impl API {
616 pub async fn kfn_wrt_abs(&self, wrt: Abstraction) -> anyhow::Result<Vec<ApiSample>> {
617 let sql: &str = const_format::concatcp!(
618 "WITH nearest AS ( ",
619 "SELECT a.abs, ",
620 "a.population, ",
621 "m.dx AS distance, ",
622 "FLOOR(RANDOM() * a.population)::INTEGER AS sample ",
623 "FROM ",
624 ABSTRACTION,
625 " a ",
626 "JOIN ",
627 METRIC,
628 " m ON m.tri = get_pair_tri(a.abs, $1) ",
629 "WHERE a.street = $2 ",
630 "AND a.abs != $1 ",
631 "ORDER BY m.dx DESC ",
632 "LIMIT $3 ",
633 ") ",
634 "SELECT e.obs, ",
635 "n.abs, ",
636 "a.equity::REAL, ",
637 "a.population::REAL / $4 AS density, ",
638 "n.distance::REAL ",
639 "FROM nearest n ",
640 "JOIN ",
641 ABSTRACTION,
642 " a ON a.abs = n.abs ",
643 "JOIN ",
644 ISOMORPHISM,
645 " e ON e.abs = n.abs AND e.position = n.sample ",
646 "ORDER BY n.distance DESC"
647 );
648 let n = wrt.street().n_isomorphisms() as f32;
649 let s = wrt.street() as i16;
650 let wrt = i16::from(wrt);
651 let rows = self
652 .0
653 .query(sql, &[&wrt, &s, &N_NEIGHBORS, &n])
654 .await
655 .map_err(|e| anyhow::anyhow!("fetch k-farthest neighbors: {}", e))?;
656 Ok(rows.into_iter().map(api_sample_from_row).collect())
657 }
658 pub async fn knn_wrt_abs(&self, wrt: Abstraction) -> anyhow::Result<Vec<ApiSample>> {
659 let sql: &str = const_format::concatcp!(
660 "WITH nearest AS ( ",
661 "SELECT a.abs, ",
662 "a.population, ",
663 "m.dx AS distance, ",
664 "FLOOR(RANDOM() * a.population)::INTEGER AS sample ",
665 "FROM ",
666 ABSTRACTION,
667 " a ",
668 "JOIN ",
669 METRIC,
670 " m ON m.tri = get_pair_tri(a.abs, $1) ",
671 "WHERE a.street = $2 ",
672 "AND a.abs != $1 ",
673 "ORDER BY m.dx ASC ",
674 "LIMIT $3 ",
675 ") ",
676 "SELECT e.obs, ",
677 "n.abs, ",
678 "a.equity::REAL, ",
679 "a.population::REAL / $4 AS density, ",
680 "n.distance::REAL ",
681 "FROM nearest n ",
682 "JOIN ",
683 ABSTRACTION,
684 " a ON a.abs = n.abs ",
685 "JOIN ",
686 ISOMORPHISM,
687 " e ON e.abs = n.abs AND e.position = n.sample ",
688 "ORDER BY n.distance ASC"
689 );
690 let n = wrt.street().n_isomorphisms() as f32;
691 let s = wrt.street() as i16;
692 let wrt = i16::from(wrt);
693 let rows = self
694 .0
695 .query(sql, &[&wrt, &s, &N_NEIGHBORS, &n])
696 .await
697 .map_err(|e| anyhow::anyhow!("fetch k-nearest neighbors: {}", e))?;
698 Ok(rows.into_iter().map(api_sample_from_row).collect())
699 }
700 pub async fn kgn_wrt_abs(
701 &self,
702 wrt: Abstraction,
703 nbr: Vec<Observation>,
704 ) -> anyhow::Result<Vec<ApiSample>> {
705 let sql: &str = const_format::concatcp!(
706 "WITH input(obs, ord) AS ( ",
707 "SELECT unnest($3::BIGINT[]), ",
708 "generate_series(1, array_length($3, 1)) ",
709 ") ",
710 "SELECT e.obs, ",
711 "e.abs, ",
712 "a.equity::REAL, ",
713 "a.population::REAL / $1 AS density, ",
714 "m.dx::REAL AS distance ",
715 "FROM input i ",
716 "JOIN ",
717 ISOMORPHISM,
718 " e ON e.obs = i.obs ",
719 "JOIN ",
720 ABSTRACTION,
721 " a ON a.abs = e.abs ",
722 "JOIN ",
723 METRIC,
724 " m ON m.tri = get_pair_tri(a.abs, $2) ",
725 "ORDER BY i.ord ",
726 "LIMIT $4"
727 );
728 let isos = nbr
729 .into_iter()
730 .map(Isomorphism::from)
731 .map(i64::from)
732 .collect::<Vec<_>>();
733 let n = wrt.street().n_isomorphisms() as f32;
734 let wrt = i16::from(wrt);
735 let rows = self
736 .0
737 .query(sql, &[&n, &wrt, &&isos, &N_NEIGHBORS])
738 .await
739 .map_err(|e| anyhow::anyhow!("fetch given neighbors: {}", e))?;
740 Ok(rows.into_iter().map(api_sample_from_row).collect())
741 }
742}
743
744impl API {
746 pub async fn hst_wrt_obs(&self, obs: Observation) -> anyhow::Result<Vec<ApiSample>> {
747 if obs.street() == Street::Rive {
748 self.hst_wrt_obs_on_river(obs).await
749 } else {
750 self.hst_wrt_obs_on_other(obs).await
751 }
752 }
753 pub async fn hst_wrt_abs(&self, abs: Abstraction) -> anyhow::Result<Vec<ApiSample>> {
754 if abs.street() == Street::Rive {
755 self.hst_wrt_abs_on_river(abs).await
756 } else {
757 self.hst_wrt_abs_on_other(abs).await
758 }
759 }
760 async fn hst_wrt_obs_on_river(&self, obs: Observation) -> anyhow::Result<Vec<ApiSample>> {
761 let sql: &str = const_format::concatcp!(
762 "WITH sample AS ( ",
763 "SELECT e.obs, ",
764 "e.abs, ",
765 "a.equity, ",
766 "a.population, ",
767 "FLOOR(RANDOM() * a.population)::INTEGER AS position ",
768 "FROM ",
769 ISOMORPHISM,
770 " e ",
771 "JOIN ",
772 ABSTRACTION,
773 " a ON a.abs = e.abs ",
774 "WHERE e.abs = (SELECT abs FROM ",
775 ISOMORPHISM,
776 " WHERE obs = $1) ",
777 "LIMIT 1 ",
778 ") ",
779 "SELECT s.obs, ",
780 "s.abs, ",
781 "s.equity::REAL, ",
782 "1::REAL AS density ",
783 "FROM sample s"
784 );
785 let iso = i64::from(Isomorphism::from(obs));
786 let rows = self
787 .0
788 .query(sql, &[&iso])
789 .await
790 .map_err(|e| anyhow::anyhow!("fetch river observation distribution: {}", e))?;
791 Ok(rows.into_iter().map(api_sample_from_row).collect())
792 }
793 async fn hst_wrt_obs_on_other(&self, obs: Observation) -> anyhow::Result<Vec<ApiSample>> {
794 let sql: &str = const_format::concatcp!(
796 "SELECT e.obs, ",
797 "e.abs, ",
798 "a.equity ",
799 "FROM ",
800 ISOMORPHISM,
801 " e ",
802 "JOIN ",
803 ABSTRACTION,
804 " a ON a.abs = e.abs ",
805 "WHERE e.obs = $1"
806 );
807 let iso = i64::from(Isomorphism::from(obs));
808 let rows = self
809 .0
810 .query(sql, &[&iso])
811 .await
812 .map_err(|e| anyhow::anyhow!("fetch observation distribution: {}", e))?;
813 Ok(rows.into_iter().map(api_sample_from_row).collect())
814 }
815 async fn hst_wrt_abs_on_river(&self, abs: Abstraction) -> anyhow::Result<Vec<ApiSample>> {
816 let sql: &str = const_format::concatcp!(
817 "WITH sample AS ( ",
818 "SELECT a.abs, ",
819 "a.population, ",
820 "a.equity, ",
821 "FLOOR(RANDOM() * a.population)::INTEGER AS position ",
822 "FROM ",
823 ABSTRACTION,
824 " a ",
825 "WHERE a.abs = $1 ",
826 "LIMIT 1 ",
827 ") ",
828 "SELECT e.obs, ",
829 "e.abs, ",
830 "s.equity::REAL, ",
831 "1::REAL AS density ",
832 "FROM sample s ",
833 "JOIN ",
834 ISOMORPHISM,
835 " e ON e.abs = s.abs AND e.position = s.position"
836 );
837 let ref abs = i16::from(abs);
838 let rows = self
839 .0
840 .query(sql, &[abs])
841 .await
842 .map_err(|e| anyhow::anyhow!("fetch river abstraction distribution: {}", e))?;
843 Ok(rows.into_iter().map(api_sample_from_row).collect())
844 }
845 async fn hst_wrt_abs_on_other(&self, abs: Abstraction) -> anyhow::Result<Vec<ApiSample>> {
846 let sql: &str = const_format::concatcp!(
847 "WITH histogram AS ( ",
848 "SELECT p.abs, ",
849 "g.dx AS probability, ",
850 "p.population, ",
851 "p.equity, ",
852 "FLOOR(RANDOM() * p.population)::INTEGER AS i ",
853 "FROM ",
854 TRANSITIONS,
855 " g ",
856 "JOIN ",
857 ABSTRACTION,
858 " p ON p.abs = g.next ",
859 "WHERE g.prev = $1 ",
860 ") ",
861 "SELECT e.obs, ",
862 "t.abs, ",
863 "t.equity::REAL, ",
864 "t.probability AS density ",
865 "FROM histogram t ",
866 "JOIN ",
867 ISOMORPHISM,
868 " e ON e.abs = t.abs AND e.position = t.i ",
869 "ORDER BY t.probability DESC"
870 );
871 let ref abs = i16::from(abs);
872 let rows = self
873 .0
874 .query(sql, &[abs])
875 .await
876 .map_err(|e| anyhow::anyhow!("fetch abstraction distribution: {}", e))?;
877 Ok(rows.into_iter().map(api_sample_from_row).collect())
878 }
879}
880
881impl API {
883 pub async fn policy(&self, recall: Partial) -> anyhow::Result<Option<ApiStrategy>> {
884 let sql: &str = const_format::concatcp!(
885 "SELECT edge, ",
886 "weight, ",
887 "counts ",
888 "FROM ",
889 BLUEPRINT,
890 " ",
891 "WHERE past = $1 ",
892 "AND present = $2 ",
893 "AND choices = $3"
894 );
895 let recall = recall.validate()?;
896 let present = self.obs_to_abs(recall.seen()).await?;
897 let info = NlheInfo::from((&recall, present));
898 let ref history = i64::from(info.subgame());
899 let ref present = i16::from(info.bucket());
900 let ref choices = i64::from(info.choices());
901 let rows = self
902 .0
903 .query(sql, &[history, present, choices])
904 .await
905 .map_err(|e| anyhow::anyhow!("fetch policy: {}", e))?;
906 match rows.len() {
907 0 => Ok(None),
908 _ => Ok(Some(api_strategy_from(Strategy::from((
909 info,
910 rows.into_iter().map(decision_from_row).collect::<Vec<_>>(),
911 ))))),
912 }
913 }
914}