From 6e9f01bd11ccef3b631f52595f32edc04a561fb2 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 2 Feb 2024 13:06:22 -0700 Subject: [PATCH 01/17] stub - turn delays module --- .../src/model/road_network/mod.rs | 2 -- .../model/traversal/access/access_model.rs | 1 + .../traversal/access/access_model_error.rs | 5 ++++ .../src/model/traversal/access/default/mod.rs | 1 + .../default/turn_delays}/edge_heading.rs | 0 .../access/default/turn_delays/mod.rs | 4 +++ .../access/default/turn_delays}/turn.rs | 14 ++++++----- .../turn_delays/turn_delay_access_model.rs | 25 +++++++++++++++++++ .../default/turn_delays/turn_delay_model.rs | 8 ++++++ .../src/model/traversal/access/mod.rs | 3 +++ .../src/model/traversal/mod.rs | 1 + .../model/traversal/traversal_model_error.rs | 6 +++-- .../src/routee/energy_model_ops.rs | 5 ++-- .../src/routee/energy_model_service.rs | 2 +- .../src/routee/energy_traversal_model.rs | 2 +- 15 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 rust/routee-compass-core/src/model/traversal/access/access_model.rs create mode 100644 rust/routee-compass-core/src/model/traversal/access/access_model_error.rs create mode 100644 rust/routee-compass-core/src/model/traversal/access/default/mod.rs rename rust/routee-compass-core/src/model/{road_network => traversal/access/default/turn_delays}/edge_heading.rs (100%) create mode 100644 rust/routee-compass-core/src/model/traversal/access/default/turn_delays/mod.rs rename rust/routee-compass-core/src/model/{road_network => traversal/access/default/turn_delays}/turn.rs (58%) create mode 100644 rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs create mode 100644 rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs create mode 100644 rust/routee-compass-core/src/model/traversal/access/mod.rs diff --git a/rust/routee-compass-core/src/model/road_network/mod.rs b/rust/routee-compass-core/src/model/road_network/mod.rs index e8651dc4..2e41655a 100644 --- a/rust/routee-compass-core/src/model/road_network/mod.rs +++ b/rust/routee-compass-core/src/model/road_network/mod.rs @@ -1,9 +1,7 @@ -pub mod edge_heading; pub mod edge_id; pub mod edge_loader; pub mod graph; pub mod graph_error; pub mod graph_loader; -pub mod turn; pub mod vertex_id; pub mod vertex_loader; diff --git a/rust/routee-compass-core/src/model/traversal/access/access_model.rs b/rust/routee-compass-core/src/model/traversal/access/access_model.rs new file mode 100644 index 00000000..d42b2240 --- /dev/null +++ b/rust/routee-compass-core/src/model/traversal/access/access_model.rs @@ -0,0 +1 @@ +pub trait AccessModel {} diff --git a/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs b/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs new file mode 100644 index 00000000..375557da --- /dev/null +++ b/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs @@ -0,0 +1,5 @@ +#[derive(thiserror::Error, Debug)] +pub enum AccessModelError { + #[error("error while executing access model {name}: {error}")] + RuntimeError { name: String, error: String }, +} diff --git a/rust/routee-compass-core/src/model/traversal/access/default/mod.rs b/rust/routee-compass-core/src/model/traversal/access/default/mod.rs new file mode 100644 index 00000000..fd81b89b --- /dev/null +++ b/rust/routee-compass-core/src/model/traversal/access/default/mod.rs @@ -0,0 +1 @@ +pub mod turn_delays; diff --git a/rust/routee-compass-core/src/model/road_network/edge_heading.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/edge_heading.rs similarity index 100% rename from rust/routee-compass-core/src/model/road_network/edge_heading.rs rename to rust/routee-compass-core/src/model/traversal/access/default/turn_delays/edge_heading.rs diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/mod.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/mod.rs new file mode 100644 index 00000000..10cb003e --- /dev/null +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/mod.rs @@ -0,0 +1,4 @@ +pub mod edge_heading; +pub mod turn; +pub mod turn_delay_access_model; +pub mod turn_delay_model; diff --git a/rust/routee-compass-core/src/model/road_network/turn.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs similarity index 58% rename from rust/routee-compass-core/src/model/road_network/turn.rs rename to rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs index dded6015..184787a6 100644 --- a/rust/routee-compass-core/src/model/road_network/turn.rs +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs @@ -1,5 +1,7 @@ -use super::graph_error::GraphError; +use crate::model::traversal::access::access_model_error::AccessModelError; +use serde::{Deserialize, Serialize}; +#[derive(Serialize, Deserialize)] pub enum Turn { NoTurn, SlightRight, @@ -12,7 +14,7 @@ pub enum Turn { } impl Turn { - pub fn from_angle(angle: i16) -> Result { + pub fn from_angle(angle: i16) -> Result { match angle { -180..=-160 => Ok(Turn::UTurn), -159..=-135 => Ok(Turn::SharpLeft), @@ -23,10 +25,10 @@ impl Turn { 45..=134 => Ok(Turn::Right), 135..=159 => Ok(Turn::SharpRight), 160..=180 => Ok(Turn::UTurn), - _ => Err(GraphError::AttributeError( - "Turn".to_string(), - format!("Angle {} out of range of -180 to 180", angle), - )), + _ => Err(AccessModelError::RuntimeError { + name: String::from("turn delays"), + error: format!("Angle {0} out of range of -180 to 180", angle), + }), } } } diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs new file mode 100644 index 00000000..88864e63 --- /dev/null +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs @@ -0,0 +1,25 @@ +use super::edge_heading::EdgeHeading; +use crate::model::road_network::edge_id::EdgeId; +use crate::model::traversal::traversal_model_error::TraversalModelError; + +pub struct TurnDelayAccessModel { + edge_headings: Box<[EdgeHeading]>, + time_fieldname: String, +} + +impl TurnDelayAccessModel {} + +/// lookup up the edge heading from the headings table +pub fn get_headings( + headings_table: &[EdgeHeading], + edge_id: EdgeId, +) -> Result { + let heading: &EdgeHeading = headings_table.get(edge_id.as_usize()).ok_or_else(|| { + TraversalModelError::MissingIdInTabularCostFunction( + format!("{}", edge_id), + String::from("EdgeId"), + String::from("headings table"), + ) + })?; + Ok(*heading) +} diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs new file mode 100644 index 00000000..74a9db98 --- /dev/null +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs @@ -0,0 +1,8 @@ +use super::turn::Turn; +use crate::model::unit::Time; +use std::collections::HashMap; + +pub enum TurnDelayModel { + /// use a mapping heuristic from turn ranges to time delays + TabularDiscrete { table: HashMap }, +} diff --git a/rust/routee-compass-core/src/model/traversal/access/mod.rs b/rust/routee-compass-core/src/model/traversal/access/mod.rs new file mode 100644 index 00000000..100a3d17 --- /dev/null +++ b/rust/routee-compass-core/src/model/traversal/access/mod.rs @@ -0,0 +1,3 @@ +pub mod access_model; +pub mod access_model_error; +pub mod default; diff --git a/rust/routee-compass-core/src/model/traversal/mod.rs b/rust/routee-compass-core/src/model/traversal/mod.rs index b51eabcf..94600155 100644 --- a/rust/routee-compass-core/src/model/traversal/mod.rs +++ b/rust/routee-compass-core/src/model/traversal/mod.rs @@ -1,3 +1,4 @@ +pub mod access; pub mod access_result; pub mod default; pub mod state; diff --git a/rust/routee-compass-core/src/model/traversal/traversal_model_error.rs b/rust/routee-compass-core/src/model/traversal/traversal_model_error.rs index 9b24724f..857923da 100644 --- a/rust/routee-compass-core/src/model/traversal/traversal_model_error.rs +++ b/rust/routee-compass-core/src/model/traversal/traversal_model_error.rs @@ -1,9 +1,9 @@ -use std::path::PathBuf; - +use super::access::access_model_error::AccessModelError; use super::state::traversal_state::TraversalState; use crate::model::road_network::graph_error::GraphError; use crate::model::unit::UnitError; use crate::util::cache_policy::cache_error::CacheError; +use std::path::PathBuf; #[derive(thiserror::Error, Debug)] pub enum TraversalModelError { @@ -25,6 +25,8 @@ pub enum TraversalModelError { CacheError(#[from] CacheError), #[error(transparent)] GraphError(#[from] GraphError), + #[error(transparent)] + AccessModelError(#[from] AccessModelError), #[error("prediction model failed with error {0}")] PredictionModel(String), } diff --git a/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs b/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs index 3cfd1f1d..2fe80a39 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs @@ -1,6 +1,7 @@ +use routee_compass_core::model::traversal::access::default::turn_delays::edge_heading::EdgeHeading; use routee_compass_core::model::{ - road_network::edge_heading::EdgeHeading, road_network::edge_id::EdgeId, - traversal::traversal_model_error::TraversalModelError, unit::Grade, + road_network::edge_id::EdgeId, traversal::traversal_model_error::TraversalModelError, + unit::Grade, }; pub const ZERO_ENERGY: f64 = 1e-9; diff --git a/rust/routee-compass-powertrain/src/routee/energy_model_service.rs b/rust/routee-compass-powertrain/src/routee/energy_model_service.rs index 6b4c2600..64334e16 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_model_service.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_model_service.rs @@ -1,6 +1,6 @@ use super::energy_traversal_model::EnergyTraversalModel; use super::vehicle::VehicleType; -use routee_compass_core::model::road_network::edge_heading::EdgeHeading; +use routee_compass_core::model::traversal::access::default::turn_delays::edge_heading::EdgeHeading; use routee_compass_core::model::traversal::default::speed_traversal_model::get_max_speed; use routee_compass_core::model::traversal::traversal_model::TraversalModel; use routee_compass_core::model::traversal::traversal_model_error::TraversalModelError; diff --git a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs index b0b0a1aa..e5e14ea9 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs @@ -3,7 +3,7 @@ use super::energy_model_service::EnergyModelService; use super::vehicle::vehicle_type::{VehicleState, VehicleType}; use routee_compass_core::model::property::edge::Edge; use routee_compass_core::model::property::vertex::Vertex; -use routee_compass_core::model::road_network::turn::Turn; +use routee_compass_core::model::traversal::access::default::turn_delays::turn::Turn; use routee_compass_core::model::traversal::default::speed_traversal_model::get_speed; use routee_compass_core::model::traversal::state::state_variable::StateVar; use routee_compass_core::model::traversal::state::traversal_state::TraversalState; From 082e2b92d40f6dd39da0faffb67c772b79a725fc Mon Sep 17 00:00:00 2001 From: rfitzger Date: Tue, 6 Feb 2024 11:29:12 -0700 Subject: [PATCH 02/17] prototyping access model --- .../model/traversal/access/access_model.rs | 39 ++++++++++++++++++- .../traversal/access/access_model_error.rs | 2 + .../access/default/turn_delays/turn.rs | 9 ++++- .../turn_delays/turn_delay_access_model.rs | 38 +++++++++++++++++- .../default/turn_delays/turn_delay_model.rs | 31 ++++++++++++++- 5 files changed, 114 insertions(+), 5 deletions(-) diff --git a/rust/routee-compass-core/src/model/traversal/access/access_model.rs b/rust/routee-compass-core/src/model/traversal/access/access_model.rs index d42b2240..347f1642 100644 --- a/rust/routee-compass-core/src/model/traversal/access/access_model.rs +++ b/rust/routee-compass-core/src/model/traversal/access/access_model.rs @@ -1 +1,38 @@ -pub trait AccessModel {} +use crate::model::{ + property::{edge::Edge, vertex::Vertex}, + traversal::{ + state::traversal_state::TraversalState, traversal_model_error::TraversalModelError, + }, +}; + +pub trait AccessModel { + /// Updates the traversal state by accessing some destination edge + /// when coming from some previous edge. + /// + /// These arguments appear in the network as: + /// `(v1) -[prev]-> (v2) -[next]-> (v3)` + /// Where `next` is the edge we want to access. + /// + /// # Arguments + /// + /// * `v1` - src of previous edge + /// * `src` - previous edge + /// * `v2` - src vertex of the next edge + /// * `dst` - edge we are determining the cost to access + /// * `v3` - dst vertex of the next edge + /// * `state` - state of the search at the beginning of the dst edge + /// + /// # Returns + /// + /// Either an optional access result or an error. if there are no + /// state updates due to access, None is returned. + fn access_edge( + &self, + v1: &Vertex, + src: &Edge, + v2: &Vertex, + dst: &Edge, + v3: &Vertex, + state: &TraversalState, + ) -> Result, TraversalModelError>; +} diff --git a/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs b/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs index 375557da..7de4122e 100644 --- a/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs +++ b/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs @@ -2,4 +2,6 @@ pub enum AccessModelError { #[error("error while executing access model {name}: {error}")] RuntimeError { name: String, error: String }, + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), } diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs index 184787a6..294deb64 100644 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs @@ -1,7 +1,8 @@ use crate::model::traversal::access::access_model_error::AccessModelError; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(rename = "snake_case")] pub enum Turn { NoTurn, SlightRight, @@ -13,6 +14,12 @@ pub enum Turn { UTurn, } +impl ToString for Turn { + fn to_string(&self) -> String { + serde_json::to_string(self).unwrap_or_else(|_| String::from("")) + } +} + impl Turn { pub fn from_angle(angle: i16) -> Result { match angle { diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs index 88864e63..01aeb18d 100644 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs @@ -1,14 +1,50 @@ use super::edge_heading::EdgeHeading; +use super::turn_delay_model::TurnDelayModel; use crate::model::road_network::edge_id::EdgeId; -use crate::model::traversal::traversal_model_error::TraversalModelError; +use crate::model::traversal::access::access_model::AccessModel; +use crate::model::{ + property::{edge::Edge, vertex::Vertex}, + traversal::{ + state::traversal_state::TraversalState, traversal_model_error::TraversalModelError, + }, +}; pub struct TurnDelayAccessModel { edge_headings: Box<[EdgeHeading]>, + turn_delay_model: TurnDelayModel, time_fieldname: String, } impl TurnDelayAccessModel {} +impl AccessModel for TurnDelayAccessModel { + fn access_edge( + &self, + _v1: &Vertex, + src: &Edge, + _v2: &Vertex, + dst: &Edge, + _v3: &Vertex, + state: &TraversalState, + ) -> Result, TraversalModelError> { + let src_heading = get_headings(&self.edge_headings, src.edge_id)?; + let dst_heading = get_headings(&self.edge_headings, dst.edge_id)?; + let angle = src_heading.next_edge_angle(&dst_heading); + + // TODO: + // - an AccessModel should be able to update the state + // - turn delays (time) + // - charging stations (energy) + // - perhaps there should be a broker that is upstream of the access and traversal + // models that manages state allocation/cloning, inspection and updates + + // let delay = self.turn_delay_model.get_delay(angle, target_time_unit)?; + // let updated_state = add_time_to_state(state, time); + // Ok(Some(updated_state)) + todo!() + } +} + /// lookup up the edge heading from the headings table pub fn get_headings( headings_table: &[EdgeHeading], diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs index 74a9db98..f219ef88 100644 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs @@ -1,8 +1,35 @@ use super::turn::Turn; -use crate::model::unit::Time; +use crate::model::{ + traversal::access::access_model_error::AccessModelError, + unit::{Time, TimeUnit}, +}; use std::collections::HashMap; pub enum TurnDelayModel { /// use a mapping heuristic from turn ranges to time delays - TabularDiscrete { table: HashMap }, + TabularDiscrete { + table: HashMap, + time_unit: TimeUnit, + }, +} + +impl TurnDelayModel { + pub fn get_delay( + &self, + angle: i16, + target_time_unit: &TimeUnit, + ) -> Result { + match self { + TurnDelayModel::TabularDiscrete { table, time_unit } => { + let turn = Turn::from_angle(angle)?; + let delay = table.get(&turn).ok_or_else(|| { + let name = String::from("tabular discrete turn delay model"); + let error = format!("table missing entry for turn {}", turn.to_string()); + AccessModelError::RuntimeError { name, error } + })?; + let result = time_unit.convert(*delay, target_time_unit); + Ok(result) + } + } + } } From 7efa14d1a47a3d4880eb3bfd8ce89787f0fd8096 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Tue, 5 Mar 2024 10:21:05 -0700 Subject: [PATCH 03/17] naming/comments --- .../default/turn_delays/edge_heading.rs | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/edge_heading.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/edge_heading.rs index 31c01cde..ce97a611 100644 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/edge_heading.rs +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/edge_heading.rs @@ -1,39 +1,38 @@ use serde::Deserialize; +/// simplifies the representation of directionality for a linestring +/// to just the headings of the start and end points, using cardinal angles [0, 360). +/// if the start and end have the same heading, the edge heading is None. #[derive(Copy, Clone, Deserialize)] pub struct EdgeHeading { - start_heading: i16, - end_heading: Option, + start_angle: i16, + end_angle: Option, } impl EdgeHeading { - pub fn with_start_and_end(start_heading: i16, end_heading: i16) -> Self { + /// creates an EdgeHeading from a start and end heading + pub fn new(start_angle: i16, end_angle: i16) -> Self { Self { - start_heading, - end_heading: Some(end_heading), + start_angle, + end_angle: Some(end_angle), } } - pub fn with_start(start_heading: i16) -> Self { - Self { - start_heading, - end_heading: None, - } + /// retrieve the start + pub fn start_angle(&self) -> i16 { + self.start_angle } - pub fn start_heading(&self) -> i16 { - self.start_heading - } /// If the end heading is not specified, it is assumed to be the same as the start heading - pub fn end_heading(&self) -> i16 { - match self.end_heading { + pub fn end_angle(&self) -> i16 { + match self.end_angle { Some(end_heading) => end_heading, - None => self.start_heading, + None => self.start_angle, } } - /// Compute the angle between this edge and the next edge - pub fn next_edge_angle(&self, next_edge_heading: &EdgeHeading) -> i16 { - let angle = next_edge_heading.start_heading() - self.end_heading(); + /// Compute the angle between this edge and some destination edge. + pub fn bearing_to_destination(&self, destination: &EdgeHeading) -> i16 { + let angle = destination.start_angle() - self.end_angle(); if angle > 180 { angle - 360 } else if angle < -180 { @@ -49,20 +48,20 @@ mod test { use super::*; #[test] - fn test_next_edge_angle() { - let edge_heading = EdgeHeading::with_start_and_end(45, 90); - let next_edge_heading = EdgeHeading::with_start_and_end(90, 135); - assert_eq!(edge_heading.next_edge_angle(&next_edge_heading), 0); + fn test_simple() { + let edge_heading = EdgeHeading::new(45, 90); + let next_edge_heading = EdgeHeading::new(90, 135); + assert_eq!(edge_heading.bearing_to_destination(&next_edge_heading), 0); } #[test] - fn test_next_edge_angle_wrap() { - let edge_heading = EdgeHeading::with_start_and_end(10, 10); - let next_edge_heading = EdgeHeading::with_start_and_end(350, 350); - assert_eq!(edge_heading.next_edge_angle(&next_edge_heading), -20); + fn test_wrap_360() { + let edge_heading = EdgeHeading::new(10, 10); + let next_edge_heading = EdgeHeading::new(350, 350); + assert_eq!(edge_heading.bearing_to_destination(&next_edge_heading), -20); - let edge_heading = EdgeHeading::with_start_and_end(350, 350); - let next_edge_heading = EdgeHeading::with_start_and_end(10, 10); - assert_eq!(edge_heading.next_edge_angle(&next_edge_heading), 20); + let edge_heading = EdgeHeading::new(350, 350); + let next_edge_heading = EdgeHeading::new(10, 10); + assert_eq!(edge_heading.bearing_to_destination(&next_edge_heading), 20); } } From 3241558e257a06f81ff7828096b06b31c05d164b Mon Sep 17 00:00:00 2001 From: rfitzger Date: Tue, 5 Mar 2024 10:21:26 -0700 Subject: [PATCH 04/17] updated with state indices --- .../model/traversal/access/access_model.rs | 17 +++---- .../turn_delays/turn_delay_access_model.rs | 45 ++++++++++++------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/rust/routee-compass-core/src/model/traversal/access/access_model.rs b/rust/routee-compass-core/src/model/traversal/access/access_model.rs index 347f1642..5e48ace7 100644 --- a/rust/routee-compass-core/src/model/traversal/access/access_model.rs +++ b/rust/routee-compass-core/src/model/traversal/access/access_model.rs @@ -9,18 +9,16 @@ pub trait AccessModel { /// Updates the traversal state by accessing some destination edge /// when coming from some previous edge. /// - /// These arguments appear in the network as: + /// The traversal argument represents a set of vertices and + /// edges connected in the network: /// `(v1) -[prev]-> (v2) -[next]-> (v3)` /// Where `next` is the edge we want to access. /// /// # Arguments /// - /// * `v1` - src of previous edge - /// * `src` - previous edge - /// * `v2` - src vertex of the next edge - /// * `dst` - edge we are determining the cost to access - /// * `v3` - dst vertex of the next edge + /// * `traversal` - the vertex/edge traversal /// * `state` - state of the search at the beginning of the dst edge + /// * `state_variable_indices` - the names and indices of state variables /// /// # Returns /// @@ -28,11 +26,8 @@ pub trait AccessModel { /// state updates due to access, None is returned. fn access_edge( &self, - v1: &Vertex, - src: &Edge, - v2: &Vertex, - dst: &Edge, - v3: &Vertex, + traversal: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), state: &TraversalState, + state_variable_indices: Vec<(String, usize)>, ) -> Result, TraversalModelError>; } diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs index 01aeb18d..dcd6f341 100644 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs +++ b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs @@ -12,7 +12,6 @@ use crate::model::{ pub struct TurnDelayAccessModel { edge_headings: Box<[EdgeHeading]>, turn_delay_model: TurnDelayModel, - time_fieldname: String, } impl TurnDelayAccessModel {} @@ -20,28 +19,40 @@ impl TurnDelayAccessModel {} impl AccessModel for TurnDelayAccessModel { fn access_edge( &self, - _v1: &Vertex, - src: &Edge, - _v2: &Vertex, - dst: &Edge, - _v3: &Vertex, + traversal: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), state: &TraversalState, + state_variable_indices: Vec<(String, usize)>, ) -> Result, TraversalModelError> { + let (_v1, src, _v2, dst, _v3) = traversal; let src_heading = get_headings(&self.edge_headings, src.edge_id)?; let dst_heading = get_headings(&self.edge_headings, dst.edge_id)?; - let angle = src_heading.next_edge_angle(&dst_heading); + let angle = src_heading.bearing_to_destination(&dst_heading); // TODO: - // - an AccessModel should be able to update the state - // - turn delays (time) - // - charging stations (energy) - // - perhaps there should be a broker that is upstream of the access and traversal - // models that manages state allocation/cloning, inspection and updates - - // let delay = self.turn_delay_model.get_delay(angle, target_time_unit)?; - // let updated_state = add_time_to_state(state, time); - // Ok(Some(updated_state)) - todo!() + // WAIT, how do we get a time unit here? + let target_time_unit = &crate::model::unit::TimeUnit::Hours; + + let delay = self.turn_delay_model.get_delay(angle, target_time_unit)?; + let (_, idx) = state_variable_indices + .iter() + .find(|(n, _)| n == "time") + .ok_or_else(|| { + TraversalModelError::InternalError(String::from( + "turn delay model assumes a 'time' state variable which is not present", + )) + })?; + + let mut updated_state = state.clone(); + if updated_state.len() <= *idx { + return Err(TraversalModelError::InternalError(format!( + "turn delay model expected 'time' variable at index {} which is out of range for state {:?}", + idx, + state + ))); + } + + updated_state[*idx] = updated_state[*idx] + delay.into(); + Ok(Some(updated_state)) } } From 9f296cc139607cfaa68a0c7c9d281b3be55848ff Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 10:40:50 -0600 Subject: [PATCH 05/17] "access" as top-level compass model --- .../src/algorithm/search/edge_traversal.rs | 8 +- .../src/algorithm/search/search_error.rs | 3 + .../src/algorithm/search/search_instance.rs | 2 + .../{traversal => }/access/access_model.rs | 18 ++- .../src/model/access/access_model_builder.rs | 23 ++++ .../access/access_model_error.rs | 4 + .../src/model/access/access_model_service.rs | 21 ++++ .../{traversal => access}/access_result.rs | 0 .../{traversal => }/access/default/mod.rs | 0 .../default/turn_delays/edge_heading.rs | 0 .../access/default/turn_delays/mod.rs | 2 + .../access/default/turn_delays/turn.rs | 2 +- .../turn_delays/turn_delay_access_model.rs | 51 ++++++++ .../turn_delay_access_model_engine.rs | 8 ++ .../turn_delay_access_model_service.rs | 21 ++++ .../default/turn_delays/turn_delay_model.rs | 11 +- .../src/model/{traversal => }/access/mod.rs | 2 + rust/routee-compass-core/src/model/mod.rs | 1 + .../turn_delays/turn_delay_access_model.rs | 72 ----------- .../default/distance_traversal_model.rs | 9 -- .../default/speed_traversal_model.rs | 9 -- .../src/model/traversal/mod.rs | 2 - .../src/model/traversal/traversal_model.rs | 52 ++++---- .../model/traversal/traversal_model_error.rs | 3 - .../src/app/compass/compass_app.rs | 14 +++ .../app/compass/config/access_model/mod.rs | 1 + .../turn_delay_access_model_builder.rs | 17 +++ .../app/compass/config/compass_app_builder.rs | 115 ++++++------------ .../config/compass_configuration_error.rs | 3 + .../config/compass_configuration_field.rs | 2 + .../src/app/compass/config/mod.rs | 1 + .../src/app/search/search_app.rs | 12 +- 32 files changed, 272 insertions(+), 217 deletions(-) rename rust/routee-compass-core/src/model/{traversal => }/access/access_model.rs (59%) create mode 100644 rust/routee-compass-core/src/model/access/access_model_builder.rs rename rust/routee-compass-core/src/model/{traversal => }/access/access_model_error.rs (69%) create mode 100644 rust/routee-compass-core/src/model/access/access_model_service.rs rename rust/routee-compass-core/src/model/{traversal => access}/access_result.rs (100%) rename rust/routee-compass-core/src/model/{traversal => }/access/default/mod.rs (100%) rename rust/routee-compass-core/src/model/{traversal => }/access/default/turn_delays/edge_heading.rs (100%) rename rust/routee-compass-core/src/model/{traversal => }/access/default/turn_delays/mod.rs (53%) rename rust/routee-compass-core/src/model/{traversal => }/access/default/turn_delays/turn.rs (93%) create mode 100644 rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model.rs create mode 100644 rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_engine.rs create mode 100644 rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_service.rs rename rust/routee-compass-core/src/model/{traversal => }/access/default/turn_delays/turn_delay_model.rs (73%) rename rust/routee-compass-core/src/model/{traversal => }/access/mod.rs (52%) delete mode 100644 rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs create mode 100644 rust/routee-compass/src/app/compass/config/access_model/mod.rs create mode 100644 rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs diff --git a/rust/routee-compass-core/src/algorithm/search/edge_traversal.rs b/rust/routee-compass-core/src/algorithm/search/edge_traversal.rs index 77617999..f2f51b75 100644 --- a/rust/routee-compass-core/src/algorithm/search/edge_traversal.rs +++ b/rust/routee-compass-core/src/algorithm/search/edge_traversal.rs @@ -64,11 +64,9 @@ impl EdgeTraversal { let (v2, e2, v3) = traversal_trajectory; let access_trajectory = (v1, e1, v2, e2, v3); - si.traversal_model.access_edge( - access_trajectory, - &mut result_state, - &si.state_model, - )?; + + si.access_model + .access_edge(access_trajectory, &mut result_state, &si.state_model)?; let ac = si .cost_model diff --git a/rust/routee-compass-core/src/algorithm/search/search_error.rs b/rust/routee-compass-core/src/algorithm/search/search_error.rs index b35a291c..0777d61b 100644 --- a/rust/routee-compass-core/src/algorithm/search/search_error.rs +++ b/rust/routee-compass-core/src/algorithm/search/search_error.rs @@ -1,4 +1,5 @@ use crate::model::{ + access::access_model_error::AccessModelError, cost::cost_error::CostError, frontier::frontier_model_error::FrontierModelError, road_network::{edge_id::EdgeId, graph_error::GraphError, vertex_id::VertexId}, @@ -20,6 +21,8 @@ pub enum SearchError { #[error(transparent)] TraversalModelFailure(#[from] TraversalModelError), #[error(transparent)] + AccessModelFailure(#[from] AccessModelError), + #[error(transparent)] FrontierModelFailure(#[from] FrontierModelError), #[error(transparent)] CostError(#[from] CostError), diff --git a/rust/routee-compass-core/src/algorithm/search/search_instance.rs b/rust/routee-compass-core/src/algorithm/search/search_instance.rs index c4c8cf23..85005033 100644 --- a/rust/routee-compass-core/src/algorithm/search/search_instance.rs +++ b/rust/routee-compass-core/src/algorithm/search/search_instance.rs @@ -1,5 +1,6 @@ use super::search_error::SearchError; use crate::model::{ + access::access_model::AccessModel, cost::cost_model::CostModel, frontier::frontier_model::FrontierModel, road_network::{graph::Graph, vertex_id::VertexId}, @@ -16,6 +17,7 @@ pub struct SearchInstance { pub directed_graph: Arc, pub state_model: Arc, pub traversal_model: Arc, + pub access_model: Arc, pub cost_model: CostModel, pub frontier_model: Arc, pub termination_model: Arc, diff --git a/rust/routee-compass-core/src/model/traversal/access/access_model.rs b/rust/routee-compass-core/src/model/access/access_model.rs similarity index 59% rename from rust/routee-compass-core/src/model/traversal/access/access_model.rs rename to rust/routee-compass-core/src/model/access/access_model.rs index 5e48ace7..c57deccd 100644 --- a/rust/routee-compass-core/src/model/traversal/access/access_model.rs +++ b/rust/routee-compass-core/src/model/access/access_model.rs @@ -1,11 +1,17 @@ +use super::access_model_error::AccessModelError; use crate::model::{ property::{edge::Edge, vertex::Vertex}, - traversal::{ - state::traversal_state::TraversalState, traversal_model_error::TraversalModelError, - }, + state::{state_feature::StateFeature, state_model::StateModel}, + traversal::state::state_variable::StateVar, }; pub trait AccessModel { + /// lists the state variables expected by this access model that are not + /// defined on the base configuration. for example, if this access model + /// has state variables that differ based on the query, they can be injected + /// into the state model by listing them here. + fn state_features(&self) -> Vec<(String, StateFeature)>; + /// Updates the traversal state by accessing some destination edge /// when coming from some previous edge. /// @@ -27,7 +33,7 @@ pub trait AccessModel { fn access_edge( &self, traversal: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), - state: &TraversalState, - state_variable_indices: Vec<(String, usize)>, - ) -> Result, TraversalModelError>; + state: &mut Vec, + state_model: &StateModel, + ) -> Result<(), AccessModelError>; } diff --git a/rust/routee-compass-core/src/model/access/access_model_builder.rs b/rust/routee-compass-core/src/model/access/access_model_builder.rs new file mode 100644 index 00000000..6d56e21c --- /dev/null +++ b/rust/routee-compass-core/src/model/access/access_model_builder.rs @@ -0,0 +1,23 @@ +use super::{access_model_error::AccessModelError, access_model_service::AccessModelService}; +use std::sync::Arc; + +/// A [`AccessModelBuilder`] takes a JSON object describing the configuration of a +/// traversal model and builds a [`AccessModelService`]. +/// +/// A [`AccessModelBuilder`] instance should be an empty struct that implements +/// this trait. +pub trait AccessModelBuilder { + /// Builds a [`AccessModelService`] from configuration. + /// + /// # Arguments + /// + /// * `parameters` - the contents of the "traversal" TOML config section + /// + /// # Returns + /// + /// A [`AccessModelService`] designed to persist the duration of the CompassApp. + fn build( + &self, + parameters: &serde_json::Value, + ) -> Result, AccessModelError>; +} diff --git a/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs b/rust/routee-compass-core/src/model/access/access_model_error.rs similarity index 69% rename from rust/routee-compass-core/src/model/traversal/access/access_model_error.rs rename to rust/routee-compass-core/src/model/access/access_model_error.rs index 7de4122e..4c3b03ce 100644 --- a/rust/routee-compass-core/src/model/traversal/access/access_model_error.rs +++ b/rust/routee-compass-core/src/model/access/access_model_error.rs @@ -1,7 +1,11 @@ +use crate::model::state::state_error::StateError; + #[derive(thiserror::Error, Debug)] pub enum AccessModelError { #[error("error while executing access model {name}: {error}")] RuntimeError { name: String, error: String }, #[error(transparent)] + StateError(#[from] StateError), + #[error(transparent)] SerdeJsonError(#[from] serde_json::Error), } diff --git a/rust/routee-compass-core/src/model/access/access_model_service.rs b/rust/routee-compass-core/src/model/access/access_model_service.rs new file mode 100644 index 00000000..8968539d --- /dev/null +++ b/rust/routee-compass-core/src/model/access/access_model_service.rs @@ -0,0 +1,21 @@ +use super::{access_model::AccessModel, access_model_error::AccessModelError}; +use std::sync::Arc; + +pub trait AccessModelService: Send + Sync { + /// Builds a [AccessModel] for the incoming query, used as parameters for this + /// build operation. + /// + /// The query is passed as parameters to this operation so that any query-time + /// coefficients may be applied to the [AccessModel]. + /// + /// # Arguments + /// + /// * `query` - the incoming query which may contain parameters for building the [AccessModel] + /// + /// # Returns + /// + /// The [AccessModel] instance for this query, or an error + /// + /// [AccessModel]: compass_core::model::access::access_model::AccessModel + fn build(&self, query: &serde_json::Value) -> Result, AccessModelError>; +} diff --git a/rust/routee-compass-core/src/model/traversal/access_result.rs b/rust/routee-compass-core/src/model/access/access_result.rs similarity index 100% rename from rust/routee-compass-core/src/model/traversal/access_result.rs rename to rust/routee-compass-core/src/model/access/access_result.rs diff --git a/rust/routee-compass-core/src/model/traversal/access/default/mod.rs b/rust/routee-compass-core/src/model/access/default/mod.rs similarity index 100% rename from rust/routee-compass-core/src/model/traversal/access/default/mod.rs rename to rust/routee-compass-core/src/model/access/default/mod.rs diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/edge_heading.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/edge_heading.rs similarity index 100% rename from rust/routee-compass-core/src/model/traversal/access/default/turn_delays/edge_heading.rs rename to rust/routee-compass-core/src/model/access/default/turn_delays/edge_heading.rs diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/mod.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/mod.rs similarity index 53% rename from rust/routee-compass-core/src/model/traversal/access/default/turn_delays/mod.rs rename to rust/routee-compass-core/src/model/access/default/turn_delays/mod.rs index 10cb003e..0de71fed 100644 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/mod.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/mod.rs @@ -1,4 +1,6 @@ pub mod edge_heading; pub mod turn; pub mod turn_delay_access_model; +pub mod turn_delay_access_model_engine; +pub mod turn_delay_access_model_service; pub mod turn_delay_model; diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn.rs similarity index 93% rename from rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs rename to rust/routee-compass-core/src/model/access/default/turn_delays/turn.rs index 294deb64..17e56417 100644 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn.rs @@ -1,4 +1,4 @@ -use crate::model::traversal::access::access_model_error::AccessModelError; +use crate::model::access::access_model_error::AccessModelError; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Eq, Hash)] diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model.rs new file mode 100644 index 00000000..7ddc6309 --- /dev/null +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model.rs @@ -0,0 +1,51 @@ +use super::{ + edge_heading::EdgeHeading, turn_delay_access_model_engine::TurnDelayAccessModelEngine, +}; +use crate::model::{ + access::{access_model::AccessModel, access_model_error::AccessModelError}, + property::{edge::Edge, vertex::Vertex}, + road_network::edge_id::EdgeId, + state::{state_feature::StateFeature, state_model::StateModel}, + traversal::state::state_variable::StateVar, +}; +use std::sync::Arc; + +pub struct TurnDelayAccessModel { + pub engine: Arc, +} + +impl AccessModel for TurnDelayAccessModel { + fn access_edge( + &self, + traversal: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), + state: &mut Vec, + state_model: &StateModel, + ) -> Result<(), AccessModelError> { + let (_v1, src, _v2, dst, _v3) = traversal; + let src_heading = get_headings(&self.engine.edge_headings, src.edge_id)?; + let dst_heading = get_headings(&self.engine.edge_headings, dst.edge_id)?; + let angle = src_heading.bearing_to_destination(&dst_heading); + let (delay, delay_unit) = self.engine.turn_delay_model.get_delay(angle)?; + state_model.add_time(state, &self.engine.time_feature_name, &delay, delay_unit)?; + Ok(()) + } + + fn state_features(&self) -> Vec<(String, StateFeature)> { + vec![] + } +} + +/// lookup up the edge heading from the headings table +pub fn get_headings( + headings_table: &[EdgeHeading], + edge_id: EdgeId, +) -> Result { + let heading: &EdgeHeading = + headings_table + .get(edge_id.as_usize()) + .ok_or_else(|| AccessModelError::RuntimeError { + name: String::from("turn delay access model"), + error: format!("missing edge id {} ", edge_id), + })?; + Ok(*heading) +} diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_engine.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_engine.rs new file mode 100644 index 00000000..0ca0641a --- /dev/null +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_engine.rs @@ -0,0 +1,8 @@ +use super::edge_heading::EdgeHeading; +use super::turn_delay_model::TurnDelayModel; + +pub struct TurnDelayAccessModelEngine { + pub edge_headings: Box<[EdgeHeading]>, + pub turn_delay_model: TurnDelayModel, + pub time_feature_name: String, +} diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_service.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_service.rs new file mode 100644 index 00000000..2cdea857 --- /dev/null +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_service.rs @@ -0,0 +1,21 @@ +use super::turn_delay_access_model::TurnDelayAccessModel; +use super::turn_delay_access_model_engine::TurnDelayAccessModelEngine; +use crate::model::access::access_model::AccessModel; +use crate::model::access::access_model_error::AccessModelError; +use crate::model::access::access_model_service::AccessModelService; +use std::sync::Arc; + +pub struct TurnDelayAccessModelService { + pub engine: Arc, +} + +impl TurnDelayAccessModelService {} + +impl AccessModelService for TurnDelayAccessModelService { + fn build(&self, query: &serde_json::Value) -> Result, AccessModelError> { + let model = TurnDelayAccessModel { + engine: self.engine.clone(), + }; + Ok(Arc::new(model)) + } +} diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs similarity index 73% rename from rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs rename to rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs index f219ef88..1c9eb2b4 100644 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_model.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs @@ -1,6 +1,6 @@ use super::turn::Turn; use crate::model::{ - traversal::access::access_model_error::AccessModelError, + access::access_model_error::AccessModelError, unit::{Time, TimeUnit}, }; use std::collections::HashMap; @@ -14,11 +14,7 @@ pub enum TurnDelayModel { } impl TurnDelayModel { - pub fn get_delay( - &self, - angle: i16, - target_time_unit: &TimeUnit, - ) -> Result { + pub fn get_delay(&self, angle: i16) -> Result<(Time, &TimeUnit), AccessModelError> { match self { TurnDelayModel::TabularDiscrete { table, time_unit } => { let turn = Turn::from_angle(angle)?; @@ -27,8 +23,7 @@ impl TurnDelayModel { let error = format!("table missing entry for turn {}", turn.to_string()); AccessModelError::RuntimeError { name, error } })?; - let result = time_unit.convert(*delay, target_time_unit); - Ok(result) + Ok((*delay, time_unit)) } } } diff --git a/rust/routee-compass-core/src/model/traversal/access/mod.rs b/rust/routee-compass-core/src/model/access/mod.rs similarity index 52% rename from rust/routee-compass-core/src/model/traversal/access/mod.rs rename to rust/routee-compass-core/src/model/access/mod.rs index 100a3d17..7f7b2145 100644 --- a/rust/routee-compass-core/src/model/traversal/access/mod.rs +++ b/rust/routee-compass-core/src/model/access/mod.rs @@ -1,3 +1,5 @@ pub mod access_model; +pub mod access_model_builder; pub mod access_model_error; +pub mod access_model_service; pub mod default; diff --git a/rust/routee-compass-core/src/model/mod.rs b/rust/routee-compass-core/src/model/mod.rs index 4dfcc06d..cf79cd37 100644 --- a/rust/routee-compass-core/src/model/mod.rs +++ b/rust/routee-compass-core/src/model/mod.rs @@ -1,3 +1,4 @@ +pub mod access; pub mod cost; pub mod frontier; pub mod property; diff --git a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs b/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs deleted file mode 100644 index dcd6f341..00000000 --- a/rust/routee-compass-core/src/model/traversal/access/default/turn_delays/turn_delay_access_model.rs +++ /dev/null @@ -1,72 +0,0 @@ -use super::edge_heading::EdgeHeading; -use super::turn_delay_model::TurnDelayModel; -use crate::model::road_network::edge_id::EdgeId; -use crate::model::traversal::access::access_model::AccessModel; -use crate::model::{ - property::{edge::Edge, vertex::Vertex}, - traversal::{ - state::traversal_state::TraversalState, traversal_model_error::TraversalModelError, - }, -}; - -pub struct TurnDelayAccessModel { - edge_headings: Box<[EdgeHeading]>, - turn_delay_model: TurnDelayModel, -} - -impl TurnDelayAccessModel {} - -impl AccessModel for TurnDelayAccessModel { - fn access_edge( - &self, - traversal: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), - state: &TraversalState, - state_variable_indices: Vec<(String, usize)>, - ) -> Result, TraversalModelError> { - let (_v1, src, _v2, dst, _v3) = traversal; - let src_heading = get_headings(&self.edge_headings, src.edge_id)?; - let dst_heading = get_headings(&self.edge_headings, dst.edge_id)?; - let angle = src_heading.bearing_to_destination(&dst_heading); - - // TODO: - // WAIT, how do we get a time unit here? - let target_time_unit = &crate::model::unit::TimeUnit::Hours; - - let delay = self.turn_delay_model.get_delay(angle, target_time_unit)?; - let (_, idx) = state_variable_indices - .iter() - .find(|(n, _)| n == "time") - .ok_or_else(|| { - TraversalModelError::InternalError(String::from( - "turn delay model assumes a 'time' state variable which is not present", - )) - })?; - - let mut updated_state = state.clone(); - if updated_state.len() <= *idx { - return Err(TraversalModelError::InternalError(format!( - "turn delay model expected 'time' variable at index {} which is out of range for state {:?}", - idx, - state - ))); - } - - updated_state[*idx] = updated_state[*idx] + delay.into(); - Ok(Some(updated_state)) - } -} - -/// lookup up the edge heading from the headings table -pub fn get_headings( - headings_table: &[EdgeHeading], - edge_id: EdgeId, -) -> Result { - let heading: &EdgeHeading = headings_table.get(edge_id.as_usize()).ok_or_else(|| { - TraversalModelError::MissingIdInTabularCostFunction( - format!("{}", edge_id), - String::from("EdgeId"), - String::from("headings table"), - ) - })?; - Ok(*heading) -} diff --git a/rust/routee-compass-core/src/model/traversal/default/distance_traversal_model.rs b/rust/routee-compass-core/src/model/traversal/default/distance_traversal_model.rs index fe816ef8..9a018e16 100644 --- a/rust/routee-compass-core/src/model/traversal/default/distance_traversal_model.rs +++ b/rust/routee-compass-core/src/model/traversal/default/distance_traversal_model.rs @@ -34,15 +34,6 @@ impl TraversalModel for DistanceTraversalModel { Ok(()) } - fn access_edge( - &self, - _trajectory: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), - _state: &mut Vec, - _state_model: &StateModel, - ) -> Result<(), TraversalModelError> { - Ok(()) - } - fn estimate_traversal( &self, od: (&Vertex, &Vertex), diff --git a/rust/routee-compass-core/src/model/traversal/default/speed_traversal_model.rs b/rust/routee-compass-core/src/model/traversal/default/speed_traversal_model.rs index 1b2b23f4..25f7d7e8 100644 --- a/rust/routee-compass-core/src/model/traversal/default/speed_traversal_model.rs +++ b/rust/routee-compass-core/src/model/traversal/default/speed_traversal_model.rs @@ -45,15 +45,6 @@ impl TraversalModel for SpeedTraversalModel { Ok(()) } - fn access_edge( - &self, - _trajectory: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), - _state: &mut Vec, - _state_model: &StateModel, - ) -> Result<(), TraversalModelError> { - Ok(()) - } - fn estimate_traversal( &self, od: (&Vertex, &Vertex), diff --git a/rust/routee-compass-core/src/model/traversal/mod.rs b/rust/routee-compass-core/src/model/traversal/mod.rs index 94600155..fd354acc 100644 --- a/rust/routee-compass-core/src/model/traversal/mod.rs +++ b/rust/routee-compass-core/src/model/traversal/mod.rs @@ -1,5 +1,3 @@ -pub mod access; -pub mod access_result; pub mod default; pub mod state; pub mod traversal_model; diff --git a/rust/routee-compass-core/src/model/traversal/traversal_model.rs b/rust/routee-compass-core/src/model/traversal/traversal_model.rs index 6709f12f..f05d2c39 100644 --- a/rust/routee-compass-core/src/model/traversal/traversal_model.rs +++ b/rust/routee-compass-core/src/model/traversal/traversal_model.rs @@ -38,32 +38,32 @@ pub trait TraversalModel: Send + Sync { state_model: &StateModel, ) -> Result<(), TraversalModelError>; - /// Updates the traversal state by accessing some destination edge - /// when coming from some previous edge. - /// - /// These arguments appear in the network as: - /// `(v1) -[prev]-> (v2) -[next]-> (v3)` - /// Where `next` is the edge we want to access. - /// - /// # Arguments - /// - /// * `v1` - src of previous edge - /// * `src` - previous edge - /// * `v2` - src vertex of the next edge - /// * `dst` - edge we are determining the cost to access - /// * `v3` - dst vertex of the next edge - /// * `state` - state of the search at the beginning of the dst edge - /// - /// # Returns - /// - /// Either an optional access result or an error. if there are no - /// state updates due to access, None is returned. - fn access_edge( - &self, - trajectory: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), - state: &mut Vec, - state_model: &StateModel, - ) -> Result<(), TraversalModelError>; + // /// Updates the traversal state by accessing some destination edge + // /// when coming from some previous edge. + // /// + // /// These arguments appear in the network as: + // /// `(v1) -[prev]-> (v2) -[next]-> (v3)` + // /// Where `next` is the edge we want to access. + // /// + // /// # Arguments + // /// + // /// * `v1` - src of previous edge + // /// * `src` - previous edge + // /// * `v2` - src vertex of the next edge + // /// * `dst` - edge we are determining the cost to access + // /// * `v3` - dst vertex of the next edge + // /// * `state` - state of the search at the beginning of the dst edge + // /// + // /// # Returns + // /// + // /// Either an optional access result or an error. if there are no + // /// state updates due to access, None is returned. + // fn access_edge( + // &self, + // trajectory: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), + // state: &mut Vec, + // state_model: &StateModel, + // ) -> Result<(), TraversalModelError>; /// Estimates the traversal state by traversing between two vertices without /// performing any graph traversals. diff --git a/rust/routee-compass-core/src/model/traversal/traversal_model_error.rs b/rust/routee-compass-core/src/model/traversal/traversal_model_error.rs index bd17bed2..ecba24af 100644 --- a/rust/routee-compass-core/src/model/traversal/traversal_model_error.rs +++ b/rust/routee-compass-core/src/model/traversal/traversal_model_error.rs @@ -1,4 +1,3 @@ -use super::access::access_model_error::AccessModelError; use super::state::traversal_state::TraversalState; use crate::model::road_network::graph_error::GraphError; use crate::model::state::state_error::StateError; @@ -27,8 +26,6 @@ pub enum TraversalModelError { #[error(transparent)] GraphError(#[from] GraphError), #[error(transparent)] - AccessModelError(#[from] AccessModelError), - #[error(transparent)] StateError(#[from] StateError), #[error("prediction model failed with error {0}")] PredictionModel(String), diff --git a/rust/routee-compass/src/app/compass/compass_app.rs b/rust/routee-compass/src/app/compass/compass_app.rs index c27de1b7..dc48138a 100644 --- a/rust/routee-compass/src/app/compass/compass_app.rs +++ b/rust/routee-compass/src/app/compass/compass_app.rs @@ -165,6 +165,19 @@ impl TryFrom<(&Config, &CompassAppBuilder)> for CompassApp { traversal_duration.hhmmss() ); + // build access model + let access_start = Local::now(); + let access_params = + config_json.get_config_section(CompassConfigurationField::Access, &"TOML")?; + let access_model_service = builder.build_access_model_service(&access_params)?; + let access_duration = (Local::now() - access_start) + .to_std() + .map_err(|e| CompassAppError::InternalError(e.to_string()))?; + log::info!( + "finished reading access model with duration {}", + access_duration.hhmmss() + ); + // build utility model let cost_params = config_json.get_config_section(CompassConfigurationField::Cost, &"TOML")?; @@ -239,6 +252,7 @@ impl TryFrom<(&Config, &CompassAppBuilder)> for CompassApp { graph, state_model, traversal_model_service, + access_model_service, cost_model_service, frontier_model_service, termination_model, diff --git a/rust/routee-compass/src/app/compass/config/access_model/mod.rs b/rust/routee-compass/src/app/compass/config/access_model/mod.rs new file mode 100644 index 00000000..2e16f609 --- /dev/null +++ b/rust/routee-compass/src/app/compass/config/access_model/mod.rs @@ -0,0 +1 @@ +pub mod turn_delay_access_model_builder; diff --git a/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs b/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs new file mode 100644 index 00000000..1beaefe5 --- /dev/null +++ b/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs @@ -0,0 +1,17 @@ +use std::sync::Arc; + +use routee_compass_core::model::access::{ + access_model_builder::AccessModelBuilder, access_model_error::AccessModelError, + access_model_service::AccessModelService, +}; + +pub struct TurnDelayAccessModelBuilder {} + +impl AccessModelBuilder for TurnDelayAccessModelBuilder { + fn build( + &self, + parameters: &serde_json::Value, + ) -> Result, AccessModelError> { + todo!() + } +} diff --git a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs index a568ebe2..74add05d 100644 --- a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs +++ b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs @@ -1,4 +1,5 @@ use super::{ + access_model::turn_delay_access_model_builder::TurnDelayAccessModelBuilder, builders::{InputPluginBuilder, OutputPluginBuilder}, compass_configuration_error::CompassConfigurationError, compass_configuration_field::CompassConfigurationField, @@ -32,9 +33,9 @@ use crate::plugin::{ output_plugin::OutputPlugin, }, }; - use itertools::Itertools; use routee_compass_core::model::{ + access::{access_model_builder::AccessModelBuilder, access_model_service::AccessModelService}, frontier::{ frontier_model_builder::FrontierModelBuilder, frontier_model_service::FrontierModelService, }, @@ -69,6 +70,7 @@ use std::{collections::HashMap, rc::Rc, sync::Arc}; /// pub struct CompassAppBuilder { pub traversal_model_builders: HashMap>, + pub access_model_builders: HashMap>, pub frontier_builders: HashMap>, pub input_plugin_builders: HashMap>, pub output_plugin_builders: HashMap>, @@ -88,6 +90,7 @@ impl CompassAppBuilder { pub fn new() -> CompassAppBuilder { CompassAppBuilder { traversal_model_builders: HashMap::new(), + access_model_builders: HashMap::new(), frontier_builders: HashMap::new(), input_plugin_builders: HashMap::new(), output_plugin_builders: HashMap::new(), @@ -98,6 +101,10 @@ impl CompassAppBuilder { let _ = self.traversal_model_builders.insert(name, builder); } + pub fn add_access_model(&mut self, name: String, builder: Rc) { + let _ = self.access_model_builders.insert(name, builder); + } + pub fn add_frontier_model(&mut self, name: String, builder: Rc) { let _ = self.frontier_builders.insert(name, builder); } @@ -130,6 +137,11 @@ impl CompassAppBuilder { (String::from("energy_model"), energy), ]); + // Access model builders + let turn_delay: Rc = Rc::new(TurnDelayAccessModelBuilder {}); + let am_builders: HashMap> = + HashMap::from([(String::from("turn_delay"), turn_delay)]); + // Frontier model builders let no_restriction: Rc = Rc::new(NoRestrictionBuilder {}); let road_class: Rc = Rc::new(RoadClassBuilder {}); @@ -176,6 +188,7 @@ impl CompassAppBuilder { CompassAppBuilder { traversal_model_builders: tm_builders, + access_model_builders: am_builders, frontier_builders: all_frontier_builders, input_plugin_builders, output_plugin_builders, @@ -188,21 +201,7 @@ impl CompassAppBuilder { &self, config: &serde_json::Value, ) -> Result, CompassConfigurationError> { - let tm_type_obj = config.get("type").ok_or_else(|| { - CompassConfigurationError::ExpectedFieldForComponent( - CompassConfigurationField::Traversal.to_string(), - String::from("type"), - ) - })?; - let tm_type: String = tm_type_obj - .as_str() - .ok_or_else(|| { - CompassConfigurationError::ExpectedFieldWithType( - String::from("type"), - String::from("String"), - ) - })? - .into(); + let tm_type = config.get_config_string(&"type", &"traversal")?; let result = self .traversal_model_builders .get(&tm_type) @@ -220,27 +219,35 @@ impl CompassAppBuilder { result } + pub fn build_access_model_service( + &self, + config: &serde_json::Value, + ) -> Result, CompassConfigurationError> { + let tm_type = config.get_config_string(&"type", &"access")?; + let result = self + .access_model_builders + .get(&tm_type) + .ok_or_else(|| { + CompassConfigurationError::UnknownModelNameForComponent( + tm_type.clone(), + String::from("access"), + self.access_model_builders.keys().join(", "), + ) + }) + .and_then(|b| { + b.build(config) + .map_err(CompassConfigurationError::AccessModelError) + }); + result + } + /// builds a frontier model with the specified type name with the provided /// frontier model configuration JSON pub fn build_frontier_model_service( &self, config: &serde_json::Value, ) -> Result, CompassConfigurationError> { - let fm_type_obj = config.get("type").ok_or_else(|| { - CompassConfigurationError::ExpectedFieldForComponent( - CompassConfigurationField::Frontier.to_string(), - String::from("type"), - ) - })?; - let fm_type: String = fm_type_obj - .as_str() - .ok_or_else(|| { - CompassConfigurationError::ExpectedFieldWithType( - String::from("type"), - String::from("String"), - ) - })? - .into(); + let fm_type = config.get_config_string(&"type", &"frontier")?; self.frontier_builders .get(&fm_type) .ok_or_else(|| { @@ -267,28 +274,7 @@ impl CompassAppBuilder { let mut plugins: Vec> = Vec::new(); for plugin_json in input_plugins.into_iter() { - let plugin_type_obj = plugin_json.as_object().ok_or_else(|| { - CompassConfigurationError::ExpectedFieldWithType( - String::from("type"), - String::from("Json Object"), - ) - })?; - let plugin_type: String = plugin_type_obj - .get("type") - .ok_or_else(|| { - CompassConfigurationError::ExpectedFieldForComponent( - CompassConfigurationField::InputPlugins.to_string(), - String::from("type"), - ) - })? - .as_str() - .ok_or_else(|| { - CompassConfigurationError::ExpectedFieldWithType( - String::from("type"), - String::from("String"), - ) - })? - .into(); + let plugin_type = config.get_config_string(&"type", &"input_plugin")?; let builder = self .input_plugin_builders .get(&plugin_type) @@ -316,28 +302,7 @@ impl CompassAppBuilder { let mut plugins: Vec> = Vec::new(); for plugin_json in output_plugins.into_iter() { - let plugin_json_obj = plugin_json.as_object().ok_or_else(|| { - CompassConfigurationError::ExpectedFieldWithType( - String::from("output_plugins"), - String::from("Json Object"), - ) - })?; - let plugin_type: String = plugin_json_obj - .get("type") - .ok_or_else(|| { - CompassConfigurationError::ExpectedFieldForComponent( - CompassConfigurationField::OutputPlugins.to_string(), - String::from("type"), - ) - })? - .as_str() - .ok_or_else(|| { - CompassConfigurationError::ExpectedFieldWithType( - String::from("type"), - String::from("String"), - ) - })? - .into(); + let plugin_type = config.get_config_string(&"type", &"output_plugin")?; let builder = self .output_plugin_builders .get(&plugin_type) diff --git a/rust/routee-compass/src/app/compass/config/compass_configuration_error.rs b/rust/routee-compass/src/app/compass/config/compass_configuration_error.rs index 52dbd1f2..729e3203 100644 --- a/rust/routee-compass/src/app/compass/config/compass_configuration_error.rs +++ b/rust/routee-compass/src/app/compass/config/compass_configuration_error.rs @@ -2,6 +2,7 @@ use crate::plugin::plugin_error::PluginError; use config::ConfigError; use routee_compass_core::{ model::{ + access::access_model_error::AccessModelError, frontier::frontier_model_error::FrontierModelError, road_network::graph_error::GraphError, traversal::traversal_model_error::TraversalModelError, }, @@ -66,6 +67,8 @@ pub enum CompassConfigurationError { #[error(transparent)] TraversalModelError(#[from] TraversalModelError), #[error(transparent)] + AccessModelError(#[from] AccessModelError), + #[error(transparent)] FrontierModelError(#[from] FrontierModelError), #[error(transparent)] PluginError(#[from] PluginError), diff --git a/rust/routee-compass/src/app/compass/config/compass_configuration_field.rs b/rust/routee-compass/src/app/compass/config/compass_configuration_field.rs index 5a4e0bc0..aa38b953 100644 --- a/rust/routee-compass/src/app/compass/config/compass_configuration_field.rs +++ b/rust/routee-compass/src/app/compass/config/compass_configuration_field.rs @@ -7,6 +7,7 @@ pub enum CompassConfigurationField { Termination, State, Traversal, + Access, Cost, Algorithm, Plugins, @@ -27,6 +28,7 @@ impl CompassConfigurationField { match self { CompassConfigurationField::Graph => "graph", CompassConfigurationField::Traversal => "traversal", + CompassConfigurationField::Access => "access", CompassConfigurationField::Cost => "cost", CompassConfigurationField::State => "state", CompassConfigurationField::Frontier => "frontier", diff --git a/rust/routee-compass/src/app/compass/config/mod.rs b/rust/routee-compass/src/app/compass/config/mod.rs index 30210407..e0614d5b 100644 --- a/rust/routee-compass/src/app/compass/config/mod.rs +++ b/rust/routee-compass/src/app/compass/config/mod.rs @@ -1,3 +1,4 @@ +pub mod access_model; pub mod builders; pub mod compass_app_builder; pub mod compass_configuration_error; diff --git a/rust/routee-compass/src/app/search/search_app.rs b/rust/routee-compass/src/app/search/search_app.rs index 1fb48ebb..61d6e7ac 100644 --- a/rust/routee-compass/src/app/search/search_app.rs +++ b/rust/routee-compass/src/app/search/search_app.rs @@ -13,6 +13,7 @@ use routee_compass_core::{ search_instance::SearchInstance, }, model::{ + access::access_model_service::AccessModelService, frontier::frontier_model_service::FrontierModelService, road_network::graph::Graph, state::state_model::StateModel, termination::termination_model::TerminationModel, traversal::traversal_model_service::TraversalModelService, @@ -27,6 +28,7 @@ pub struct SearchApp { pub directed_graph: Arc, pub state_model: Arc, pub traversal_model_service: Arc, + pub access_model_service: Arc, pub cost_model_service: Arc, pub frontier_model_service: Arc, pub termination_model: Arc, @@ -40,6 +42,7 @@ impl SearchApp { graph: Graph, state_model: Arc, traversal_model_service: Arc, + access_model_service: Arc, cost_model_service: CostModelService, frontier_model_service: Arc, termination_model: TerminationModel, @@ -49,6 +52,7 @@ impl SearchApp { directed_graph: Arc::new(graph), state_model, traversal_model_service, + access_model_service, cost_model_service: Arc::new(cost_model_service), frontier_model_service, termination_model: Arc::new(termination_model), @@ -163,7 +167,12 @@ impl SearchApp { query: &serde_json::Value, ) -> Result { let traversal_model = self.traversal_model_service.build(query)?; - let state_model = Arc::new(self.state_model.extend(traversal_model.state_features())?); + let access_model = self.access_model_service.build(query)?; + + let mut added_features = traversal_model.state_features(); + added_features.extend(access_model.state_features()); + let state_model = Arc::new(self.state_model.extend(added_features)?); + let cost_model = self .cost_model_service .build(query, state_model.clone()) @@ -176,6 +185,7 @@ impl SearchApp { directed_graph: self.directed_graph.clone(), state_model, traversal_model, + access_model, cost_model, frontier_model, termination_model: self.termination_model.clone(), From b309a9bce6d08f0de25ca605ba2a2866a3c4e473 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 10:41:00 -0600 Subject: [PATCH 06/17] "access" as top-level compass model --- .../src/routee/energy_model_ops.rs | 5 +- .../src/routee/energy_model_service.rs | 3 +- .../src/routee/energy_traversal_model.rs | 58 ------------------- 3 files changed, 3 insertions(+), 63 deletions(-) diff --git a/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs b/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs index 2fe80a39..eb3238ce 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs @@ -1,7 +1,6 @@ -use routee_compass_core::model::traversal::access::default::turn_delays::edge_heading::EdgeHeading; use routee_compass_core::model::{ - road_network::edge_id::EdgeId, traversal::traversal_model_error::TraversalModelError, - unit::Grade, + access::default::turn_delays::edge_heading::EdgeHeading, road_network::edge_id::EdgeId, + traversal::traversal_model_error::TraversalModelError, unit::Grade, }; pub const ZERO_ENERGY: f64 = 1e-9; diff --git a/rust/routee-compass-powertrain/src/routee/energy_model_service.rs b/rust/routee-compass-powertrain/src/routee/energy_model_service.rs index eeab177f..7963b640 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_model_service.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_model_service.rs @@ -1,7 +1,6 @@ use super::energy_traversal_model::EnergyTraversalModel; use super::vehicle::VehicleType; -use routee_compass_core::model::traversal::access::default::turn_delays::edge_heading::EdgeHeading; -use routee_compass_core::model::traversal::default::speed_traversal_model::get_max_speed; +use routee_compass_core::model::access::default::turn_delays::edge_heading::EdgeHeading; use routee_compass_core::model::traversal::traversal_model::TraversalModel; use routee_compass_core::model::traversal::traversal_model_error::TraversalModelError; use routee_compass_core::model::traversal::traversal_model_service::TraversalModelService; diff --git a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs index 5fd8344f..ae07a39c 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs @@ -78,64 +78,6 @@ impl TraversalModel for EnergyTraversalModel { Ok(()) } - fn access_edge( - &self, - trajectory: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), - state: &mut Vec, - state_model: &StateModel, - ) -> Result<(), TraversalModelError> { - // defer access updates to time model - self.time_model.access_edge(trajectory, state, state_model) - // match self.energy_model_service.headings_table.as_deref() { - // None => Ok(None), - // Some(headings_table) => { - // let src_heading = get_headings(headings_table, src.edge_id)?; - // let dst_heading = get_headings(headings_table, dst.edge_id)?; - // let angle = src_heading.next_edge_angle(&dst_heading); - // let turn = Turn::from_angle(angle)?; - // let time_cost = match turn { - // Turn::NoTurn => { - // // no penalty for straight - // Time::new(0.0) - // } - // Turn::SlightRight => { - // // 0.5 second penalty for slight right - // Time::new(0.5) - // } - // Turn::Right => { - // // 1 second penalty for right - // Time::new(1.0) - // } - // Turn::SharpRight => { - // // 1.5 second penalty for sharp right - // Time::new(1.5) - // } - // Turn::SlightLeft => { - // // 1 second penalty for slight left - // Time::new(1.0) - // } - // Turn::Left => { - // // 2.5 second penalty for left - // Time::new(2.5) - // } - // Turn::SharpLeft => { - // // 3.5 second penalty for sharp left - // Time::new(3.5) - // } - // Turn::UTurn => { - // // 9.5 second penalty for U-turn - // Time::new(9.5) - // } - // }; - // let time = - // TimeUnit::Seconds.convert(time_cost, &self.energy_model_service.time_unit); - // let time_idx = self.time_model.get_state_variable(&"time", state)?; - // let mut updated_state = state.clone(); - // Ok(Some(updated_state)) - // } - // } - } - fn estimate_traversal( &self, od: (&Vertex, &Vertex), From 00b1fa2fab331659874aaf0a40c1ce4f4a5d9bb5 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 10:48:40 -0600 Subject: [PATCH 07/17] default "no access model" --- .../search/a_star/a_star_algorithm.rs | 2 + .../src/model/access/default/mod.rs | 1 + .../model/access/default/no_access_model.rs | 56 +++++++++++++++++++ .../turn_delay_access_model_service.rs | 2 +- .../src/app/compass/config.default.toml | 3 + .../app/compass/config/compass_app_builder.rs | 12 +++- 6 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 rust/routee-compass-core/src/model/access/default/no_access_model.rs diff --git a/rust/routee-compass-core/src/algorithm/search/a_star/a_star_algorithm.rs b/rust/routee-compass-core/src/algorithm/search/a_star/a_star_algorithm.rs index 2b6aaa3b..9f7c3f28 100644 --- a/rust/routee-compass-core/src/algorithm/search/a_star/a_star_algorithm.rs +++ b/rust/routee-compass-core/src/algorithm/search/a_star/a_star_algorithm.rs @@ -333,6 +333,7 @@ mod tests { use super::*; use crate::algorithm::search::backtrack::vertex_oriented_route; use crate::algorithm::search::MinSearchTree; + use crate::model::access::default::no_access_model::NoAccessModel; use crate::model::cost::cost_aggregation::CostAggregation; use crate::model::cost::cost_model::CostModel; use crate::model::cost::vehicle::vehicle_cost_rate::VehicleCostRate; @@ -460,6 +461,7 @@ mod tests { directed_graph: Arc::new(build_mock_graph()), state_model: state_model.clone(), traversal_model: Arc::new(DistanceTraversalModel::new(DistanceUnit::Meters)), + access_model: Arc::new(NoAccessModel {}), cost_model, frontier_model: Arc::new(NoRestriction {}), termination_model: Arc::new(TerminationModel::IterationsLimit { limit: 20 }), diff --git a/rust/routee-compass-core/src/model/access/default/mod.rs b/rust/routee-compass-core/src/model/access/default/mod.rs index fd81b89b..59cc04bf 100644 --- a/rust/routee-compass-core/src/model/access/default/mod.rs +++ b/rust/routee-compass-core/src/model/access/default/mod.rs @@ -1 +1,2 @@ +pub mod no_access_model; pub mod turn_delays; diff --git a/rust/routee-compass-core/src/model/access/default/no_access_model.rs b/rust/routee-compass-core/src/model/access/default/no_access_model.rs new file mode 100644 index 00000000..236adc53 --- /dev/null +++ b/rust/routee-compass-core/src/model/access/default/no_access_model.rs @@ -0,0 +1,56 @@ +use std::sync::Arc; + +use crate::model::access::{ + access_model::AccessModel, access_model_builder::AccessModelBuilder, + access_model_service::AccessModelService, +}; + +#[derive(Clone, Debug)] +pub struct NoAccessModel {} + +impl AccessModel for NoAccessModel { + fn state_features(&self) -> Vec<(String, crate::model::state::state_feature::StateFeature)> { + vec![] + } + + fn access_edge( + &self, + _traversal: ( + &crate::model::property::vertex::Vertex, + &crate::model::property::edge::Edge, + &crate::model::property::vertex::Vertex, + &crate::model::property::edge::Edge, + &crate::model::property::vertex::Vertex, + ), + _state: &mut Vec, + _state_model: &crate::model::state::state_model::StateModel, + ) -> Result<(), crate::model::access::access_model_error::AccessModelError> { + Ok(()) + } +} + +impl AccessModelService for NoAccessModel { + fn build( + &self, + _query: &serde_json::Value, + ) -> Result< + std::sync::Arc, + crate::model::access::access_model_error::AccessModelError, + > { + let model: Arc = Arc::new(self.clone()); + Ok(model) + } +} + +impl AccessModelBuilder for NoAccessModel { + fn build( + &self, + _parameters: &serde_json::Value, + ) -> Result< + Arc, + crate::model::access::access_model_error::AccessModelError, + > { + let service: Arc = Arc::new(self.clone()); + Ok(service) + } +} diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_service.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_service.rs index 2cdea857..f8c35c11 100644 --- a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_service.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_service.rs @@ -12,7 +12,7 @@ pub struct TurnDelayAccessModelService { impl TurnDelayAccessModelService {} impl AccessModelService for TurnDelayAccessModelService { - fn build(&self, query: &serde_json::Value) -> Result, AccessModelError> { + fn build(&self, _query: &serde_json::Value) -> Result, AccessModelError> { let model = TurnDelayAccessModel { engine: self.engine.clone(), }; diff --git a/rust/routee-compass/src/app/compass/config.default.toml b/rust/routee-compass/src/app/compass/config.default.toml index 026b848b..bd9123ed 100644 --- a/rust/routee-compass/src/app/compass/config.default.toml +++ b/rust/routee-compass/src/app/compass/config.default.toml @@ -24,6 +24,9 @@ type = "a*" type = "distance" distance_unit = "kilometers" +[access] +type = "no_access_model" + [cost] cost_aggregation = "sum" network_state_variable_rates = {} diff --git a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs index 74add05d..12c1c455 100644 --- a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs +++ b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs @@ -35,7 +35,10 @@ use crate::plugin::{ }; use itertools::Itertools; use routee_compass_core::model::{ - access::{access_model_builder::AccessModelBuilder, access_model_service::AccessModelService}, + access::{ + access_model_builder::AccessModelBuilder, access_model_service::AccessModelService, + default::no_access_model::NoAccessModel, + }, frontier::{ frontier_model_builder::FrontierModelBuilder, frontier_model_service::FrontierModelService, }, @@ -138,9 +141,12 @@ impl CompassAppBuilder { ]); // Access model builders + let no_access_model: Rc = Rc::new(NoAccessModel {}); let turn_delay: Rc = Rc::new(TurnDelayAccessModelBuilder {}); - let am_builders: HashMap> = - HashMap::from([(String::from("turn_delay"), turn_delay)]); + let am_builders: HashMap> = HashMap::from([ + (String::from("no_access_model"), no_access_model), + (String::from("turn_delay"), turn_delay), + ]); // Frontier model builders let no_restriction: Rc = Rc::new(NoRestrictionBuilder {}); From 6364a8f1666afc0d842f4afe9d85ae1a6f4bef0c Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 11:06:57 -0600 Subject: [PATCH 08/17] combined access model variant --- .../src/model/access/access_model.rs | 2 +- .../src/model/access/access_model_error.rs | 2 + .../model/access/default/combined_model.rs | 51 +++++++++++++++++++ .../src/model/access/default/mod.rs | 1 + .../combined_access_model_builder.rs | 49 ++++++++++++++++++ .../app/compass/config/access_model/mod.rs | 1 + 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 rust/routee-compass-core/src/model/access/default/combined_model.rs create mode 100644 rust/routee-compass/src/app/compass/config/access_model/combined_access_model_builder.rs diff --git a/rust/routee-compass-core/src/model/access/access_model.rs b/rust/routee-compass-core/src/model/access/access_model.rs index c57deccd..f6f0597e 100644 --- a/rust/routee-compass-core/src/model/access/access_model.rs +++ b/rust/routee-compass-core/src/model/access/access_model.rs @@ -5,7 +5,7 @@ use crate::model::{ traversal::state::state_variable::StateVar, }; -pub trait AccessModel { +pub trait AccessModel: Send + Sync { /// lists the state variables expected by this access model that are not /// defined on the base configuration. for example, if this access model /// has state variables that differ based on the query, they can be injected diff --git a/rust/routee-compass-core/src/model/access/access_model_error.rs b/rust/routee-compass-core/src/model/access/access_model_error.rs index 4c3b03ce..1787a77d 100644 --- a/rust/routee-compass-core/src/model/access/access_model_error.rs +++ b/rust/routee-compass-core/src/model/access/access_model_error.rs @@ -8,4 +8,6 @@ pub enum AccessModelError { StateError(#[from] StateError), #[error(transparent)] SerdeJsonError(#[from] serde_json::Error), + #[error("{0}")] + BuildError(String), } diff --git a/rust/routee-compass-core/src/model/access/default/combined_model.rs b/rust/routee-compass-core/src/model/access/default/combined_model.rs new file mode 100644 index 00000000..118bc62f --- /dev/null +++ b/rust/routee-compass-core/src/model/access/default/combined_model.rs @@ -0,0 +1,51 @@ +use crate::model::{ + access::{ + access_model::AccessModel, access_model_error::AccessModelError, + access_model_service::AccessModelService, + }, + property::{edge::Edge, vertex::Vertex}, + state::{state_feature::StateFeature, state_model::StateModel}, + traversal::state::state_variable::StateVar, +}; +use itertools::Itertools; +use std::sync::Arc; + +pub struct CombinedAccessModelService { + pub services: Vec>, +} + +pub struct CombinedAccessModel { + pub models: Vec>, +} + +impl AccessModelService for CombinedAccessModelService { + fn build(&self, query: &serde_json::Value) -> Result, AccessModelError> { + let models = self + .services + .iter() + .map(|m| m.build(query)) + .collect::>()?; + Ok(Arc::new(CombinedAccessModel { models })) + } +} + +impl AccessModel for CombinedAccessModel { + fn state_features(&self) -> Vec<(String, StateFeature)> { + self.models + .iter() + .flat_map(|m| m.state_features()) + .collect_vec() + } + + fn access_edge( + &self, + traversal: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), + state: &mut Vec, + state_model: &StateModel, + ) -> Result<(), AccessModelError> { + for model in self.models.iter() { + model.access_edge(traversal, state, state_model)?; + } + Ok(()) + } +} diff --git a/rust/routee-compass-core/src/model/access/default/mod.rs b/rust/routee-compass-core/src/model/access/default/mod.rs index 59cc04bf..ecebe944 100644 --- a/rust/routee-compass-core/src/model/access/default/mod.rs +++ b/rust/routee-compass-core/src/model/access/default/mod.rs @@ -1,2 +1,3 @@ +pub mod combined_model; pub mod no_access_model; pub mod turn_delays; diff --git a/rust/routee-compass/src/app/compass/config/access_model/combined_access_model_builder.rs b/rust/routee-compass/src/app/compass/config/access_model/combined_access_model_builder.rs new file mode 100644 index 00000000..83cbc4fc --- /dev/null +++ b/rust/routee-compass/src/app/compass/config/access_model/combined_access_model_builder.rs @@ -0,0 +1,49 @@ +use crate::app::compass::config::config_json_extension::ConfigJsonExtensions; +use itertools::Itertools; +use routee_compass_core::model::access::{ + access_model_builder::AccessModelBuilder, access_model_error::AccessModelError, + access_model_service::AccessModelService, default::combined_model::CombinedAccessModelService, +}; +use std::{collections::HashMap, rc::Rc, sync::Arc}; + +pub struct CombinedAccessModelBuilder { + pub builders: HashMap>, +} + +impl AccessModelBuilder for CombinedAccessModelBuilder { + fn build( + &self, + parameters: &serde_json::Value, + ) -> Result, AccessModelError> { + let model_params = parameters + .get_config_array(&"access_models", &"combined") + .map_err(|e| { + AccessModelError::BuildError(format!( + "unable to decode combined.access_models: {}", + e + )) + })?; + let services = model_params + .iter() + .map(|params| { + let model_type = params + .get_config_string(&"type", &"combined.access_models") + .map_err(|e| { + AccessModelError::BuildError(format!( + "unable to find 'type' of combined.access_model listing: {}", + e + )) + })?; + let builder = self.builders.get(&model_type).ok_or_else(|| { + let alts = self.builders.keys().join(","); + AccessModelError::BuildError(format!( + "unregistered access model {}, should be one of: {{{}}}", + model_type, alts + )) + })?; + builder.build(params) + }) + .collect::>()?; + Ok(Arc::new(CombinedAccessModelService { services })) + } +} diff --git a/rust/routee-compass/src/app/compass/config/access_model/mod.rs b/rust/routee-compass/src/app/compass/config/access_model/mod.rs index 2e16f609..35c9d4b9 100644 --- a/rust/routee-compass/src/app/compass/config/access_model/mod.rs +++ b/rust/routee-compass/src/app/compass/config/access_model/mod.rs @@ -1 +1,2 @@ +pub mod combined_access_model_builder; pub mod turn_delay_access_model_builder; From 593405aaa3a1b66fda7a1d8638ccb06d019f9374 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 11:09:32 -0600 Subject: [PATCH 09/17] add combined option to default builders --- .../src/app/compass/config/compass_app_builder.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs index 12c1c455..17d1a691 100644 --- a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs +++ b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs @@ -1,5 +1,8 @@ use super::{ - access_model::turn_delay_access_model_builder::TurnDelayAccessModelBuilder, + access_model::{ + combined_access_model_builder::CombinedAccessModelBuilder, + turn_delay_access_model_builder::TurnDelayAccessModelBuilder, + }, builders::{InputPluginBuilder, OutputPluginBuilder}, compass_configuration_error::CompassConfigurationError, compass_configuration_field::CompassConfigurationField, @@ -143,9 +146,16 @@ impl CompassAppBuilder { // Access model builders let no_access_model: Rc = Rc::new(NoAccessModel {}); let turn_delay: Rc = Rc::new(TurnDelayAccessModelBuilder {}); + let combined_am: Rc = Rc::new(CombinedAccessModelBuilder { + builders: HashMap::from([ + (String::from("no_access_model"), no_access_model.clone()), + (String::from("turn_delay"), turn_delay.clone()), + ]), + }); let am_builders: HashMap> = HashMap::from([ (String::from("no_access_model"), no_access_model), (String::from("turn_delay"), turn_delay), + (String::from("combined"), combined_am), ]); // Frontier model builders From 7f385806136fecf918615ec7aa901c09e327d8c2 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 11:18:38 -0600 Subject: [PATCH 10/17] clippy --- rust/routee-compass-core/src/model/cost/cost_model.rs | 2 +- .../config/access_model/turn_delay_access_model_builder.rs | 2 +- rust/routee-compass/src/app/search/search_app.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rust/routee-compass-core/src/model/cost/cost_model.rs b/rust/routee-compass-core/src/model/cost/cost_model.rs index 3a0bb33e..44bfd6c4 100644 --- a/rust/routee-compass-core/src/model/cost/cost_model.rs +++ b/rust/routee-compass-core/src/model/cost/cost_model.rs @@ -25,7 +25,7 @@ pub struct CostModel { impl CostModel { const VEHICLE_RATES: &'static str = "vehicle_rates"; const NETWORK_RATES: &'static str = "network_rates"; - const FEATURES: &'static str = "features"; + // const FEATURES: &'static str = "features"; const WEIGHTS: &'static str = "weights"; const VEHICLE_RATE: &'static str = "vehicle_rate"; const NETWORK_RATE: &'static str = "network_rate"; diff --git a/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs b/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs index 1beaefe5..91494b14 100644 --- a/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs +++ b/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs @@ -10,7 +10,7 @@ pub struct TurnDelayAccessModelBuilder {} impl AccessModelBuilder for TurnDelayAccessModelBuilder { fn build( &self, - parameters: &serde_json::Value, + _parameters: &serde_json::Value, ) -> Result, AccessModelError> { todo!() } diff --git a/rust/routee-compass/src/app/search/search_app.rs b/rust/routee-compass/src/app/search/search_app.rs index 61d6e7ac..76a19f77 100644 --- a/rust/routee-compass/src/app/search/search_app.rs +++ b/rust/routee-compass/src/app/search/search_app.rs @@ -37,6 +37,7 @@ pub struct SearchApp { impl SearchApp { /// builds a new SearchApp from the required components. /// handles all of the specialized boxing that allows for simple parallelization. + #[allow(clippy::too_many_arguments)] pub fn new( search_algorithm: SearchAlgorithm, graph: Graph, From 663faa8e800fc1e40a00465fbe5674ab74884410 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 11:25:34 -0600 Subject: [PATCH 11/17] fix tests --- .../src/routee/energy_model_service.rs | 18 ------------------ .../src/routee/energy_traversal_model.rs | 1 - .../app/compass/config/compass_app_builder.rs | 4 ++-- .../traversal_model/energy_model_builder.rs | 5 ----- .../compass/test/speeds_test/speeds_debug.toml | 3 +++ .../compass/test/speeds_test/speeds_test.toml | 3 +++ 6 files changed, 8 insertions(+), 26 deletions(-) diff --git a/rust/routee-compass-powertrain/src/routee/energy_model_service.rs b/rust/routee-compass-powertrain/src/routee/energy_model_service.rs index 7963b640..5153c113 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_model_service.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_model_service.rs @@ -1,6 +1,5 @@ use super::energy_traversal_model::EnergyTraversalModel; use super::vehicle::VehicleType; -use routee_compass_core::model::access::default::turn_delays::edge_heading::EdgeHeading; use routee_compass_core::model::traversal::traversal_model::TraversalModel; use routee_compass_core::model::traversal::traversal_model_error::TraversalModelError; use routee_compass_core::model::traversal::traversal_model_service::TraversalModelService; @@ -20,7 +19,6 @@ pub struct EnergyModelService { pub time_unit: TimeUnit, pub distance_unit: DistanceUnit, pub vehicle_library: HashMap>, - pub headings_table: Arc>>, } impl EnergyModelService { @@ -33,7 +31,6 @@ impl EnergyModelService { output_time_unit_option: Option, output_distance_unit_option: Option, vehicle_library: HashMap>, - headings_table_path: &Option

