diff --git a/abstutil/src/serde.rs b/abstutil/src/serde.rs index 6997878bd3..59f4f2f8df 100644 --- a/abstutil/src/serde.rs +++ b/abstutil/src/serde.rs @@ -2,32 +2,37 @@ use crate::MultiMap; use serde::de::DeserializeOwned; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cmp::Ord; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::convert::TryFrom; use std::error::Error; +/// Stringifies an object to nicely formatted JSON. pub fn to_json(obj: &T) -> String { serde_json::to_string_pretty(obj).unwrap() } +/// Stringifies an object to terse JSON. pub fn to_json_terse(obj: &T) -> String { serde_json::to_string(obj).unwrap() } +/// Deserializes an object from a JSON string. pub fn from_json(raw: &Vec) -> Result> { serde_json::from_slice(raw).map_err(|x| x.into()) } +/// Deserializes an object from the bincode format. pub fn from_binary(raw: &Vec) -> Result> { bincode::deserialize(raw).map_err(|x| x.into()) } +/// The number of bytes for an object serialized to bincode. pub fn serialized_size_bytes(obj: &T) -> usize { bincode::serialized_size(obj).unwrap() as usize } -// For BTreeMaps with struct keys. See https://github.com/serde-rs/json/issues/402. - +/// Serializes a BTreeMap as a list of tuples. Necessary when the keys are structs; see +/// https://github.com/serde-rs/json/issues/402. pub fn serialize_btreemap( map: &BTreeMap, s: S, @@ -35,6 +40,8 @@ pub fn serialize_btreemap( map.iter().collect::>().serialize(s) } +/// Deserializes a BTreeMap from a list of tuples. Necessary when the keys are structs; see +/// https://github.com/serde-rs/json/issues/402. pub fn deserialize_btreemap< 'de, D: Deserializer<'de>, @@ -51,6 +58,35 @@ pub fn deserialize_btreemap< Ok(map) } +/// Serializes a HashMap as a list of tuples, first sorting by the keys. This ensures the +/// serialized form is deterministic. +pub fn serialize_hashmap( + map: &HashMap, + s: S, +) -> Result { + let mut list: Vec<(&K, &V)> = map.iter().collect(); + list.sort_by_key(|(k, _)| k.clone()); + list.serialize(s) +} + +/// Deserializes a HashMap from a list of tuples. +pub fn deserialize_hashmap< + 'de, + D: Deserializer<'de>, + K: Deserialize<'de> + std::hash::Hash + Eq, + V: Deserialize<'de>, +>( + d: D, +) -> Result, D::Error> { + let vec = >::deserialize(d)?; + let mut map = HashMap::new(); + for (k, v) in vec { + map.insert(k, v); + } + Ok(map) +} + +/// Serializes a MultiMap. pub fn serialize_multimap< S: Serializer, K: Serialize + Eq + Ord + Clone, @@ -63,6 +99,7 @@ pub fn serialize_multimap< map.raw_map().iter().collect::>().serialize(s) } +/// Deserializes a MultiMap. pub fn deserialize_multimap< 'de, D: Deserializer<'de>, @@ -81,6 +118,8 @@ pub fn deserialize_multimap< Ok(map) } +/// Serializes a `usize` as a `u32` to save space. Useful when you need `usize` for indexing, but +/// the values don't exceed 2^32. pub fn serialize_usize(x: &usize, s: S) -> Result { if let Ok(x) = u32::try_from(*x) { x.serialize(s) @@ -89,6 +128,7 @@ pub fn serialize_usize(x: &usize, s: S) -> Result>(d: D) -> Result { let x = ::deserialize(d)?; Ok(x as usize) diff --git a/sim/src/mechanics/driving.rs b/sim/src/mechanics/driving.rs index 1255099edb..e9f1ca8181 100644 --- a/sim/src/mechanics/driving.rs +++ b/sim/src/mechanics/driving.rs @@ -1,8 +1,8 @@ -use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}; use serde::{Deserialize, Serialize}; -use abstutil::{deserialize_btreemap, serialize_btreemap, FixedMap, IndexableKey}; +use abstutil::{deserialize_hashmap, serialize_hashmap, FixedMap, IndexableKey}; use geom::{Distance, Duration, PolyLine, Speed, Time}; use map_model::{LaneID, Map, Path, PathStep, Traversable}; @@ -30,11 +30,14 @@ pub struct DrivingSimState { // gain much faster lookup, which has shown dramatic speedups in the scenarios being run so // far. cars: FixedMap, + // Note this uses a HashMap for faster lookup. Although the order of iterating over the HashMap + // is random, determinism in the simulation is preserved, because nothing iterates over + // everything. #[serde( - serialize_with = "serialize_btreemap", - deserialize_with = "deserialize_btreemap" + serialize_with = "serialize_hashmap", + deserialize_with = "deserialize_hashmap" )] - queues: BTreeMap, + queues: HashMap, events: Vec, recalc_lanechanging: bool, @@ -50,7 +53,7 @@ impl DrivingSimState { pub fn new(map: &Map, opts: &SimOptions) -> DrivingSimState { let mut sim = DrivingSimState { cars: FixedMap::new(), - queues: BTreeMap::new(), + queues: HashMap::new(), events: Vec::new(), recalc_lanechanging: opts.recalc_lanechanging, handle_uber_turns: opts.handle_uber_turns, @@ -217,7 +220,7 @@ impl DrivingSimState { if !need_distances { // We need to mutate two different cars in one case. To avoid fighting the borrow - // checker, temporarily move one of them out of the BTreeMap. + // checker, temporarily move one of them out of the map. let mut car = self.cars.remove(&id).unwrap(); // Responsibility of update_car to manage scheduling stuff! need_distances = self.update_car_without_distances(&mut car, now, ctx, transit); @@ -234,7 +237,7 @@ impl DrivingSimState { let idx = dists.iter().position(|(c, _)| *c == id).unwrap(); // We need to mutate two different cars in some cases. To avoid fighting the borrow - // checker, temporarily move one of them out of the BTreeMap. + // checker, temporarily move one of them out of the map. let mut car = self.cars.remove(&id).unwrap(); // Responsibility of update_car_with_distances to manage scheduling stuff! if self @@ -835,6 +838,7 @@ impl DrivingSimState { } } + /// Note the ordering of results is non-deterministic! pub fn get_unzoomed_agents(&self, now: Time, map: &Map) -> Vec { let mut result = Vec::new(); @@ -867,6 +871,7 @@ impl DrivingSimState { self.cars.contains_key(&id) } + /// Note the ordering of results is non-deterministic! pub fn get_all_draw_cars( &self, now: Time, diff --git a/sim/src/mechanics/intersection.rs b/sim/src/mechanics/intersection.rs index 25cdfd3977..e44ac41bbe 100644 --- a/sim/src/mechanics/intersection.rs +++ b/sim/src/mechanics/intersection.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, BTreeSet, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use serde::{Deserialize, Serialize}; @@ -289,7 +289,7 @@ impl IntersectionSimState { maybe_cars_and_queues: Option<( &Car, &FixedMap, - &mut BTreeMap, + &mut HashMap, )>, ) -> bool { let req = Request { agent, turn }; @@ -660,7 +660,7 @@ impl IntersectionSimState { &mut self, req: &Request, map: &Map, - maybe_cars_and_queues: Option<(&FixedMap, &BTreeMap)>, + maybe_cars_and_queues: Option<(&FixedMap, &HashMap)>, ) -> bool { let turn = map.get_t(req.turn); let mut cycle_detected = false; @@ -713,7 +713,7 @@ impl IntersectionSimState { fn detect_conflict_cycle( &self, car: CarID, - pair: (&FixedMap, &BTreeMap), + pair: (&FixedMap, &HashMap), ) -> Option> { let (cars, queues) = pair; diff --git a/sim/src/mechanics/queue.rs b/sim/src/mechanics/queue.rs index a65e3fa177..eef1fb5cdf 100644 --- a/sim/src/mechanics/queue.rs +++ b/sim/src/mechanics/queue.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, BTreeSet, VecDeque}; +use std::collections::{BTreeSet, HashMap, VecDeque}; use serde::{Deserialize, Serialize}; @@ -43,7 +43,7 @@ impl Queue { &self, now: Time, cars: &FixedMap, - queues: &BTreeMap, + queues: &HashMap, ) -> Option<(CarID, Distance)> { self.inner_get_last_car_position(now, cars, queues, &mut BTreeSet::new(), None) } @@ -53,7 +53,7 @@ impl Queue { &self, now: Time, cars: &FixedMap, - queues: &BTreeMap, + queues: &HashMap, ) -> Vec<(CarID, Distance)> { let mut all_cars = vec![]; self.inner_get_last_car_position( @@ -70,7 +70,7 @@ impl Queue { &self, now: Time, cars: &FixedMap, - queues: &BTreeMap, + queues: &HashMap, recursed_queues: &mut BTreeSet, mut intermediate_results: Option<&mut Vec<(CarID, Distance)>>, ) -> Option<(CarID, Distance)> { @@ -204,7 +204,7 @@ impl Queue { vehicle_len: Distance, now: Time, cars: &FixedMap, - queues: &BTreeMap, + queues: &HashMap, ) -> Option { if self.laggy_head.is_none() && self.cars.is_empty() { return Some(0); diff --git a/sim/src/router.rs b/sim/src/router.rs index 5752ee2cb4..37be6f18a5 100644 --- a/sim/src/router.rs +++ b/sim/src/router.rs @@ -1,7 +1,7 @@ //! For vehicles only, not pedestrians. Follows a Path from map_model, but can opportunistically //! lane-change to avoid a slow lane, can can handle re-planning to look for available parking. -use std::collections::BTreeMap; +use std::collections::HashMap; use serde::{Deserialize, Serialize}; @@ -334,7 +334,7 @@ impl Router { pub fn opportunistically_lanechange( &mut self, - queues: &BTreeMap, + queues: &HashMap, map: &Map, handle_uber_turns: bool, ) {