use ::rand::{rngs::StdRng};
use crate::match_object_panic;
use crate::config_parser::ConfigurationValue;
use crate::routing::prelude::*;
use crate::topology::{Topology,NeighbourRouterIteratorItem,Location};
use crate::matrix::Matrix;
#[derive(Debug)]
pub struct UpDown
{
}
impl Routing for UpDown
{
fn next(&self, _routing_info:&RoutingInfo, topology:&dyn Topology, current_router:usize, target_router: usize, target_server:Option<usize>, num_virtual_channels:usize, _rng: &mut StdRng) -> Result<RoutingNextCandidates,Error>
{
let (up_distance, down_distance) = topology.up_down_distance(current_router,target_router).unwrap_or_else(||panic!("The topology does not provide an up/down path from {} to {}",current_router,target_router));
if up_distance + down_distance == 0
{
let target_server = target_server.expect("target server was not given.");
for i in 0..topology.ports(current_router)
{
if let (Location::ServerPort(server),_link_class)=topology.neighbour(current_router,i)
{
if server==target_server
{
return Ok(RoutingNextCandidates{candidates:(0..num_virtual_channels).map(|vc|CandidateEgress::new(i,vc)).collect(),idempotent:true});
}
}
}
unreachable!();
}
let num_ports=topology.ports(current_router);
let mut r=Vec::with_capacity(num_ports*num_virtual_channels);
for i in 0..num_ports
{
if let (Location::RouterPort{router_index,router_port:_},_link_class)=topology.neighbour(current_router,i)
{
if let Some((new_u, new_d)) = topology.up_down_distance(router_index,target_router)
{
if (new_u<up_distance && new_d<=down_distance) || (new_u<=up_distance && new_d<down_distance)
{
r.extend((0..num_virtual_channels).map(|vc|CandidateEgress::new(i,vc)));
}
}
}
}
Ok(RoutingNextCandidates{candidates:r,idempotent:true})
}
}
impl UpDown
{
pub fn new(arg: RoutingBuilderArgument) -> UpDown
{
match_object_panic!(arg.cv,"UpDown",_value);
UpDown{
}
}
}
#[derive(Debug)]
pub struct ExplicitUpDown
{
pub root: Option<usize>,
pub up_down_distances: Matrix<Option<u8>>,
pub down_distances: Matrix<Option<u8>>,
pub distance_to_root: Vec<u8>,
pub branch_crossings_downwards: bool,
pub branch_crossings_upwards: bool,
pub label_up: i32,
pub label_down: i32,
pub label_horizontal_vec: Vec<i32>,
pub label_horizontal_otherwise: i32,
}
impl Routing for ExplicitUpDown
{
fn next(&self, _routing_info:&RoutingInfo, topology:&dyn Topology, current_router:usize, target_router: usize, target_server:Option<usize>, num_virtual_channels:usize, _rng: &mut StdRng) -> Result<RoutingNextCandidates,Error>
{
if current_router == target_router
{
let target_server = target_server.expect("target server was not given.");
for i in 0..topology.ports(current_router)
{
if let (Location::ServerPort(server),_link_class)=topology.neighbour(current_router,i)
{
if server==target_server
{
return Ok(RoutingNextCandidates{candidates:(0..num_virtual_channels).map(|vc|CandidateEgress::new(i,vc)).collect(),idempotent:true});
}
}
}
unreachable!();
}
let up_down_distance = self.up_down_distances.get(current_router,target_router).unwrap_or_else(||panic!("Missing up/down path from {} to {}",current_router,target_router));
let down_distance = self.down_distances.get(current_router,target_router);
let num_ports=topology.ports(current_router);
let mut r=Vec::with_capacity(num_ports*num_virtual_channels);
for i in 0..num_ports
{
if let (Location::RouterPort{router_index,router_port:_},_link_class)=topology.neighbour(current_router,i)
{
let mut label = 0i32;
let mut new_hops = 0usize;
let good = if let &Some(down_distance) = down_distance {
let mut good = if let &Some(new_down) = self.down_distances.get(router_index,target_router) {
label = self.label_down;
new_hops = new_down.into();
new_down < down_distance
} else {
false
};
if !good && self.branch_crossings_downwards && self.distance_to_root[router_index]==self.distance_to_root[current_router] {
if let &Some(new_up_down) = self.up_down_distances.get(router_index,target_router)
{
if new_up_down < down_distance
{
good = true;
new_hops = new_up_down.into();
let delta = (down_distance-1-new_up_down) as usize;
if let Some(&x) = self.label_horizontal_vec.get(delta) {
label = x;
} else {
label = self.label_horizontal_otherwise;
}
}
}
}
good
} else {
if let &Some(new_up_down) = self.up_down_distances.get(router_index,target_router)
{
if new_up_down < up_down_distance {
label = self.label_up;
new_hops = new_up_down.into();
let mut good = self.distance_to_root[router_index]<self.distance_to_root[current_router];
if !good && self.branch_crossings_upwards && self.distance_to_root[router_index]==self.distance_to_root[current_router] {
good = true;
let delta = (up_down_distance-1-new_up_down) as usize;
if let Some(&x) = self.label_horizontal_vec.get(delta) {
label = x;
} else {
label = self.label_horizontal_otherwise;
}
}
good
} else { false }
} else {
false
}
};
if good{
r.extend((0..num_virtual_channels).map(|vc|{
let mut cand = CandidateEgress::new(i,vc);
cand.label = label;
cand.estimated_remaining_hops = Some(1+new_hops);
cand
}));
}
}
}
Ok(RoutingNextCandidates{candidates:r,idempotent:true})
}
fn initialize(&mut self, topology:&dyn Topology, _rng: &mut StdRng)
{
let n = topology.num_routers();
if let Some(root) = self.root
{
self.up_down_distances = Matrix::constant(None,n,n);
self.down_distances = Matrix::constant(None,n,n);
let mut distance_to_root=vec![None;n];
distance_to_root[root]=Some(0);
let mut downwards = Vec::with_capacity(n);
let mut read_index = 0;
downwards.push(root);
while read_index < downwards.len()
{
let current = downwards[read_index];
read_index+=1;
if let Some(current_distance) = distance_to_root[current]
{
let alternate_distance = current_distance + 1;
for NeighbourRouterIteratorItem{neighbour_router:neighbour,..} in topology.neighbour_router_iter(current)
{
if distance_to_root[neighbour].is_none()
{
distance_to_root[neighbour]=Some(alternate_distance);
downwards.push(neighbour);
}
}
}
}
self.distance_to_root = distance_to_root.into_iter().map(|d|d.unwrap()).collect();
for origin in 0..n
{
let origin_to_root = self.distance_to_root[origin];
for target in 0..n
{
let target_to_root = self.distance_to_root[target];
*self.up_down_distances.get_mut(origin,target) = Some(origin_to_root+target_to_root);
}
*self.down_distances.get_mut(root,origin) = Some(origin_to_root);
}
for origin in 0..n
{
*self.up_down_distances.get_mut(origin,origin) = Some(0);
*self.down_distances.get_mut(origin,origin) = Some(0);
}
for (low_index,&low) in downwards.iter().enumerate()
{
for &high in downwards[0..low_index].iter()
{
for NeighbourRouterIteratorItem{neighbour_router:neighbour,..} in topology.neighbour_router_iter(low)
{
if self.distance_to_root[neighbour]+1==self.distance_to_root[low]
{
let neighbour_up_down = self.up_down_distances.get(neighbour,high).unwrap();
let origin_up_down = self.up_down_distances.get(low,high).unwrap();
if neighbour_up_down+1 < origin_up_down
{
*self.up_down_distances.get_mut(low,high) = Some(neighbour_up_down+1);
*self.up_down_distances.get_mut(high,low) = Some(neighbour_up_down+1);
}
if let Some(neighbour_down) = self.down_distances.get(high,neighbour)
{
if self.down_distances.get(high,low).map(|origin_down|neighbour_down+1<origin_down).unwrap_or(true)
{
*self.down_distances.get_mut(high,low) = Some(neighbour_down+1);
}
}
}
}
}
}
}
if n!=self.up_down_distances.get_columns()
{
panic!("ExplicitUpDown has not being properly initialized");
}
}
}
impl ExplicitUpDown
{
pub fn new(arg: RoutingBuilderArgument) -> ExplicitUpDown
{
let mut root = None;
let mut branch_crossings_downwards = false;
let mut branch_crossings_upwards = false;
let mut label_down = 0i32;
let mut label_up = 0i32;
let mut label_horizontal_vec = vec![];
let mut label_horizontal_otherwise = 0i32;
match_object_panic!(arg.cv,"UpDownStar",value,
"root" => root=Some(value.as_f64().expect("bad value for root") as usize),
"branch_crossings" => {
branch_crossings_upwards = value.as_bool().expect("bad value for branch_crossings");
branch_crossings_downwards = branch_crossings_upwards;
},
"branch_crossings_upwards" => branch_crossings_upwards=value.as_bool().expect("bad value for branch_crossings_upwards"),
"branch_crossings_downwards" => branch_crossings_downwards=value.as_bool().expect("bad value for branch_crossings_downwards"),
"label_up" | "label_upwards" => label_up = value.as_i32().expect("bad value for label_up"),
"label_down" | "label_downwards" => label_down = value.as_i32().expect("bad value for label_down"),
"label_horizontal_vec" => label_horizontal_vec = value.as_array().expect("bad value for label_horizontal_vec").iter().map(|x|{
x.as_i32().expect("bad value for label_horizontal_vec entry")
}).collect(),
"label_horizontal_otherwise" => label_horizontal_otherwise = value.as_i32().expect("bad value for label_horizontal_otherwise"),
);
ExplicitUpDown{
root,
up_down_distances: Matrix::constant(None,0,0),
down_distances: Matrix::constant(None,0,0),
distance_to_root: Vec::new(),
branch_crossings_downwards,
branch_crossings_upwards,
label_down,
label_up,
label_horizontal_vec,
label_horizontal_otherwise,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Plugs;
use rand::SeedableRng;
use crate::topology::cartesian::Hamming;
#[test]
fn up_down_star()
{
let plugs = Plugs::default();
let uds_cv = ConfigurationValue::Object("UpDownStar".to_string(),vec![("root".to_string(),ConfigurationValue::Number(5.0))]);
let uds_arg = RoutingBuilderArgument{cv:&uds_cv,plugs:&plugs};
let mut uds = ExplicitUpDown::new(uds_arg);
let mut rng=StdRng::seed_from_u64(10u64);
let hamming_cv = ConfigurationValue::Object("Hamming".to_string(),vec![("sides".to_string(),ConfigurationValue::Array(vec![
ConfigurationValue::Number(8.0),
ConfigurationValue::Number(8.0),
])),("servers_per_router".to_string(),ConfigurationValue::Number(8.0))]);
let topology = Hamming::new(&hamming_cv);
uds.initialize(&topology,&mut rng);
let n = topology.num_routers();
for origin in 0..n
{
for destination in 0..n
{
let origin_ud = uds.up_down_distances.get(origin,destination).expect("missing an up/down distance");
let is_down = uds.down_distances.get(origin,destination).is_some();
let mut count_improvers = 0;
for NeighbourRouterIteratorItem{neighbour_router:neighbour,..} in topology.neighbour_router_iter(origin)
{
let neighbour_ud = uds.up_down_distances.get(neighbour,destination).expect("missing an up/down distance");
if neighbour_ud < origin_ud && (is_down || uds.distance_to_root[origin]==uds.distance_to_root[neighbour]+1) {
count_improvers +=1;
}
}
assert!(origin==destination || count_improvers>=1,"origin={} destination={} ud={}",origin,destination,origin_ud);
}
}
}
}