, ) -> Result { let output_time_unit = output_time_unit_option.unwrap_or(BASE_TIME_UNIT); let output_distance_unit = output_distance_unit_option.unwrap_or(BASE_DISTANCE_UNIT); @@ -47,20 +44,6 @@ impl EnergyModelService { None => Arc::new(None), }; - let headings_table: Arc>> = match headings_table_path { - Some(headings_path) => { - let headings_table: Box<[EdgeHeading]> = - read_utils::from_csv(headings_path, true, None).map_err(|e| { - TraversalModelError::FileReadError( - headings_path.as_ref().to_path_buf(), - e.to_string(), - ) - })?; - Arc::new(Some(headings_table)) - } - None => Arc::new(None), - }; - Ok(EnergyModelService { time_model_service, time_model_speed_unit, @@ -69,7 +52,6 @@ impl EnergyModelService { time_unit: output_time_unit, distance_unit: output_distance_unit, vehicle_library, - headings_table, }) } } diff --git a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs index ae07a39c..831435c9 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs @@ -260,7 +260,6 @@ mod tests { None, None, model_library, - &Some(heading_file_path), ) .unwrap(); let arc_service = Arc::new(service); diff --git a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs index 17d1a691..e542aa7b 100644 --- a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs +++ b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs @@ -290,7 +290,7 @@ impl CompassAppBuilder { let mut plugins: Vec> = Vec::new(); for plugin_json in input_plugins.into_iter() { - let plugin_type = config.get_config_string(&"type", &"input_plugin")?; + let plugin_type = plugin_json.get_config_string(&"type", &"input_plugin")?; let builder = self .input_plugin_builders .get(&plugin_type) @@ -318,7 +318,7 @@ impl CompassAppBuilder { let mut plugins: Vec> = Vec::new(); for plugin_json in output_plugins.into_iter() { - let plugin_type = config.get_config_string(&"type", &"output_plugin")?; + let plugin_type = plugin_json.get_config_string(&"type", &"output_plugin")?; let builder = self .output_plugin_builders .get(&plugin_type) diff --git a/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_builder.rs b/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_builder.rs index e436f1fc..924d08d7 100644 --- a/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_builder.rs +++ b/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_builder.rs @@ -84,10 +84,6 @@ impl TraversalModelBuilder for EnergyModelBuilder { .get_config_serde_optional::(&"distance_unit", &parent_key) .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let headings_table_path = params - .get_config_path_optional(&"headings_table_input_file", &parent_key) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let service = EnergyModelService::new( time_model_service, time_model_speed_unit, @@ -96,7 +92,6 @@ impl TraversalModelBuilder for EnergyModelBuilder { time_unit_option, distance_unit_option, vehicle_library, - &headings_table_path, )?; Ok(Arc::new(service)) diff --git a/rust/routee-compass/src/app/compass/test/speeds_test/speeds_debug.toml b/rust/routee-compass/src/app/compass/test/speeds_test/speeds_debug.toml index 1a19c1a1..3c4f200b 100644 --- a/rust/routee-compass/src/app/compass/test/speeds_test/speeds_debug.toml +++ b/rust/routee-compass/src/app/compass/test/speeds_test/speeds_debug.toml @@ -13,6 +13,9 @@ speed_table_input_file = "routee-compass/src/app/compass/test/speeds_test/test_e speed_unit = "kilometers_per_hour" output_time_unit = "hours" +[access] +type = "no_access_model" + [cost] cost_aggregation = "sum" [cost.state_variable_coefficients] diff --git a/rust/routee-compass/src/app/compass/test/speeds_test/speeds_test.toml b/rust/routee-compass/src/app/compass/test/speeds_test/speeds_test.toml index 290ea493..458c6413 100644 --- a/rust/routee-compass/src/app/compass/test/speeds_test/speeds_test.toml +++ b/rust/routee-compass/src/app/compass/test/speeds_test/speeds_test.toml @@ -13,6 +13,9 @@ speed_table_input_file = "src/app/compass/test/speeds_test/test_edge_speeds.csv" speed_unit = "kilometers_per_hour" output_time_unit = "hours" +[access] +type = "no_access_model" + [cost] cost_aggregation = "sum" [cost.state_variable_coefficients] From 63e426d2276395a4a139cd58a6998a80086fdf5d Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 11:41:05 -0600 Subject: [PATCH 12/17] build turn delay model --- .../default/turn_delays/turn_delay_model.rs | 4 ++ .../turn_delay_access_model_builder.rs | 63 ++++++++++++++++--- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs index 1c9eb2b4..3c54f61a 100644 --- a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use super::turn::Turn; use crate::model::{ access::access_model_error::AccessModelError, @@ -5,6 +7,8 @@ use crate::model::{ }; use std::collections::HashMap; +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "snake_case", tag = "type")] pub enum TurnDelayModel { /// use a mapping heuristic from turn ranges to time delays TabularDiscrete { diff --git a/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs b/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs index 91494b14..71a36d55 100644 --- a/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs +++ b/rust/routee-compass/src/app/compass/config/access_model/turn_delay_access_model_builder.rs @@ -1,17 +1,66 @@ -use std::sync::Arc; - -use routee_compass_core::model::access::{ - access_model_builder::AccessModelBuilder, access_model_error::AccessModelError, - access_model_service::AccessModelService, +use crate::app::compass::config::config_json_extension::ConfigJsonExtensions; +use routee_compass_core::{ + model::access::{ + access_model_builder::AccessModelBuilder, + access_model_error::AccessModelError, + access_model_service::AccessModelService, + default::turn_delays::{ + edge_heading::EdgeHeading, turn_delay_access_model_engine::TurnDelayAccessModelEngine, + turn_delay_access_model_service::TurnDelayAccessModelService, + turn_delay_model::TurnDelayModel, + }, + }, + util::fs::read_utils, }; +use std::sync::Arc; pub struct TurnDelayAccessModelBuilder {} impl AccessModelBuilder for TurnDelayAccessModelBuilder { fn build( &self, - _parameters: &serde_json::Value, + parameters: &serde_json::Value, ) -> Result, AccessModelError> { - todo!() + let file_path = parameters + .get_config_path(&"edge_heading_input_file", &"turn delay access model") + .map_err(|e| { + AccessModelError::BuildError(format!( + "failure reading 'edge_heading_input_file' from access model configuration: {}", + e + )) + })?; + let edge_headings = read_utils::from_csv::(&file_path.as_path(), true, None) + .map_err(|e| { + AccessModelError::BuildError(format!( + "error reading headings from file {:?}: {}", + file_path, e + )) + })?; + let turn_delay_model = parameters + .get_config_serde::(&"turn_delay_model", &"turn delay access model") + .map_err(|e| { + AccessModelError::BuildError(format!( + "failure reading 'turn_delay_model' from access model configuration: {}", + e + )) + })?; + let time_feature_name = parameters + .get_config_serde_optional::(&"time_feature_name", &"turn delay access model") + .map_err(|e| { + AccessModelError::BuildError(format!( + "failure reading 'time_unit' from access model configuration: {}", + e + )) + })? + .unwrap_or_else(|| String::from("time")); + let engine = TurnDelayAccessModelEngine { + edge_headings, + turn_delay_model, + time_feature_name, + }; + let service = TurnDelayAccessModelService { + engine: Arc::new(engine), + }; + Ok(Arc::new(service)) } } From f956fce389f390b54e3a87b25602701fd1dcb6ae Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 12:29:05 -0600 Subject: [PATCH 13/17] turn delay access model --- .../default/turn_delays/edge_heading.rs | 22 ++++----- .../model/access/default/turn_delays/turn.rs | 2 +- .../turn_delays/turn_delay_access_model.rs | 26 +---------- .../turn_delay_access_model_engine.rs | 45 +++++++++++++++++++ .../default/turn_delays/turn_delay_model.rs | 36 ++++++--------- 5 files changed, 73 insertions(+), 58 deletions(-) diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/edge_heading.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/edge_heading.rs index ce97a611..a172f46e 100644 --- a/rust/routee-compass-core/src/model/access/default/turn_delays/edge_heading.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/edge_heading.rs @@ -5,34 +5,34 @@ use serde::Deserialize; /// if the start and end have the same heading, the edge heading is None. #[derive(Copy, Clone, Deserialize)] pub struct EdgeHeading { - start_angle: i16, - end_angle: Option, + arrival_heading: i16, + departure_heading: Option, } impl EdgeHeading { /// creates an EdgeHeading from a start and end heading - pub fn new(start_angle: i16, end_angle: i16) -> Self { + pub fn new(arrival_heading: i16, departure_heading: i16) -> Self { Self { - start_angle, - end_angle: Some(end_angle), + arrival_heading, + departure_heading: Some(departure_heading), } } /// retrieve the start - pub fn start_angle(&self) -> i16 { - self.start_angle + pub fn start_heading(&self) -> i16 { + self.arrival_heading } /// If the end heading is not specified, it is assumed to be the same as the start heading - pub fn end_angle(&self) -> i16 { - match self.end_angle { + pub fn end_heading(&self) -> i16 { + match self.departure_heading { Some(end_heading) => end_heading, - None => self.start_angle, + None => self.arrival_heading, } } /// Compute the angle between this edge and some destination edge. pub fn bearing_to_destination(&self, destination: &EdgeHeading) -> i16 { - let angle = destination.start_angle() - self.end_angle(); + let angle = destination.start_heading() - self.end_heading(); if angle > 180 { angle - 360 } else if angle < -180 { diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn.rs index 17e56417..41bc7c35 100644 --- a/rust/routee-compass-core/src/model/access/default/turn_delays/turn.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn.rs @@ -2,7 +2,7 @@ use crate::model::access::access_model_error::AccessModelError; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Eq, Hash)] -#[serde(rename = "snake_case")] +#[serde(rename_all = "snake_case")] pub enum Turn { NoTurn, SlightRight, diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model.rs index 7ddc6309..a019c2ab 100644 --- a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model.rs @@ -1,10 +1,7 @@ -use super::{ - edge_heading::EdgeHeading, turn_delay_access_model_engine::TurnDelayAccessModelEngine, -}; +use super::turn_delay_access_model_engine::TurnDelayAccessModelEngine; use crate::model::{ access::{access_model::AccessModel, access_model_error::AccessModelError}, property::{edge::Edge, vertex::Vertex}, - road_network::edge_id::EdgeId, state::{state_feature::StateFeature, state_model::StateModel}, traversal::state::state_variable::StateVar, }; @@ -21,11 +18,7 @@ impl AccessModel for TurnDelayAccessModel { state: &mut Vec, state_model: &StateModel, ) -> Result<(), AccessModelError> { - let (_v1, src, _v2, dst, _v3) = traversal; - let src_heading = get_headings(&self.engine.edge_headings, src.edge_id)?; - let dst_heading = get_headings(&self.engine.edge_headings, dst.edge_id)?; - let angle = src_heading.bearing_to_destination(&dst_heading); - let (delay, delay_unit) = self.engine.turn_delay_model.get_delay(angle)?; + let (delay, delay_unit) = self.engine.get_delay(traversal)?; state_model.add_time(state, &self.engine.time_feature_name, &delay, delay_unit)?; Ok(()) } @@ -34,18 +27,3 @@ impl AccessModel for TurnDelayAccessModel { vec![] } } - -/// lookup up the edge heading from the headings table -pub fn get_headings( - headings_table: &[EdgeHeading], - edge_id: EdgeId, -) -> Result { - let heading: &EdgeHeading = - headings_table - .get(edge_id.as_usize()) - .ok_or_else(|| AccessModelError::RuntimeError { - name: String::from("turn delay access model"), - error: format!("missing edge id {} ", edge_id), - })?; - Ok(*heading) -} diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_engine.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_engine.rs index 0ca0641a..519a2466 100644 --- a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_engine.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_access_model_engine.rs @@ -1,4 +1,11 @@ +use crate::model::access::access_model_error::AccessModelError; +use crate::model::property::edge::Edge; +use crate::model::property::vertex::Vertex; +use crate::model::road_network::edge_id::EdgeId; +use crate::model::unit::{Time, TimeUnit}; + use super::edge_heading::EdgeHeading; +use super::turn::Turn; use super::turn_delay_model::TurnDelayModel; pub struct TurnDelayAccessModelEngine { @@ -6,3 +13,41 @@ pub struct TurnDelayAccessModelEngine { pub turn_delay_model: TurnDelayModel, pub time_feature_name: String, } + +impl TurnDelayAccessModelEngine { + pub fn get_delay<'a>( + &'a self, + traversal: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), + ) -> Result<(Time, &'a TimeUnit), AccessModelError> { + let (_v1, src, _v2, dst, _v3) = traversal; + let src_heading = get_headings(&self.edge_headings, src.edge_id)?; + let dst_heading = get_headings(&self.edge_headings, dst.edge_id)?; + let angle = src_heading.bearing_to_destination(&dst_heading); + match &self.turn_delay_model { + TurnDelayModel::TabularDiscrete { table, time_unit } => { + let turn = Turn::from_angle(angle)?; + let delay = table.get(&turn).ok_or_else(|| { + let name = String::from("tabular discrete turn delay model"); + let error = format!("table missing entry for turn {}", turn.to_string()); + AccessModelError::RuntimeError { name, error } + })?; + Ok((*delay, &time_unit)) + } // TurnDelayModel::TabularDiscreteWithRoadClasses { table, time_unit } => {} + } + } +} + +/// lookup up the edge heading from the headings table +pub fn get_headings( + headings_table: &[EdgeHeading], + edge_id: EdgeId, +) -> Result { + let heading: &EdgeHeading = + headings_table + .get(edge_id.as_usize()) + .ok_or_else(|| AccessModelError::RuntimeError { + name: String::from("turn delay access model"), + error: format!("missing edge id {} ", edge_id), + })?; + Ok(*heading) +} diff --git a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs index 3c54f61a..29b5899e 100644 --- a/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs +++ b/rust/routee-compass-core/src/model/access/default/turn_delays/turn_delay_model.rs @@ -1,10 +1,6 @@ -use serde::{Deserialize, Serialize}; - use super::turn::Turn; -use crate::model::{ - access::access_model_error::AccessModelError, - unit::{Time, TimeUnit}, -}; +use crate::model::unit::{Time, TimeUnit}; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Serialize, Deserialize)] @@ -15,20 +11,16 @@ pub enum TurnDelayModel { table: HashMap, time_unit: TimeUnit, }, -} - -impl TurnDelayModel { - pub fn get_delay(&self, angle: i16) -> Result<(Time, &TimeUnit), AccessModelError> { - match self { - TurnDelayModel::TabularDiscrete { table, time_unit } => { - let turn = Turn::from_angle(angle)?; - let delay = table.get(&turn).ok_or_else(|| { - let name = String::from("tabular discrete turn delay model"); - let error = format!("table missing entry for turn {}", turn.to_string()); - AccessModelError::RuntimeError { name, error } - })?; - Ok((*delay, time_unit)) - } - } - } + // /// use a mapping heuristic from turn ranges and road class transitions + // /// to time delays + // /// TODO: + // /// - first, move ConfigJsonExtension to core crate + // /// - then we can write a TurnDelayModel::new(&serde_json::Value) method which can + // /// use the JSON extension methods + // /// + // TabularDiscreteWithRoadClasses { + // table: HashMap<(Turn, u8, u8), Time>, + // road_classes: Box<[u8]>, + // time_unit: TimeUnit, + // }, } From 39cdd9fdca99b63886a0d46a60b3ab5572a7bfcc Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 12:38:07 -0600 Subject: [PATCH 14/17] time traversal features --- .../default/speed_traversal_model.rs | 19 +++++++++++++++++-- .../src/routee/energy_traversal_model.rs | 4 +++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/rust/routee-compass-core/src/model/traversal/default/speed_traversal_model.rs b/rust/routee-compass-core/src/model/traversal/default/speed_traversal_model.rs index 25f7d7e8..d7033b0d 100644 --- a/rust/routee-compass-core/src/model/traversal/default/speed_traversal_model.rs +++ b/rust/routee-compass-core/src/model/traversal/default/speed_traversal_model.rs @@ -71,9 +71,24 @@ impl TraversalModel for SpeedTraversalModel { Ok(()) } - /// no additional state features are needed + /// track the time state feature fn state_features(&self) -> Vec<(String, StateFeature)> { - vec![] + vec![ + ( + String::from("time"), + StateFeature::Time { + time_unit: self.engine.time_unit, + initial: Time::ZERO, + }, + ), + ( + String::from("distance"), + StateFeature::Distance { + distance_unit: self.engine.distance_unit, + initial: Distance::ZERO, + }, + ), + ] } } diff --git a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs index 831435c9..179ffed5 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs @@ -21,7 +21,9 @@ pub struct EnergyTraversalModel { impl TraversalModel for EnergyTraversalModel { /// inject the state features required by the VehicleType fn state_features(&self) -> Vec<(String, StateFeature)> { - self.vehicle.state_features() + let mut features = self.vehicle.state_features(); + features.extend(self.time_model.state_features()); + features } fn traverse_edge( From 5e358814d156ddde810b3da9ad0695a4a7bb7eae Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 12:44:26 -0600 Subject: [PATCH 15/17] clippy --- .../src/routee/energy_traversal_model.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs index 179ffed5..539b95f2 100644 --- a/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs @@ -186,11 +186,6 @@ mod tests { .join("routee") .join("test") .join("grades.txt"); - let heading_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("src") - .join("routee") - .join("test") - .join("headings.csv"); let model_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("src") .join("routee") From f049e14030b75bddd8316f4536f42c7acda4d902 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 12:50:12 -0600 Subject: [PATCH 16/17] cleanup comments --- .../src/model/traversal/traversal_model.rs | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/rust/routee-compass-core/src/model/traversal/traversal_model.rs b/rust/routee-compass-core/src/model/traversal/traversal_model.rs index f05d2c39..53547170 100644 --- a/rust/routee-compass-core/src/model/traversal/traversal_model.rs +++ b/rust/routee-compass-core/src/model/traversal/traversal_model.rs @@ -38,33 +38,6 @@ pub trait TraversalModel: Send + Sync { state_model: &StateModel, ) -> Result<(), TraversalModelError>; - // /// Updates the traversal state by accessing some destination edge - // /// when coming from some previous edge. - // /// - // /// These arguments appear in the network as: - // /// `(v1) -[prev]-> (v2) -[next]-> (v3)` - // /// Where `next` is the edge we want to access. - // /// - // /// # Arguments - // /// - // /// * `v1` - src of previous edge - // /// * `src` - previous edge - // /// * `v2` - src vertex of the next edge - // /// * `dst` - edge we are determining the cost to access - // /// * `v3` - dst vertex of the next edge - // /// * `state` - state of the search at the beginning of the dst edge - // /// - // /// # Returns - // /// - // /// Either an optional access result or an error. if there are no - // /// state updates due to access, None is returned. - // fn access_edge( - // &self, - // trajectory: (&Vertex, &Edge, &Vertex, &Edge, &Vertex), - // state: &mut Vec, - // state_model: &StateModel, - // ) -> Result<(), TraversalModelError>; - /// Estimates the traversal state by traversing between two vertices without /// performing any graph traversals. /// From 4f8c3c6da61474b2bd80907cc8135cafcd4d0993 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Fri, 22 Mar 2024 16:15:11 -0600 Subject: [PATCH 17/17] rename heading columns, file suffix --- python/nrel/routee/compass/io/generate_dataset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/nrel/routee/compass/io/generate_dataset.py b/python/nrel/routee/compass/io/generate_dataset.py index da0be85f..938a46be 100644 --- a/python/nrel/routee/compass/io/generate_dataset.py +++ b/python/nrel/routee/compass/io/generate_dataset.py @@ -155,14 +155,14 @@ def replace_id(vertex_uuid): ) headings = e.bearing.fillna(0).apply(lambda x: int(round(x))) - headings_df = headings.to_frame(name="start_heading") + headings_df = headings.to_frame(name="arrival_heading") # We could get more sophisticated and compute the end heading # for links that might have some significant curvature, but # for now we'll just use the start heading. - headings_df["end_heading"] = None + headings_df["destination_heading"] = None headings_df.to_csv( - output_directory / "edges-headings-enumerated.txt.gz", + output_directory / "edges-headings-enumerated.csv.gz", index=False, compression="gzip", )