Started work on exercise 15.

This commit is contained in:
Filipe Rodrigues 2023-12-16 18:11:48 +00:00
parent ef2df1159c
commit e0617ec046
13 changed files with 673 additions and 14 deletions

View File

@ -20,6 +20,8 @@
"linetype",
"logseq",
"maxs",
"ncol",
"nrow",
"packetsize",
"pktnet",
"pnet",

View File

@ -25,22 +25,24 @@ Flows <- list(
kleinrock <- approx_kleinrock(LinkCapacities, Flows, packet_size)
str(kleinrock)
cat(sprintf("Kleinrock average: %.20f\n", kleinrock$total_wait))
cat(sprintf("Kleinrock average: %.20f\n", kleinrock$avg_packet_delay_network))
min_rate <- min(sapply(Flows, function(flow) flow$rate))
endTime <- 10000 * (1 / min_rate)
cat(sprintf("End time = %.2f\n", endTime))
set.seed(0)
pnet_results <- lapply(1:2, function(...) pnet(LinkCapacities, Flows, endTime))
pnet_avgs <- sapply(pnet_results, function(res) res$avg_flow_delays)
pnet_results <- lapply(1:10, function(...) pnet(LinkCapacities, Flows, endTime))
for (flow_idx in 1:4) {
pnet_avgs <- sapply(pnet_results, \(res) res$avg_flow_delays[[flow_idx]])
pnet_mean <- mean(pnet_avgs)
pnet_var <- var(pnet_avgs)
pnet_mean <- mean(pnet_avgs)
pnet_var <- var(pnet_avgs)
confidence <- 0.95
p <- 1 - (1 - confidence) / 2
t <- qt(p, length(pnet_results) - 1)
pnet_ci_min <- pnet_mean - t * sqrt(pnet_var / length(pnet_results))
pnet_ci_max <- pnet_mean + t * sqrt(pnet_var / length(pnet_results))
cat(sprintf("PNet average results: %.5f .. %.5f\n", pnet_ci_min, pnet_ci_max))
confidence <- 0.95
p <- 1 - (1 - confidence) / 2
t <- qt(p, length(pnet_results) - 1)
pnet_ci_min <- pnet_mean - t * sqrt(pnet_var / length(pnet_results))
pnet_ci_max <- pnet_mean + t * sqrt(pnet_var / length(pnet_results))
cat(sprintf("PNet average results: %.5f .. %.5f\n", pnet_ci_min, pnet_ci_max))
}

19
code/15c.R Normal file
View File

@ -0,0 +1,19 @@
source("code/kleinrock.R")
k <- 1000
link_capacity <- 256 * k
packet_size <- 1 * k
link_capacities <- replicate(7, link_capacity)
flows <- list(
list(rate = 171.5, packetsize = packet_size, route = c(1, 3, 6)),
list(rate = 43.5, packetsize = packet_size, route = c(1, 4, 7)),
list(rate = 64, packetsize = packet_size, route = c(2, 5)),
list(rate = 128, packetsize = packet_size, route = c(2, 5, 7)),
list(rate = 128, packetsize = packet_size, route = c(4))
)
kleinrock <- approx_kleinrock(link_capacities, flows, packet_size)
str(kleinrock)
cat(sprintf("Kleinrock average: %.20f\n", kleinrock$avg_packet_delay_network))

19
code/15d.R Normal file
View File

@ -0,0 +1,19 @@
source("code/kleinrock.R")
k <- 1000
link_capacity <- 256 * k
packet_size <- 1 * k
link_capacities <- replicate(7, link_capacity)
flows <- list(
list(rate = 170.168, packetsize = packet_size, route = c(1, 3, 6)),
list(rate = 172.832, packetsize = packet_size, route = c(2, 5, 7)),
list(rate = 39.218, packetsize = packet_size, route = c(1, 4)),
list(rate = 24.782, packetsize = packet_size, route = c(2, 5)),
list(rate = 128.0, packetsize = packet_size, route = c(4))
)
kleinrock <- approx_kleinrock(link_capacities, flows, packet_size)
str(kleinrock)
cat(sprintf("Kleinrock average: %.20f\n", kleinrock$avg_packet_delay_network))

116
code/bg/aux_bg.R Normal file
View File

@ -0,0 +1,116 @@
loadlinks = function(NumNodes,NumODPairs,Paths) {
#Loads links with rate contained in Paths list
LinkRates=matrix(rep(0,NumNodes^2),nrow=NumNodes) #Initializes LinkRates matrix with all zeros
for (i in 1:NumODPairs) { #Iterates over OD pairs
NumPaths=length(Paths[[i]]) #Number of paths in OD pair
for (j in 1:NumPaths) { #Iterates over paths of OD pair
ThisPath=Paths[[i]][[j]]$route #Current path
ThisRate=Paths[[i]][[j]]$rate #Current rate
for (k in 1:(length(ThisPath)-1)) { #Iterates over links of path
LinkOrigin=ThisPath[k] #Origin of link
LinkDestination=ThisPath[k+1] #Destination of link
#Loads link with rate of path
LinkRates[LinkOrigin,LinkDestination]=LinkRates[LinkOrigin,LinkDestination]+ThisRate
}
}
}
return(LinkRates)
}
bestpathinsert = function(ODPair,NumPaths,Paths,BestPath) {
#Inserts best path in Paths list for specific OD pair (if not already there) and determines index of
#best path within this list; if best path is new its rate is zero
BestPathIndex=0 #Initializes index of best path
for (j in 1:NumPaths) { #Iterates over paths
ThisPath=Paths[[ODPair]][[j]]$route #Extracts path from Paths list
if (length(BestPath)==length(ThisPath) && all(BestPath==ThisPath)) { #Best path is already in list
BestPathIndex=j #Sets index of best path
break
}
}
if (BestPathIndex==0){ #Best path is not in Paths list
NumPaths=NumPaths+1 #Increments number of path
BestPathIndex=NumPaths #Sets index of best path
Paths[[ODPair]][[BestPathIndex]]=list(route=BestPath,rate=0) #Inserts best path in Paths list
}
return(list(numroutes=NumPaths,bestrouteindex=BestPathIndex,paths=Paths))
}
pathlengths = function(ODPair,NumPaths,Paths,LinkFirstDerivativeCosts) {
#Determines first derivative path lengths of all paths belonging to given OD pair
PathFirstDerivativeLengths=vector(mode="numeric",length=NumPaths) #Initializes PathFirstDerivativeLengths vector
for (j in 1:NumPaths) { #Iterates over paths
ThisPath=Paths[[ODPair]][[j]]$route #Extracts path from Paths list
ThisLength=0 #Initializes path length
for (k in 1:(length(ThisPath)-1)) { #Iterates over links of path
LinkOrigin=ThisPath[k] #Origin of link
LinkDestination=ThisPath[k+1] #Destination of link
ThisLength=ThisLength+LinkFirstDerivativeCosts[LinkOrigin,LinkDestination] #Updates path length
}
PathFirstDerivativeLengths[j]=ThisLength #Stores path length in PathFirstDerivativeLengths vector
}
return(PathFirstDerivativeLengths)
}
deviaterate = function(ODPair,NumPaths,BestPathIndex,Paths,PathFirstDerivativeLengths,LinkSecondDerivativeCosts,Alpha) {
#Deviates rate between paths of given OD pair, knowing the best route
#Extracts MFDL and the path that owns it
MFDL=PathFirstDerivativeLengths[BestPathIndex] #Extracts MFDL
#print("BestIncDelayFirst");print(BestIncDelayFirst)
BestPath=Paths[[ODPair]][[BestPathIndex]]$route #Extracts path with MFDL
for (i in 1:NumPaths) { #Iterates over paths
if (BestPathIndex==i) { #Path is MFDL
#Do noting
} else { #Path is non-MFDL
ThisLength=PathFirstDerivativeLengths[i]
#print("ThisLength");print(ThisLength)
#Calculates Hp parameter
ThisPath=Paths[[ODPair]][[i]]$route #Extracts path from Paths list
NonRepeatedLinks=nonrepeatedlinks(ThisPath,BestPath) #Determines list of links not common to MFDL path and non-MFDL path
Hp=0 #Initializes Hp parameter
for (j in 1:length(NonRepeatedLinks)) { #Iterates over non-repeted links
LinkOrigin=NonRepeatedLinks[[j]][1] #Origin of link
LinkDestination=NonRepeatedLinks[[j]][2] #Destination of link
Hp=Hp+LinkSecondDerivativeCosts[LinkOrigin,LinkDestination] #Updates Hp parameter
}
#print("Hp");print(Hp)
#Calculates Delta parameter
Delta=Alpha*(ThisLength-MFDL)/Hp
#print("Delta");print(Delta)
#Deviates rate
PreviousRate=Paths[[ODPair]][[i]]$rate
NewRate=PreviousRate-Delta #Checks if new rate of non-MFDL path is non-negative
if (NewRate>=0) {
Paths[[ODPair]][[BestPathIndex]]$rate=Paths[[ODPair]][[BestPathIndex]]$rate+Delta #Adds Delta to MFDL path
Paths[[ODPair]][[i]]$rate=NewRate #Subtracts Delta from non-MFDL path
} else {
Paths[[ODPair]][[i]]$rate=0
Paths[[ODPair]][[BestPathIndex]]$rate=Paths[[ODPair]][[BestPathIndex]]$rate+PreviousRate
}
}
}
return(Paths)
}
nonrepeatedlinks = function(Path1,Path2) {
#Returns list with the links that are not common to Path1 and Path2
NonRepeatedLinks=list() #Initializes NonRepeatedLinks list
for (j in 1:(length(Path1)-1)) { #Iterates over links of Path1
NonRepeatedLinks[[j]]=Path1[j:(j+1)] #Loads NonRepeatedLinks list with links of Path1
}
for (j in 1:(length(Path2)-1)) { #Iterates over links of Path2
ThisLink=Path2[j:(j+1)] #Extracts link from Path2
RepeatedLink=FALSE #Set ThisLink as not repeated, initially
for (k in 1:length(NonRepeatedLinks)) { #Iterates over links already in NonRepeatedLinks list
if (identical(NonRepeatedLinks[[k]],ThisLink)) { #If ThisLink is already in NonRepeatedLinks list
RepeatedLink=TRUE #ThisLink is repeated
break #Stop searching
}
}
if (RepeatedLink==FALSE) { #If ThisLink is not repeated
NonRepeatedLinks[[length(NonRepeatedLinks)+1]]=ThisLink #Insert ThisLink in NonRepeatedLinks list
}
}
return(NonRepeatedLinks)
}

160
code/bg/bg.R Normal file
View File

@ -0,0 +1,160 @@
# This script determines the optimal flow bifurcations that minimize the network
# average packet delay, using the Bertsekas algorithm (section 5.7 of Data
# Networks book)
source("code/bg/parameters.R")
source("code/bg/shortestpath.R")
source("code/bg/aux_bg.R")
parameters() # Calls parameters routine
NumNodes <- ncol(LinkCapacities) # Number of nodes
NumLinks <- sum(LinkCapacities != 0) # Number of links
NumODPairs <- nrow(Flows) # Number of flows
# Alpha=NumLinks/NumNodes^2
Alpha <- 1 # Stepsize for flow deviation
DeltaCap <- 0.05 # Capacity adjusting factor, for phase I
ErrorPhase2 <- 10^(-8) # Convergence tolerance phase II
LinkRates <- matrix(rep(0, NumNodes^2), nrow = NumNodes) # Initializes LinkRates matrix with all zeros
LinkFirstDerivativeCosts <- 1 / LinkCapacities # Initial link costs with all link rates equal to zero
# print(LinkFirstDerivativeCosts)
Paths <- rep(list(list(list(route = c(), rate = NULL))), NumODPairs) # Initializes Path list
# Initial solution - each flow routed through shortest path based on link first derivative costs
for (i in 1:NumODPairs) {
Origin <- Flows[i, 1] # Origin of flow
Destination <- Flows[i, 2] # Destination of flow
Rate <- Flows[i, 3] # Rate of flow
Route <- shortestpath(LinkFirstDerivativeCosts, Origin, Destination) # Shortest path of flow
Paths[[i]][[1]] <- list(route = Route, rate = Rate) # Inserts path information in Paths list
}
# Loads links with rate
LinkRates <- loadlinks(NumNodes, NumODPairs, Paths)
# print("LinkRates");print(LinkRates)
# Determines maximum link load (ignoring NaN)
MaxUtilization <- max(LinkRates / LinkCapacities, na.rm = TRUE)
# print("MaxUtilization");print(MaxUtilization)
# print("Paths");print(Paths)
# Phase I: finds feasible solution
niter1 <- 0 # Initializes number of iterations of phase I
while (MaxUtilization >= 1) { # While there is at least one link overloaded
CapacityAdjustFactor <- (1 + DeltaCap) * MaxUtilization # Determines capacity adjust factor
# print("CapacityAdjustFactor");print(CapacityAdjustFactor)
AdjustedLinkCapacities <- CapacityAdjustFactor * LinkCapacities # Determines adjusted link capacities
# print("AdjustedLinkCapacities");print(AdjustedLinkCapacities)
# Determines link costs based on adjusted link capacities
LinkFirstDerivativeCosts <- AdjustedLinkCapacities / (AdjustedLinkCapacities - LinkRates)^2
LinkFirstDerivativeCosts[is.nan(LinkFirstDerivativeCosts)] <- Inf
# print("LinkFirstDerivativeCosts");print(LinkFirstDerivativeCosts)
# Determines second derivative link costs based on adjusted link capacities
LinkSecondDerivativeCosts <- 2 * AdjustedLinkCapacities / (AdjustedLinkCapacities - LinkRates)^3
LinkSecondDerivativeCosts[is.nan(LinkSecondDerivativeCosts)] <- Inf
# print("LinkSecondDerivativeCosts");print(LinkSecondDerivativeCosts)
# Iterates over OD pairs to deviate rate
for (i in 1:NumODPairs) {
Origin <- Flows[i, 1] # Origin of OD pair
Destination <- Flows[i, 2] # Destination of OD pair
NumPaths <- length(Paths[[i]]) # Number of paths of OD pair
# Determines best path of OD pair
BestPath <- shortestpath(LinkFirstDerivativeCosts, Origin, Destination)
# Inserts best path in Paths list (if not already there) and determines index
# of best path within this list; if best path is new its rate is zero
Best <- bestpathinsert(i, NumPaths, Paths, BestPath)
NumPaths <- Best$numroutes
BestPathIndex <- Best$bestrouteindex
Paths <- Best$paths
# Determines first derivative path lengths
PathFirstDerivativeLengths <- pathlengths(i, NumPaths, Paths, LinkFirstDerivativeCosts)
# Deviates rate between routes
Paths <- deviaterate(i, NumPaths, BestPathIndex, Paths, PathFirstDerivativeLengths, LinkSecondDerivativeCosts, Alpha)
# print("Paths");print(Paths)
}
LinkRates <- loadlinks(NumNodes, NumODPairs, Paths) # Loads links with rate
MaxUtilization <- max(LinkRates / LinkCapacities, na.rm = TRUE) # Determines maximum link load
niter1 <- niter1 + 1 # Increments number of iterations
}
# Display number of iterations in phase I
cat(sprintf("Number of iterations in phase I: %i", niter1), "\n")
# print("LinkRates");print(LinkRates)
# Phase II: finds optimal solution
Error2 <- Inf # Initializes convergence error
PreviousTotalMeanPackets <- 0 # Initializes PreviousTotalMeanPackets
niter2 <- 0 # Initializes number of iterations of phase II
while (abs(1 - Error2) > ErrorPhase2) { # While convergence tolerance not reached
# Determines link costs
LinkFirstDerivativeCosts <- LinkCapacities / (LinkCapacities - LinkRates)^2
LinkFirstDerivativeCosts[is.nan(LinkFirstDerivativeCosts)] <- Inf
# print("LinkFirstDerivativeCosts");print(LinkFirstDerivativeCosts)
# Determines second derivative link costs
LinkSecondDerivativeCosts <- 2 * LinkCapacities / (LinkCapacities - LinkRates)^3 # Incremental delays based on adjusted link capacities
LinkSecondDerivativeCosts[is.nan(LinkSecondDerivativeCosts)] <- Inf
# print("LinkSecondDerivativeCosts");print(LinkSecondDerivativeCosts)
# Iterates over OD pairs to deviate rate
for (i in 1:NumODPairs) {
Origin <- Flows[i, 1] # Origin of OD pair
Destination <- Flows[i, 2] # Destination of OD pair
NumPaths <- length(Paths[[i]]) # Number of paths of OD pair
# Determines best path of OD pair
BestPath <- shortestpath(LinkFirstDerivativeCosts, Origin, Destination)
# Inserts best path in Paths list (if not already there) and determines index
# of best path within this list; if best path is new its rate is zero
Best <- bestpathinsert(i, NumPaths, Paths, BestPath)
NumPaths <- Best$numroutes
BestPathIndex <- Best$bestrouteindex
Paths <- Best$paths
# Determines first derivative path lengths, of all paths belonging to OD pair i
PathFirstDerivativeLengths <- pathlengths(i, NumPaths, Paths, LinkFirstDerivativeCosts)
# Deviates rate between routes
Paths <- deviaterate(i, NumPaths, BestPathIndex, Paths, PathFirstDerivativeLengths, LinkSecondDerivativeCosts, Alpha)
# print("Paths");print(Paths)
}
# Loads links with rate
LinkRates <- loadlinks(NumNodes, NumODPairs, Paths)
# Determines average number of packets in each link
LinkMeanPackets <- LinkRates / (LinkCapacities - LinkRates)
LinkMeanPackets[which(is.nan(LinkMeanPackets))] <- 0
# Determines average number of packets in network
TotalMeanPackets <- sum(LinkMeanPackets)
# Determines convergence error
Error2 <- TotalMeanPackets / PreviousTotalMeanPackets
# print("Error2");print(Error2)
# Updates PreviousTotalMeanPackets
PreviousTotalMeanPackets <- TotalMeanPackets
# Increments number of iterations
niter2 <- niter2 + 1
}
# print("LinkRates");print(LinkRates)
# print("LinkMeanPackets");print(LinkMeanPackets)
# print("Paths");print(Paths)
# Print results
cat(sprintf("Number of iterations in phase 2: %i", niter2), "\n")
cat("\n")
cat(sprintf("Average number of packets: %f", TotalMeanPackets), "\n")
cat("\n")
cat(sprintf("Flow rates:"), "\n")
for (i in 1:NumODPairs) {
Origin <- Flows[i, 1]
Destination <- Flows[i, 2]
cat(sprintf("Flow from %i to %i", Origin, Destination), "\n")
NumPaths <- length(Paths[[i]])
for (j in 1:NumPaths) {
ThisPath <- Paths[[i]][[j]]$route
ThisRate <- Paths[[i]][[j]]$rate
cat(" Rate of route ")
cat(ThisPath, sep = "-")
cat(sprintf(": %f", ThisRate), "\n")
}
}

36
code/bg/parameters.R Normal file
View File

@ -0,0 +1,36 @@
parameters <- function() {
# Define here the capacity of each link, in bits/sec. The LinkCapacities matrix
# is a square matrix where the number of rows equals the number of nodes. Rows
# correspond to origin nodes and columns to destination nodes. Element(i,j)
# should be equal to the link capacity if there is a link from node i to node j,
# and should be 0 otherwise
link_capacity <- 256 * 1000
LinkCapacities <<- matrix(
c(
0, link_capacity, link_capacity, 0, 0, 0,
0, 0, 0, link_capacity, link_capacity, 0,
0, 0, 0, 0, link_capacity, 0,
0, 0, 0, 0, 0, link_capacity,
0, 0, 0, 0, 0, link_capacity,
0, 0, 0, 0, 0, 0
),
nrow = 6,
ncol = 6,
byrow = TRUE
)
# Define here the flows. Flows is a matrix with a user-defined number of rows and
# 3 columns. Each row corresponds to a different flow, and the three columns
# correspond to (1) the origin node, (2) the destination node, and (3) the
# offered rate between the two previous nodes, expressed in bits/sec
Flows <<- matrix(
c(
1, 6, (215 + 128) * 1000,
1, 5, 64 * 1000,
2, 5, 128 * 1000
),
nrow = 3,
ncol = 3,
byrow = TRUE
)
}

59
code/bg/shortestpath.R Normal file
View File

@ -0,0 +1,59 @@
shortestpath = function (Costs,Source,Destination) {
#Given a matrix of costs this function determines the shorthest path between a
#source and destination using the Dijkstra's algorithm. The route is the
#sequence of nodes from source to destination. Here is an example of a costs
#matrix:
#Costs = [0 10 10 Inf
# 10 0 10 10
# 20 5 0 10
# Inf 25 5 0];
NumNodes=nrow(Costs); #number of nodes
NodeLabels=Costs[Source,] #vector of node label
TempNodes=1:NumNodes #vector of temporary nodes
TempNodes=TempNodes[-Source] #remove source node from vector of temporary nodes
TempLabels=NodeLabels #vector of temporary labels
TempLabels=TempLabels[-Source] #remove label of source node from vector of temporary labels
NodeParents=rep(Source,NumNodes) #vector of node parents
p=Source #node that becomes permanently labelled
while (TRUE) {
i=which.min(TempLabels) #determines index of lower label node
c=min(TempLabels) #determines label of lower label node
p=TempNodes[i] #p is the next permanent node
#print(p)
TempNodes=TempNodes[-i] #remove p node from vector of temporary nodes
TempLabels=TempLabels[-i] #remove label of p node from vector of temporary labels
NodeLabels[p]=c #update node label of p node
if (p==Destination) break #iteration ends when destination found
for (k in 1:length(TempNodes)) { #update labels of temporary nodes
nt=TempNodes[k] #next temporary node
#print(nt)
ct=NodeLabels[p]+Costs[p,nt] #calculate cost to temporary node via p
# print(ct)
# print(Costs[p,nt])
# print(NodeLabels[nt])
if (ct < NodeLabels[nt]) { #update only if new cost is lower
TempLabels[k]=ct #update label on vector of temporary node labels
NodeLabels[nt]=ct #update label on vector of node labels
NodeParents[nt]=p #update node parent
}
}
}
#determines route from vector of node parents
Route=c(Destination)
r=Destination
while (r!=Source) {
r=NodeParents[r]
Route=cbind(r,Route);
}
return(Route)
}

BIN
images/15-diagram.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1,7 +1,7 @@
#import "@preview/tablex:0.0.6": tablex, rowspanx, colspanx
#import "/typst/util.typ" as util: indent_par, code_figure
#indent_par[The following code 10 is our implementation of the kleinrock approximation. It takes the link capacities, flows and packet size similarly to the `pnet` simulator.]
#indent_par[The following code 10 is our implementation of the Kleinrock approximation. It takes the link capacities, flows and packet size similarly to the `pnet` simulator.]
#code_figure(
text(size: 0.8em, raw(read("/code/kleinrock.R"), lang: "R", block: true)),

View File

@ -1,7 +1,7 @@
#import "@preview/tablex:0.0.6": tablex, rowspanx, colspanx
#import "/typst/util.typ" as util: indent_par, code_figure
#indent_par[We used our previously developed kleinrock simulator and the `pnet` simulator, running it 10 times and calculating 95% confidence intervals, acquiring the following results in table 18:]
#indent_par[We used our previously developed Kleinrock script and the `pnet` simulator, running it 10 times and calculating 95% confidence intervals, acquiring the following results in table 18:]
#figure(
pad(1em, tablex(
@ -41,6 +41,6 @@
caption: [Results]
)
#indent_par[Although the kleinrock approximation never enters the confidence intervals obtained from the `pnet` simulator, for lower values of $ρ$, it is close to the confidence intervals. However for larger values of $ρ$, it starts to drift apart very noticeably.]
#indent_par[Although the Kleinrock approximation never enters the confidence intervals obtained from the `pnet` simulator, for lower values of $ρ$, it is close to the confidence intervals. However for larger values of $ρ$, it starts to drift apart very noticeably.]
#pagebreak()

240
typst/exercises/15.typ Normal file
View File

@ -0,0 +1,240 @@
#import "@preview/tablex:0.0.6": tablex, rowspanx, colspanx
#import "/typst/util.typ" as util: indent_par, code_figure
#indent_par[Using the following network in figure 22, adapted from the guide, we define the link numbers and routes in the following colors:]
- Flow 1: Red
- Flow 2: Green
- Flow 3: Blue
- Flow 4: Cyan
#figure(
image("/images/15-diagram.png", width: 75%),
caption: "Network diagram"
)
==== a. Kleinrock
#indent_par[Using the previously developed Kleinrock script, we obtained the following results in table 20:]
#figure(
pad(1em, tablex(
columns: (auto, 1fr, 1fr),
align: center + horizon,
rowspanx(2)[ Flow ],
colspanx(2)[ Average packet delay ($"ms"$) ],
[ Per flow ],
[ Network ],
[1],
[73.2],
rowspanx(4)[44.36],
[2],
[31.2],
[3],
[39.1],
[4],
[7.81],
)),
kind: table,
caption: [Results]
)
#pagebreak()
==== b.
#indent_par[We ran the `pnet` simulator 10 times, calculating 95% confidence intervals, and obtained the results in table 21:]
#indent_par[The network average packet delay was calculated from each flow's average delay via the following formula:]
$ W = (sum_i λ_i W_i) / (sum_j λ_j) $
#indent_par[We used the following parameters for the simulation:]
- ```R
LinkCapacities <- replicate(7, 256 * 1000)
```
- ```R
Flows <- list(
list(rate = 215, packetsize = packet_size, route = c(1, 3, 6)),
list(rate = 64, packetsize = packet_size, route = c(2, 5)),
list(rate = 128, packetsize = packet_size, route = c(2, 5, 7)),
list(rate = 128, packetsize = packet_size, route = c(4))
)
```
- ```R
endTime <- 10000 * (1 / 64) # 156.25
```
#figure(
pad(1em, tablex(
columns: (auto, 1fr, 1fr),
align: center + horizon,
rowspanx(2)[ Flow ],
colspanx(2)[ Average packet delay ($"ms"$) ],
[ Per flow ],
[ Network ],
[1],
[53.64 .. 57.33],
rowspanx(4)[ 35.30 .. 37.15 ],
[2],
[27.96 .. 28.80],
[3],
[35.80 .. 36.72],
[4],
[7.66 .. 7.84],
)),
kind: table,
caption: [Results]
)
#pagebreak()
==== c.
#indent_par[In order to determine the optimal bifurcation path, we first determine that the flow through the link $2 -> 4$ and $2 -> 5$ must be equal. Given that the flow 4 already uses the link $2 -> 5$, we must account for it in the calculations.]
#indent_par[With this in mind, we reach the following equation system 17, where $l_24$ is the flow through link $2 -> 4$ and $l_25$ is the flow through link $2 -> 5$]
$ cases( l_24 + l_25 = 215, l_24 = l_25 + 128 ) $
#indent_par[Solving these, we reach $l_24 = 171.5$ and $l_25 = 43.5$]
#indent_par[In order to compare against our results in exercise 15.a, we used the Kleinrock script and got the following results in table 22.]
#figure(
pad(1em, tablex(
columns: (auto, auto, 1fr, 1fr),
align: center + horizon,
rowspanx(2)[ Flow ],
rowspanx(2)[ Bifurcation ],
colspanx(2)[ Average packet delay ($"ms"$) ],
[ Per flow ],
[ Network ],
rowspanx(2)[1],
[1],
rowspanx(2)[48.1],
rowspanx(5)[36.19],
[2],
[2],
rowspanx(3)[ N/A ],
[31.2],
[3],
[43.1],
[4],
[11.8],
)),
kind: table,
caption: [Results]
)
#indent_par[We can be sure that we have obtained the optimal bifurcation, because the average packet delay for each bifurcated flow has the same value. We also see that flow 2 was not affected at all, since flow 1 does not cross it on any of it's path or bifurcations. However, flow 3 and flow 4 have both been slightly affected due to the 2nd bifurcation of flow 1 passing through them. This is to be expected, as we are pushing more packets through the same links. Overall, the network average packet delay is lower, despite the increases to flow 3 and 4, due to flow 1 now having much lower delays.]
#pagebreak()
==== d.
#indent_par[We used the following parameters for the simulation of `bg.R`:]
- #text(size: 0.7em, ```R
LinkCapacities <<- matrix(
c(
0, 256e3, 256e3, 0 , 0 , 0,
0, 0 , 0 , 256e3, 256e3, 0,
0, 0 , 0 , 0 , 256e3, 0,
0, 0 , 0 , 0 , 0 , 256e3,
0, 0 , 0 , 0 , 0 , 256e3,
0, 0 , 0 , 0 , 0 , 0
),
nrow = 6,
ncol = 6,
byrow = TRUE
)
```)
- #text(size: 0.7em, ```R
Flows <<- matrix(
c(
1, 6, 215e3 + 128e3,
1, 5, 64e3,
2, 5, 128e3
),
nrow = 3,
ncol = 3,
byrow = TRUE
)
```)
#indent_par[Then obtained the results from the `bg.R` script in the following table 23:]
#figure(
pad(1em, tablex(
columns: (auto, auto, auto),
align: center + horizon,
[ Flow ],
[ Rate ($"bits" s^(-1)$) ],
[ Rate ($"packets" s^(-1)$) ],
[ $1 -> 2 -> 4 -> 6$ ], [ 170168 ], [ 170.168 ],
[ $1 -> 3 -> 5 -> 6$ ], [ 172832 ], [ 172.832 ],
[ $1 -> 2 -> 5 -> 6$ ], [ 0 ], [ 0 ],
[ $1 -> 2 -> 5$ ], [ 39218 ], [ 39.218 ],
[ $1 -> 3 -> 5$ ], [ 24782 ], [ 24.782 ],
[ $2 -> 5$ ], [ 128000 ], [ 128.000 ],
)),
kind: table,
caption: [Results]
)
#indent_par[Finally, using our previously developed Kleinrock script to get the following results in table 24:]
#figure(
pad(1em, tablex(
columns: (auto, 1fr, 1fr, 1fr),
align: center + horizon,
colspanx(2, rowspanx(2)[ Flow ]),
colspanx(2)[ Average packet delay ($"ms"$) ],
[ Per flow ],
[ Network ],
[1], [ $1 -> 2 -> 4 -> 6$ ], [ 44.8 ],
rowspanx(5)[ 35.87 ],
[2], [ $1 -> 3 -> 5 -> 6$ ], [ 46.3 ],
[3], [ $1 -> 2 -> 5$ ], [ 32.7 ],
[4], [ $1 -> 3 -> 5$ ], [ 34.3 ],
[5], [ $2 -> 5$ ], [ 11.3 ],
)),
kind: table,
caption: [Results]
)
#indent_par[Comparing with our previous attempt at bifurcation in exercise 15.c, we see an small overall improvement to the network average packet delay. The gradient projection algorithm discovered 2 new flows we hadn't used, namely $1 -> 3 -> 5 -> 6$ and $1 -> 2 -> 5$. Despite outputting the flow $1 -> 2 -> 5 -> 6$, it has no rate, so we did not include it in the kleinrock script.]

View File

@ -100,3 +100,6 @@
=== 14. Exercise 14
#include "exercises/14.typ"
=== 15. Exercise 15
#include "exercises/15.typ"