###
### Copyright (C) 2012 Martin J Reed martin@reednet.org.uk
### University of Essex, Colchester, UK
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation; either version 2 of the License, or
### (at your option) any later version.
###
### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License along
### with this program; if not, write to the Free Software Foundation, Inc.,
### 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
require("igraph",quietly=TRUE)
require("graph",quietly=TRUE,warn.conflicts=FALSE)
require("RBGL",quietly=TRUE,warn.conflicts=FALSE)
require("genalg",quietly=TRUE)
require("inlinedocs")
### This is rather dangerous and discouraged as it erases any state in
### the global environment that would mask objects from this
### package. DO NOT include in version submitted to CRAN
.onAttach <- function(libname, pkgname) {
nummasked <- length(intersect(ls(".GlobalEnv"),ls("package:reedgraph")))
if ( nummasked >0 ) {
packageStartupMessage(
paste(" WARNING ", nummasked, " objects from reedgraph\n",
" have overwritten the same objects in .GlobalEnv\n"))
}
rm(list=intersect(ls(".GlobalEnv"),ls("package:reedgraph")),
pos=".GlobalEnv")
}
rg.update2package <- function # Update objects in .GlobalEnv
### Update objects in .GlobalEnv to the latest in the package
### Usefull if you have stale values in .Rdata files saved
### in a previous workspace.
() {
print("Removing old definitions")
rm(list=intersect(ls(".GlobalEnv"),ls("package:reedgraph")),
pos=".GlobalEnv")
}
### return all the edges as a list of lists(head,tail)
### in graphNEL
### result is list of edges
### edgeMatrix might be better
rg.edgeL <- function(g) {
ed <- list()
for(i in nodes(g)) {
for(el in graph::edges(g)[[i]]) {
ed <- c(ed,list(c(i,el)))
}
}
ed
}
### returns the attribute of the graphNEL as a double
### vector (in natural edge order)
### input
### g - graphNEL
### attr="weight" for the default attribute
### returns c(....)
rg.edgeVector <- function(g,attr="weight") {
return(as.double(edgeData(g,attr=attr)))
}
rg.edgeVectorGamma <- function(g) {
c <- as.double(edgeData(g,attr="capacity"))
f <- as.double(edgeData(g,attr="weight"))
gamma <- (c-f)/c
return(gamma)
}
rg.demandsVector <- function(demands,attr="demand") {
return(as.double(lapply(res$demands,"[[",attr)))
}
rg.count.paths <- function(demands,numlist=NULL) {
num <- 0
if(is.null(numlist))
numlist <- seq(1,length(demands))
for(i in numlist){
for(p in names(demands[[i]]$paths)) {
num <- num + 1
}
}
return(num)
}
### Creates an adjacency matrix containing edge ids
### useful to get an edge number given two nodes
### g - graphNEL (with L edges)
### returns matrix (node/node) where element is edge
### number 1 ..... L between node/node
adjMatrix <- function(g) {
em <- edgeMatrix(g)
L <- length(em)/2
N <- length(nodes(g))
m <- matrix(nrow=N,ncol=N)
for(i in seq(1:L)) {
m[as.integer(em[1,i]),as.integer(em[2,i])] <- i
}
return(m)
}
### Calculates the number of edge disjoint paths
### between every pair of nodes
### Returns a matrix where element [u,v] is the
### number of paths between u and v where
### 0 means disconnected (or to itself)
### 1 for one path etc
### THIS IS INCORRECT
rg.num.edge.disjoint.paths <- function(g) {
# set ip a matrix to count (will be symmetrical for undirected graph)
count <- matrix(0,ncol=length(nodes(g)),
nrow=length(nodes(g)))
for(i in nodes(g)) {
splist <- dijkstra.sp(g,start=i)
for(j in nodes(g)) {
if ( i != j ) {
if (is.infinite(splist$distances[[j]])) {
count[as.integer(i),as.integer(j)] <- 0
} else {
gtmp <- g
splisttmp <- splist
while( is.finite(splisttmp$distances[[j]])) {
count[as.integer(i),as.integer(j)] <-
count[as.integer(i),as.integer(j)] + 1
path <- extractPath(i,j,splisttmp$penult)
# removing an edge takes longer, so using set.weight
# instead
#gtmp <- rg.remove.edges(gtmp.path)
gtmp <- rg.set.weight.on.path(gtmp,path,Inf)
splisttmp <- dijkstra.sp(gtmp,start=i)
}
}
}
}
}
return(count)
}
### Counts number of paths in demands
rg.count.paths <- function(demands) {
count <- 0
for(demand in demands) {
count <- count + length(demand$paths)
}
return(count)
}
### Removes edges from a graph
### g is GraphNEL
### path is vector of the path vertex labels
### from source to tail
### returns GraphNEL with path removed
rg.remove.edges <- function(g,path) {
fromlist <- path[1:{length(path)-1}]
tolist <- path[2:{length(path)}]
for(i in 1:length(fromlist)) {
g <- removeEdge(as.character(fromlist[[i]]),
as.character(tolist[[i]])
,g)
}
return(g)
}
### Simple way to set demands
### val the value to assign to ALL demands
### array of node pairs, as characters
rg.set.demand <- function(val=1.0,nodes) {
if(length(nodes) %% 2 != 0) {
cat("Error in rg.set.demands nodes must be even\n")
return(NULL)
}
j <- 1
count <- 1
demand <- list()
for(i in seq(1,length(nodes),2)) {
j <- i+1
demand[[as.character(count)]]$source <- as.character(nodes[i])
demand[[as.character(count)]]$sink <- as.character(nodes[j])
demand[[as.character(count)]]$demand <- val
count <- count + 1
}
return(demand)
}
rg.gen.demands <- function # Generate some demands
### Generates a list of list("number",source=,sink=,demand=) where
### each demand is identified by a number in the order of the demand
### therefore probably not best as a mutuable structure unless care is taken
### that number and indexing is used correctly.
(g, ##< graphNEL
num=NULL, ##< num number of demands to generate , it will be this many across a
## uniformly random selection of node pairs (i,j) where i != j
## If NULL then it will be between every pair
val=1.0, ##< value of demand. Accepts a vector and iterates through in
## order and will continue back to beggining if necessary
## use runif(num,min,max) if uniform random required
nodes=NULL ##< list of nodes to generate demands between. If NULL it will
## be every node in the graph
) {
if( is.null(nodes) ) nodes <- nodes(g)
comm <- list()
n=1;
k <- 1
vallen <- length(val)
if( is.null(num) ) { # all pairs uniform demands
for(i in nodes) {
##cat("doing",i,"\n")
for(j in nodes) {
if ( i != j ) {
comm[[as.character(n)]] <- list(source=i,sink=j,demand=val[k])
k <- k + 1
if(k > vallen) k <- 1
n <- n+1
}
}
}
} else { # random selection of nodes
numnodes <- length(nodes)
for(i in seq(1:num)) {
fromto <- floor(runif(2,min=1,max=numnodes+1))
while(fromto[1] == fromto[2])
fromto <- floor(runif(2,min=1,max=numnodes+1))
comm[[as.character(n)]] <- list(
source=
nodes[fromto[1]],
sink=
nodes[fromto[2]]
,demand=val[k])
k <- k + 1
if(k > vallen) k <- 1
n <- n+1
}
}
comm
### a list of elements list("number",source=,sink=,demand=)
}
### Sets all demands to a constant value
### val - value to set
### returns new demands
rg.set.demands.demand <- function(demands,val) {
for(n in names(demands)) {
demands[[n]]$demand <- val
}
demands
}
rg.set.capacity <- function # Sets the capacity of the edges on the graph
(g, ##< a graphNEL object
val ##< scalar or vector length of edges
) {
em <- edgeMatrix(g)
if (match("capacity",names(edgeDataDefaults(g)),nomatch=1)) {
edgeDataDefaults(g,"capacity") <- 1.0
}
nodes <- nodes(g)
edgeData(g,
from=nodes[em[1,]],
to=nodes[em[2,]],
attr="capacity") <- val
g
### graphNEL with capacity attribute set
}
### Sets the weight of the edges on the graph
### val - scalar or vector length of edges
rg.set.weight <- function(g,val) {
em <- edgeMatrix(g)
if (match("weight",names(edgeDataDefaults(g)),nomatch=1)) {
edgeDataDefaults(g,"weight") <- 1.0
}
edgeData(g,
from=nodes(g)[em[1,]],
to=nodes(g)[em[2,]],
attr="weight") <- val
g
}
### make sure the labels are the same as vertex numbers
### this seems to be needed else there are problems with
### dijkstra.sp which takes in "labels" and returns vertices
### labels as numbers
### use g <- rg.relabel(g)
rg.relabel <- function(myg) {
nodes(myg) <- as.character(seq(1:length(nodes(myg))))
myg
}
### adds a value val to the weight attribute on each edge
### in path
### input graphNEL, path vector of integer, val is the amount to add
### this is very inefficient if called repeatedly, better to copy
### and paste "Inline" if done many times
rg.addto.weight.on.path <- function(g,path,val,attr="weight") {
nodes <- nodes(g)
if(length(path) == 1) {
edgeData(g,nodes[path[1]],nodes[path[2]],attr) <-
edgeData(g,nodes[path[1]],nodes[path[2]],attr) +val
} else {
fromlist <- path[1:{length(path)-1}]
tolist <- path[2:{length(path)}]
newvals <- as.double(edgeData(g,nodes[fromlist],nodes[tolist],attr)) + val
edgeData(g,nodes[fromlist],nodes[tolist],attr) <- newvals
}
return(g)
}
### sets the weight attribute on each edge
### in path to val WARNING THIS WILL OVERIDE OTHER PATHS THAT HAVE SET
### THE EDGE WEIGHT : you probably want rg.addto.weight.on.path()
### input graphNEL, path vector of integer, val is the amount to set
###
rg.set.weight.on.path <- function(g,path,val) {
if(length(path) == 1) {
edgeData(g,as.character(path[1]),as.character(path[2]),"weight") <- val
} else {
fromlist <- path[1:{length(path)-1}]
tolist <- path[2:{length(path)}]
edgeData(g,as.character(fromlist),as.character(tolist),"weight") <- val
}
return(g)
}
### demands:
##' Compute the multicommodity flow with shortest path
##'
##' Compute the multicommodity flow in a graph using naive shortest
##' path g graph of type graphNEL with the edge weight variable used
##' for the shortest path and capacities ignored (ie it may overbook
##' an edge)
##' @title Shortest Path Max Concurrent Flow
##' @param g graphNEL object with capacity attribute on each edge
##' @param demands a list each element is a list [source, sink, demand]
##' @return a list(demands,gflow,lambda,gamma) where demands is a
##' list(demand,source,sink,paths,flow) where paths is a list("X|Y|Z")
##' of the path (only one path in this function, others have
##' more). lambda is the max concurrent flow primal value (very
##' unoptimal in this case), gamma is the prmal value for the min
##' congestion flow (again highly suboptimal)
##' @author Martin Reed
rg.sp.max.concurrent.flow <- function # Compute the multicommodity flow with shortest path
### Compute the multicommodity flow in a graph using naive shortest
### path g graph of type graphNEL with the edge weight variable used
### for the shortest path and capacities ignored (ie it may overbook
### an edge)
(g, ##< graphNEL object with capacity attribute on each edge
demands ##< a list each element is a list [source, sink, demand]
) {
# convert demands node labels to the index
nodelabels <- nodes(g)
demands <- rg.demands.relable.to.indices(demands,nodelabels)
g <- rg.relabel(g)
for(c in names(demands)) {
demands[[c]]$flow <- 0
}
gsol <- g
g.sp <- list()
g <- rg.set.weight(g,1.0)
gsol <- rg.set.all.graph.edge.weights(gsol)
ccount <- 1
for(i in demands) {
## calculate dijkstra.sp if we do not have one for this vertex
if(is.null(g.sp[[i$source]]))
g.sp[[i$source]] <- dijkstra.sp(g,start=i$source)$penult
path <- extractPath(i$source,i$sink,g.sp[[i$source]])
gsol <- rg.addto.weight.on.path(gsol,path,i$demand)
demands[[ccount]] <- updateExplicitFlow(g,g.sp[[i$source]],i$demand,i)
demands[[ccount]]$flow <- demands[[ccount]]$flow +i$demand
ccount <- ccount + 1
}
f <- as.double(edgeData(gsol,attr="weight"))
c <- as.double(edgeData(gsol,attr="capacity"))
lambda <- min(c/f)
gamma <- min((c-f)/c)
#gsol <- rg.max.concurrent.flow.graph(gsol,demands)
##put back the demands labels instead of indices
nodes(gsol) <- nodelabels
demands <- rg.demands.relable.from.indices(demands,nodelabels)
retval <- list(demands=demands,gflow=gsol,lambda=lambda,gamma=gamma)
return(retval)
### A list(demands,gflow,lambda,gamma) where demands is a
### list(demand,source,sink,paths,flow) where paths is a list("X|Y|Z")
### of the path (only one path in this function, others have
### more). lambda is the max concurrent flow primal value (very
### unoptimal in this case), gamma is the prmal value for the min
### congestion flow (again highly suboptimal)
}
### Compute the multicommodity flow in a graph using naive shortest
### path with first fit for g, graph of type graphNEL, with the edge
### weight variable used for the shortest path. If the capacity is
### reached it will set the edge weight to infinity so that a fully
### booked edge is omitted next time demands: a list each element is a
### list [source, sink, demand] WARNING NOT WRITTEN YET
rg.sp.ff.max.concurrent.flow <- function(g,demands) {
# convert demands node labels to the index
nodelabels <- nodes(g)
demands <- rg.demands.relable.to.indices(demands,nodelabels)
g <- rg.relabel(g)
for(c in names(demands)) {
demands[[c]]$flow <- 0
demands[[c]]$paths <- list()
}
gsol <- g
g.sp <- list()
g <- rg.set.weight(g,1.0)
gsol <- rg.set.all.graph.edge.weights(gsol)
edgesfrom <- as.character(
sapply(names(edgeData(g,attr="capacity")),function(x) strsplit(x,"\\|")[[1]])[1,])
edgesto <- as.character(
sapply(names(edgeData(g,attr="capacity")),function(x) strsplit(x,"\\|")[[1]])[2,])
for(d in names(demands)) {
i <- demands[[d]]
gtmp <- g
## find edges in gsol that can not accept demand and set them to infinity
weights <- as.double(edgeData(gsol,att="weight"))
capacities <- as.double(edgeData(gsol,att="capacity"))
free <- (capacities-weights)
gtmpw <- as.double(edgeData(gtmp,att="weight"))
gtmpw[i$demand > free] <- Inf
edgeData(gtmp,from = edgesfrom, to = edgesto, attr="weight") <- gtmpw
dij <- dijkstra.sp(gtmp,start=i$source)
if(dij$distance[i$sink] < Inf) {
path <- extractPath(i$source,i$sink,dij$penult)
fromlist <- path[1:length(path)-1]
tolist <- path[2:length(path)]
weights <- as.double(edgeData(gsol,from=as.character(fromlist),
to=as.character(tolist),att="weight"))
capacities <- as.double(edgeData(gsol,from=as.character(fromlist),
to=as.character(tolist),att="capacity"))
minfree <- min(capacities-weights)
free <- (capacities-weights)
gsol <- rg.addto.weight.on.path(gsol,path,i$demand)
demands[[d]] <- updateExplicitFlow(g,dij$penult,i$demand,i)
demands[[d]]$flow <- i$demand
}
}
f <- as.double(edgeData(gsol,attr="weight"))
c <- as.double(edgeData(gsol,attr="capacity"))
lambda <- min(c/f)
gamma <- min((c-f)/c)
##put back the demands labels instead of indices
nodes(gsol) <- nodelabels
demands <- rg.demands.relable.from.indices(demands,nodelabels)
retval <- list(demands=demands,gflow=gsol,lambda=lambda,gamma=gamma)
return(retval)
}
rg.demands.relable.to.indices <- function # Relable-demands-to-indices
### Relable a demands list using indices from labels
(demands, ##<< demands list in format shown elsewhere
nodelabels ##<< vector of nodelables c("1","2","fred"...)
) {
demands <- lapply(demands,function(x) {x$source <- as.character(which(nodelabels==x$source));
return(x)})
demands <- lapply(demands,function(x) {x$sink <- as.character(which(nodelabels==x$sink));
return(x)})
## Need to fix the path labels here as well!
## below will create the correct key, just need to update a key
for(i in names(demands)) {
demand <- demands[[i]]
if(!is.null(demand$paths)) {
demands[[i]]$paths <- list()
for(p in names(demand$paths)) {
tmp <- as.numeric(demand$paths[[p]])
demands[[i]]$paths[[paste0(as.character(sapply(
strsplit(p, "\\|")[[1]],function(x) which(nodelabels==x))),collapse="|")]] <- tmp
}
}
}
return(demands)
### The list of demands with node labels converted to indices
}
rg.demands.relable.from.indices <- function(demands,nodelabels) {
demands <- lapply(demands,function(x) {x$source <- nodelabels[as.integer(x$source)]; return(x)})
demands <- lapply(demands,function(x) {x$sink <- nodelabels[as.integer(x$sink)]; return(x)})
# Need to fix the path labels here as well!
# below will create the correct key, just need to update a key
for(i in names(demands)) {
demand <- demands[[i]]
demands[[i]]$paths <- list()
for(p in names(demand$paths)) {
tmp <- as.numeric(demand$paths[[p]])
demands[[i]]$paths[[paste0(nodelabels[as.integer(strsplit(p,"\\|")[[1]])],collapse="|")]] <- tmp
}
}
return(demands)
}
### Compute the multicommodity flow in a graph using naive shortest
### path g graph of type graphNEL with the edge weight variable used
### for the shortest path and capacities ignored (ie it may overbook
### an edge)
### demands: a list each element is a list [source, sink, demand]
rg.sp.max.concurrent.flow.residual <- function(g,demands) {
for(c in names(demands)) {
demands[[c]]$flow <- 0
}
gsol <- g
g.sp <- list()
gsol <- rg.set.all.graph.edge.weights(gsol)
ccount <- 1
for(i in demands) {
## calculate dijkstra.sp if we do not have one for this vertex
if(is.null(g.sp[[i$source]]))
g.sp[[i$source]] <- dijkstra.sp(g,start=i$source)$penult
path <- extractPath(i$source,i$sink,g.sp[[i$source]])
gsol <- rg.addto.weight.on.path(gsol,path,i$demand)
demands[[ccount]]$flow <- demands[[ccount]]$flow +i$demand
ccount <- ccount + 1
}
f <- as.double(edgeData(gsol,attr="weight"))
c <- as.double(edgeData(g,attr="capacity"))
lambda <- min(c/f)
gamma <- min((c-f)/c)
#demands <- rg.max.concurrent.flow.rescale.demands.flow(demands,lambda)
#gsol <- rg.max.concurrent.flow.graph(gsol,demands)
retval <- list(demands=demands,gflow=gsol,lambda=lambda,gamma=gamma)
return(retval)
}
### Utility function for MCF algorithm
### at the end this is the dual solution value
calcBetaRestricted <- function(demands,gdual) {
Alpha <- 0
for(demand in demands) {
lcost <- Inf
for(p in names(demand$paths)) {
pv <- as.vector(strsplit(p,"|",fixed=TRUE)[[1]])
fromlist <- pv[1:length(pv)-1]
tolist <- pv[2:length(pv)]
weights <- as.double(edgeData(gdual,from=fromlist,to=tolist,att="weight"))
distance <- sum(weights)
if(lcost > distance)
lcost <- distance
}
Alpha <- Alpha + demand$demand * lcost
}
D <- sum(as.double(edgeData(gdual,attr="capacity"))*
as.double(edgeData(gdual,attr="weight")))
Beta <- D / Alpha
Beta
}
### Utility function for MCF algorithm
### at the end this is the dual solution value
calcBeta <- function(demands,gdual) {
Alpha <- 0
for(demand in demands) {
sp <- dijkstra.sp(gdual,demand$source)
Alpha <- Alpha + demand$demand * sp$distances[[demand$sink]]
}
D <- sum(as.double(edgeData(gdual,attr="capacity"))*
as.double(edgeData(gdual,attr="weight")))
Beta <- D / Alpha
Beta
}
### Utility function for MCF algorithm
### at the end this is the primal solution value
calcLambda <- function(demands) {
d <- as.double(lapply(demands,"[[","demand"))
f <- as.double(lapply(demands,"[[","flow"))
lambda <- min(f/d)
lambda
}
### Calculates the maximum concurrent flow using
### rg.fleischer.max.concurrent.flow It uses the demand scaling
### algorithm suggested by "Faster approximation schemes for
### fractional multicommodity flow problems" G. Karakostas,
### Proceedings ACM/SIAM SODA 2002
### g: graphNEL weights ignored
### e: approximation value for a w-opt solution
### where (1+w) = (1-e)^2 (default e=0.1)
### progress: display graphical progress bar (default false)
rg.max.concurrent.flow.prescaled <- function(g,demands,e=0.1,
progress=FALSE,ccode=TRUE,updateflow=TRUE,permutation="random",deltaf=1.0) {
savedemands <- demands
## first obtain a reasonable feasible flow using
## a poor shortest path flow
estimate <- rg.sp.max.concurrent.flow(g,demands)
## we know that estimate$lambda < Beta
## so scale demands by this much to make sure now
## that beta > 1
estlambdascale <- estimate$lambda
demands <- rg.rescale.demands(demands,estimate$lambda)
## Beta might be very high so obtain 2-approximate solution
## (1+w) = 2 = (1-e)^-3
e2= 1- 2^(-1/3)
if(progress != FALSE)
progress <- "Calculating 2 opt"
if (ccode) {
res.2opt <- rg.fleischer.max.concurrent.flow.c(g,demands,e=e2,
updateflow=FALSE,
progress=progress,permutation,
deltaf)
} else {
res.2opt <- rg.fleischer.max.concurrent.flow(g,demands,e=e2,
updateflow=FALSE,progress=progress)
}
## now have 2-opt solution as beta_est so beta < beta_est < 2*beta
## scaling by beta_est/2 give best demand for reduced running time
scalef <- res.2opt$beta /2
opt2scale <- scalef
demands <- rg.rescale.demands(demands,scalef)
if(progress != FALSE)
progress <- "Main calculation"
if (ccode) {
res <- rg.fleischer.max.concurrent.flow.c(g,demands,e=e,
progress=progress,updateflow=updateflow,
permutation,
deltaf)
} else {
res <- rg.fleischer.max.concurrent.flow(g,demands,e=e,
progress=progress,updateflow=updateflow)
}
for(n in names(demands)) {
res$demands[[n]]$demand <- savedemands[[n]]$demand
}
res$lambda <- calcLambda(res$demands)
res$beta <- calcBeta(res$demands,res$gdual)
res$estlambdascale <- estlambdascale
res$opt2scale <- opt2scale
res
}
### Compute the maximum concurrent flow See Fleischer "Approximating
### fractional multicommodity flow independent of the number of
### commodities", SIAM J. Discrete Maths, Vol 13/4, pp 505-520
###
### g: graphNEL (vertex labels need to be same as vertex index)
### demands: vector
###
### g: graphNEL with capacity data on each edge, edge
### weight is ignored (traffic is routed over any way to meet
### concurrent flow
###
### e: accuracy ( 0 < e < 1 ) 0 is more accurate, 1 less accurate
###
### permutation: how the demands are chosen can be either
### c(....) integers specifying demand order
### "fixed" done in fixed order
### "random" done in random order
### "lowest" done in lowest cost (lowest dual path) order
###
### output: list:
### graph: with weight set to edge flow
### demands: each with met demand and paths
rg.fleischer.max.concurrent.flow.c <- function(g,
demands,
e=0.1,
updateflow=TRUE,
progress=FALSE,
permutation="random",
deltaf=1.0) {
nodelabels <- nodes(g)
demands <- rg.demands.relable.to.indices(demands,nodelabels)
g <- rg.relabel(g)
em <- edgeMatrix(g)
nN <- nodes(g)
nv <- length(nN)
ne <- ncol(em)
eW <- unlist(edgeWeights(g))
cap <- as.double(edgeData(g,attr="capacity"))
demands.sources <- as.integer(lapply(demands,"[[","source"))
demands.sinks <- as.integer(lapply(demands,"[[","sink"))
demands.demand <- lapply(demands,"[[","demand")
retlist <- .Call("rg_fleischer_max_concurrent_flow_c_new",
as.double(cap),
as.integer(em-1),
as.integer(nv),
as.integer(demands.sources -1 ),
as.integer(demands.sinks -1),
as.double(demands.demand),
as.double(e)
)
demands <- retlist$demands
delta <- (ne / (1-e)) ^ (-1/e)
scalef <- 1 / log(1/delta,base=(1+e))
gdual <- g
fromlist <- edgeMatrix(g)[1,]
tolist <- edgeMatrix(g)[2,]
edgeData(gdual,from=as.character(fromlist),to=as.character(tolist),attr="weight") <- retlist$lengths
#demands <- rg.max.concurrent.flow.rescale.demands.flow(demands,scalef)
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
beta <- calcBeta(demands,gdual)
lambda <- calcLambda(demands)
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
##put back the demands labels instead of indices
demands <- rg.demands.relable.from.indices(demands,nodelabels)
nodes(gflow) <- nodelabels
nodes(gdual) <- nodelabels
retlist2 <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,lambda=lambda,
phases=retlist$totalphases,e=e,
ratio=foundratio,
bound=ratiobound
)
}
rg.max.concurrent.flow.int <- function # Integer minimum congestion concurrent Flow
### Computes the integer minimum congestion concurrent flow
### Based on testing for best integer flow so far as part of fractional
### multicommodity flow solution.
(g, ##< graphNEL
demands, ##< demands - list (index character count) of list("source","sink",demand)
e=0.1, ##< approximation limit 0 < e < 1.0, smaller is more accurate but takes longer
updateflow=TRUE, ##< update the flow
progress=FALSE, ##< display progress bar (not working in latest version)
permutation="random", ##< only random in this version
deltaf=1.0, ##< not used in this version
prescaled=TRUE ## scaling linear demands for faster run-time
) {
nodelabels <- nodes(g)
demands <- rg.demands.relable.to.indices(demands,nodelabels)
g <- rg.relabel(g)
em <- edgeMatrix(g)
nN <- nodes(g)
nv <- length(nN)
ne <- ncol(em)
eW <- unlist(edgeWeights(g))
cap <- as.double(edgeData(g,attr="capacity"))
demands.sources <- as.integer(lapply(demands,"[[","source"))
demands.sinks <- as.integer(lapply(demands,"[[","sink"))
demands.demand <- lapply(demands,"[[","demand")
retlist <- .Call("max_concurrent_flow_int",
as.double(cap),
as.integer(em-1),
as.integer(nv),
as.integer(demands.sources -1 ),
as.integer(demands.sinks -1),
as.double(demands.demand),
as.double(e),
prescaled
)
demands <- retlist$demands
delta <- (ne / (1-e)) ^ (-1/e)
scalef <- 1 / log(1/delta,base=(1+e))
gdual <- g
fromlist <- edgeMatrix(g)[1,]
tolist <- edgeMatrix(g)[2,]
edgeData(gdual,from=as.character(fromlist),to=as.character(tolist),attr="weight") <- retlist$lengths
#demands <- rg.max.concurrent.flow.rescale.demands.flow(demands,scalef)
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
beta <- retlist$beta
lambda <- retlist$lambda
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
##put back the demands labels instead of indices
demands <- rg.demands.relable.from.indices(demands,nodelabels)
nodes(gflow) <- nodelabels
nodes(gdual) <- nodelabels
retlist2 <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,lambda=lambda,
phases=retlist$totalphases,e=e,
ratio=foundratio,
bound=ratiobound,
gamma=retlist$gamma
)
### a list of many things:
### demands - list of lists("source","sink",val,flow,path) <-format needs checking
### gflow - graphNEL with flow on each edge as a weight
### gdual -graphNEL with dual graph lengths - only useful for analysis
### beta - the linear dual problem limit
### lambda - the linear primal problem limit
### phases - the number of phases it took
### ratio - the found ratio between primal/dual, would like this to be near 1.0
### bound - the expected ratio between primal/dual
### gamma - the fractional free capacity on most constrained link (bigger better).
}
### As above but using prescaling to speed it up
rg.max.concurrent.flow.prescaled.c <- function(g,
demands,
e=0.1,
updateflow=TRUE,
progress=FALSE,
permutation="random",
deltaf=1.0) {
nodelabels <- nodes(g)
demands <- rg.demands.relable.to.indices(demands,nodelabels)
g <- rg.relabel(g)
em <- edgeMatrix(g)
nN <- nodes(g)
nv <- length(nN)
ne <- ncol(em)
eW <- unlist(edgeWeights(g))
cap <- as.double(edgeData(g,attr="capacity"))
demands.sources <- as.integer(lapply(demands,"[[","source"))
demands.sinks <- as.integer(lapply(demands,"[[","sink"))
demands.demand <- lapply(demands,"[[","demand")
retlist <- .Call("rg_fleischer_max_concurrent_flow_c_prescaled",
as.double(cap),
as.integer(em-1),
as.integer(nv),
as.integer(demands.sources -1 ),
as.integer(demands.sinks -1),
as.double(demands.demand),
as.double(e)
)
demands <- retlist$demands
delta <- (ne / (1-e)) ^ (-1/e)
scalef <- 1 / log(1/delta,base=(1+e))
gdual <- g
fromlist <- edgeMatrix(g)[1,]
tolist <- edgeMatrix(g)[2,]
edgeData(gdual,from=as.character(fromlist),to=as.character(tolist),attr="weight") <- retlist$lengths
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
beta <- calcBeta(demands,gdual)
lambda <- calcLambda(demands)
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
##put back the demands labels instead of indices
demands <- rg.demands.relable.from.indices(demands,nodelabels)
nodes(gflow) <- nodelabels
nodes(gdual) <- nodelabels
retlist2 <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,lambda=lambda,
phases=retlist$totalphases,e=e,
ratio=foundratio,
bound=ratiobound
)
}
### As above but using prescaling to speed it up
rg.minimum.congestion.flow.c <- function(g,
demands,
e=0.1,
updateflow=TRUE,
progress=FALSE,
permutation="random",
deltaf=1.0) {
nodelabels <- nodes(g)
demands <- rg.demands.relable.to.indices(demands,nodelabels)
g <- rg.relabel(g)
em <- edgeMatrix(g)
nN <- nodes(g)
nv <- length(nN)
ne <- ncol(em)
eW <- unlist(edgeWeights(g))
cap <- as.double(edgeData(g,attr="capacity"))
demands.sources <- as.integer(lapply(demands,"[[","source"))
demands.sinks <- as.integer(lapply(demands,"[[","sink"))
demands.demand <- lapply(demands,"[[","demand")
retlist <- .Call("rg_min_congestion_flow",
as.double(cap),
as.integer(em-1),
as.integer(nv),
as.integer(demands.sources -1 ),
as.integer(demands.sinks -1),
as.double(demands.demand),
as.double(e)
)
demands <- retlist$demands
delta <- (ne / (1-e)) ^ (-1/e)
scalef <- 1 / log(1/delta,base=(1+e))
gdual <- g
fromlist <- edgeMatrix(g)[1,]
tolist <- edgeMatrix(g)[2,]
edgeData(gdual,from=as.character(fromlist),to=as.character(tolist),attr="weight") <- retlist$lengths
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
res$gamma <- rg.mcf.find.gamma(gflow)
##beta <- calcBeta(demands,gdual)
beta <- retlist$beta
##lambda <- calcLambda(demands)
lambda <- retlist$lambda
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
##put back the demands labels instead of indices
demands <- rg.demands.relable.from.indices(demands,nodelabels)
nodes(gflow) <- nodelabels
nodes(gdual) <- nodelabels
retlist2 <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,lambda=lambda,
phases=retlist$totalphases,e=e,
ratio=foundratio,
bound=ratiobound
)
}
rg.minimum.congestion.flow.int.c <- function(g,
demands,
e=0.1,
updateflow=TRUE,
progress=FALSE,
permutation="random",
deltaf=1.0) {
nodelabels <- nodes(g)
demands <- rg.demands.relable.to.indices(demands,nodelabels)
g <- rg.relabel(g)
em <- edgeMatrix(g)
nN <- nodes(g)
nv <- length(nN)
ne <- ncol(em)
eW <- unlist(edgeWeights(g))
cap <- as.double(edgeData(g,attr="capacity"))
demands.sources <- as.integer(lapply(demands,"[[","source"))
demands.sinks <- as.integer(lapply(demands,"[[","sink"))
demands.demand <- lapply(demands,"[[","demand")
retlist <- .Call("rg_min_congestion_flow_int",
as.double(cap),
as.integer(em-1),
as.integer(nv),
as.integer(demands.sources -1 ),
as.integer(demands.sinks -1),
as.double(demands.demand),
as.double(e)
)
demands <- retlist$demands
delta <- (ne / (1-e)) ^ (-1/e)
scalef <- 1 / log(1/delta,base=(1+e))
gdual <- g
fromlist <- edgeMatrix(g)[1,]
tolist <- edgeMatrix(g)[2,]
edgeData(gdual,from=as.character(fromlist),to=as.character(tolist),attr="weight") <- retlist$lengths
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
res$gamma <- rg.mcf.find.gamma(gflow)
beta <- calcBeta(demands,gdual)
lambda <- calcLambda(demands)
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
##put back the demands labels instead of indices
demands <- rg.demands.relable.from.indices(demands,nodelabels)
nodes(gflow) <- nodelabels
nodes(gdual) <- nodelabels
retlist2 <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,lambda=lambda,
phases=retlist$totalphases,e=e,
ratio=foundratio,
bound=ratiobound
)
}
rg.fleischer.max.concurrent.flow.c.old <- function(g,
demands,
e=0.1,
updateflow=TRUE,
progress=FALSE,
permutation="random",
deltaf=1.0) {
em <- edgeMatrix(g)
nN <- nodes(g)
nv <- length(nN)
ne <- ncol(em)
eW <- unlist(edgeWeights(g))
cap <- as.double(edgeData(g,attr="capacity"))
demands.sources <- as.integer(lapply(demands,"[[","source"))
demands.sinks <- as.integer(lapply(demands,"[[","sink"))
demands.demand <- lapply(demands,"[[","demand")
doubreq <- 2/e * log(ne/(1-e),base=(1+e))
if(progress != FALSE) {
pb <- txtProgressBar(title = "progress bar", min = 0,
max = doubreq, style=3)
} else {
pb <- NULL
}
if(is.numeric(permutation))
permutation <- permutation - 1
else if(permutation == "fixed")
permutation <- 0: (length(demands) -1)
else if(permutation == "random")
permutation <- -1
else if(permutation == "lowest")
permutation <- -2
#permutation <- seq(0,length(demands)-1)
retlist <- .Call("rg_fleischer_max_concurrent_flow_c_boost",
as.integer(nv),
as.integer(ne),
as.integer(em-1),
as.double(eW),
as.double(cap),
as.integer(length(demands)),
as.integer(demands.sources -1 ),
as.integer(demands.sinks -1),
as.double(demands.demand),
as.double(e),
as.logical(updateflow),
pb,
environment(),
progress,
permutation,
deltaf
)
demands <- retlist$demands
delta <- deltaf * (ne / (1-e)) ^ (-1/e)
scalef <- 1 / log(1/delta,base=(1+e))
gdual <- g
fromlist <- edgeMatrix(g)[1,]
tolist <- edgeMatrix(g)[2,]
edgeData(gdual,from=as.character(fromlist),to=as.character(tolist),attr="weight") <- retlist$lengths
demands <- rg.max.concurrent.flow.rescale.demands.flow(demands,scalef)
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
beta <- calcBeta(demands,gdual)
lambda=NULL
if(updateflow) {
lambda <- calcLambda(demands)
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
} else {
foundratio <- NULL
ratiobound <- NULL
}
if( progress != FALSE) {
close(pb)
}
retlist2 <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,lambda=lambda,
phases=retlist$totalphases,e=e,
ratio=foundratio,
bound=ratiobound
)
}
### Utility function used by rg.fleischer.max.concurrent.flow()
### penult - vetor from Dijkstra.Sp
### mincap - mincapacity to set flow to this value
### demand - the specific demand to update
updateExplicitFlow <- function(g,penult,mincap,demand) {
s <- demand$source
t <- demand$sink
p <- t
t <- penult[[t]]
tag <- paste(nodes(g)[t],"|",p,sep="")
path <- tag
while(s !=t ) {
p <- t
t <- penult[[t]]
path <- paste(nodes(g)[t],"|",path,sep="")
}
if(is.null(demand$paths[[path]])) {
demand$paths[[path]] <- mincap
} else {
demand$paths[[path]] <- demand$paths[[path]] + mincap
}
demand
}
### Native R version of rg.fleischer.max.concurrent.flow.c()
### Compute the maximum concurrent flow See Fleischer "Approximating
### fractional multicommodity flow independent of the number of
### commodities", SIAM J. Discrete Maths, Vol 13/4, pp 505-520
###
### g: graphNEL (vertex labels need to be same as vertex index)
### demands: vector
###
### g: graphNEL with capacity data on each edge, edge
### weight is ignored (traffic is routed over any way to meet
### concurrent flow
###
### e: accuracy ( 0 < e < 1 ) 0 is more accurate, 1 less accurate
###
### output: list:
### gflow: with weight set to edge flow
### gdual: with weight set to dual edge lengths
### demands: each with met demand and paths and edges
rg.fleischer.max.concurrent.flow <- function(g,demands,e=0.1,updateflow=TRUE, progress=FALSE) {
savedemands <- demands
## note this is not the dual value
calcD <- function() {
sum(as.double(edgeData(gdual,attr="capacity"))*
as.double(edgeData(gdual,attr="weight")))
}
doubleCount <- 0
doubleDemands <- function(demands) {
for(n in names(demands)) {
demands[[n]]$demand <- demands[[n]]$demand * 2
}
doubleCount <- doubleCount + 1
demands
}
gdual <- g
# number of arcs
m <- length(rg.edgeL(g))
# number of nodes
N <- length(nodes(g))
delta <- (m / (1-e)) ^ (-1/e)
capacities <- as.double(edgeData(g,attr="capacity"))
fromedges <- edgeMatrix(g)[1,]
toedges <- edgeMatrix(g)[2,]
edgeData(gdual,
nodes(gdual)[fromedges],
nodes(gdual)[toedges],
"weight") <- delta / capacities
for(c in names(demands)) {
demands[[c]]$paths <- list()
demands[[c]]$flow <- 0
}
doubreq <- ceiling(2/e * log(m/(1-e),base=(1+e)))
## updatepb used to measure progress as percent
updatepb <- as.integer(ceiling(doubreq / 100.0))
if(progress != FALSE)
pb <- txtProgressBar(min = 0,
max = doubreq, style=3)
phases <- 0
totalphases <- 0
D <- calcD()
lambdav <- c()
betav <- c()
while(D < 1 ) {
if(progress != FALSE && totalphases %% updatepb == 0) {
setTxtProgressBar(pb,totalphases)
}
if(phases > doubreq) {
cat("doubling\n");
demands <- doubleDemands(demands)
phases <- 0
}
ccount <- 1
for(c in demands) {
demand <- c$demand
D <- calcD()
while( D < 1 && demand > 0 ) {
sp <- dijkstra.sp(gdual,c$source)
p <- extractPath(which(nodes(gdual)==c$source),
which(nodes(gdual)==c$sink),sp$penult)
caponpath <- as.double(edgeData(gdual,
from=nodes(gdual)[p[1:length(p)-1]],
to=nodes(gdual)[p[2:length(p)]],
attr="capacity"))
mincap <- min(demand,caponpath)
demand <- demand - mincap
lengths <- as.double(edgeData(gdual,
from=nodes(gdual)[p[1:length(p)-1]],
to=nodes(gdual)[p[2:length(p)]],
attr="weight"))
lengths <- lengths * (1 + (e*mincap) / caponpath)
edgeData(gdual,
from=nodes(gdual)[p[1:length(p)-1]],
to=nodes(gdual)[p[2:length(p)]],
attr="weight") <- lengths
if(updateflow)
c <- updateExplicitFlow(gdual,sp$penult,mincap,c)
c$flow <- c$flow + mincap
D <- calcD()
}
demands[[ccount]] <- c
ccount <- ccount + 1
}
phases <- phases +1
totalphases <- totalphases + 1
}
## If there had been demands doubling we need to
## fix things for the returned demands
for(n in names(demands)) {
demands[[n]]$demand <- savedemands[[n]]$demand
}
scalef <- 1 / log(1/delta,base=(1+e))
demands <- rg.max.concurrent.flow.rescale.demands.flow(demands,scalef)
beta <- calcBeta(demands,gdual)
lambda=NULL
if(updateflow) {
lambda <- calcLambda(demands)
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
}
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
retval <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,lambda=lambda,phases=totalphases,e=e)
if(progress != FALSE)
close(pb)
retval
}
### Calculates the maximum concurrent flow as per Fleischer, but with the
### modification that demand paths have been specified
### g - graphNEL with edge attributes capacity and weight (weight not used)
### demands$flow
### demands$paths - list of paths each element list[["1|2|3"]] = flow on path
### only the path is used not the amount of flow that
### may have been calculated on a previous run
### e - the approximation limit
### updateflow - default TRUE, may run a bit faster if FALSE but only
### lambda, bega and demand flow are given (path values not
### updated.
### progress - prints out the value of D() (finishes when D >=1.0)
### returns list:
### demands paths - list of paths each element list[["1|2|3"]] = flow on path
### gflow - graph as g but with weight set to traffic flow
### gdual - the dual graphNEL with weight set to edge lengths
### beta - the dual value
### lambda - the primal value
### phases - the total number of phases
### e - as per the input
rg.fleischer.max.concurrent.flow.restricted <- function(g,demands,e=0.1,updateflow=TRUE, progress=FALSE) {
## this is actually slower than below!
findshortestpathtest <- function(paths,vlength) {
sums <- sapply(paths,function(x) sum(vlength[x]))
return(which.min(sums))
}
findshortestpath <- function(paths,vlength) {
min <- Inf
minp <- NULL
minpcount <- NULL
for(i in 1:length(paths)) {
if ( sum(vlength[paths[[i]]]) < min) {
minp <- paths[[i]]
minpcount <- i
min <- sum(vlength[paths[[i]]])
}
}
return(minpcount)
}
savedemands <- demands
vdemands <- as.double(lapply(demands,"[[","demand"))
vcapacity <- as.double(edgeData(g,attr="capacity"))
vweight <- rep(0.0,length(vcapacity))
vlength <- rep(0.0,length(vcapacity))
vdemandflow <- rep(0.0,length(vcapacity))
L <- length(vlength)
edgeMap <- adjMatrix(g)
demandpaths <- list()
demandpathflows <- list()
j <- 1
for(d in demands) {
demandpaths[[j]] <- list()
demandpathflows[[j]] <- list()
k <- 1
for(p in names(d$paths)) {
pv <- as.vector(strsplit(p,"|",fixed=TRUE)[[1]])
fromlist <- as.integer(pv[1:length(pv)-1])
tolist <- as.integer(pv[2:length(pv)])
me <- rbind(fromlist,tolist)
pv <- c()
for(i in 1:length(fromlist)) {
v <- as.vector(me[,i])
em <- edgeMap[v[1],v[2]]
pv <- append(pv,em)
}
demandpaths[[j]][[k]] <- pv
demandpathflows[[j]][[k]] <- 0.0
k <- k + 1
}
j <- j + 1
}
## note this is not the dual value
calcD <- function() {
sum(vcapacity*vlength)
}
doubleCount <- 0
doubleDemands <- function(demands) {
vdemands <- vdemands * 2.0
doubleCount <- doubleCount + 1
vdemands
}
gdual <- g
## number of arcs
m <- length(rg.edgeL(g))
## number of nodes
N <- length(nodes(g))
delta <- (m / (1-e)) ^ (-1/e)
vlength <- delta / vcapacity
for(c in names(demands)) {
demands[[c]]$paths <- list()
demands[[c]]$flow <- 0
}
doubreq <- ceiling(2/e * log(m/(1-e),base=(1+e)))
## updatepb used to measure progress as percent
updatepb <- as.integer(ceiling(doubreq / 100.0))
if(progress != FALSE)
pb <- txtProgressBar(min = 0,
max = doubreq, style=3)
phases <- 0
totalphases <- 0
D <- calcD()
## main algorithm
while(D < 1 ) {
if(progress != FALSE && totalphases %% updatepb == 0) {
setTxtProgressBar(pb,totalphases)
}
if(phases > doubreq) {
cat("doubling",doubreq,totalphases,"\n");
vdemands <- doubleDemands(vdemands)
phases <- 0
}
for(i in 1:length(vdemands)) {
demand <- vdemands[i]
D <- calcD()
while( D < 1 && demand > 0 ) {
p <- findshortestpath(demandpaths[[i]],vlength)
caponpath <- vcapacity[ demandpaths[[i]][[p]] ]
mincap <- min(demand,caponpath)
demand <- demand - mincap
lengths <- vlength[ demandpaths[[i]][[p]] ]
lengths <- lengths * (1 + (e*mincap) / caponpath)
vlength[ demandpaths[[i]][[p]] ] <- lengths
demandpathflows[[i]][[p]] <- demandpathflows[[i]][[p]] + mincap
vdemandflow[i] <- vdemandflow[i] + mincap
D <- calcD()
}
}
phases <- phases +1
totalphases <- totalphases + 1
}
## end of main algorithm - now need to pack results
## If there had been demands doubling we need to
## fix things for the returned demands
for(n in 1:length(demands)) {
demands[[n]]$demand <- savedemands[[n]]$demand
demands[[n]]$paths <- savedemands[[n]]$paths
demands[[n]]$flow <- vdemandflow[n]
for(i in 1:length(demands[[n]]$paths)) {
demands[[n]]$paths[[i]] <- demandpathflows[[n]][[i]]
}
}
gdual <- rg.set.weight(gdual,vlength)
scalef <- 1 / log(1/delta,base=(1+e))
demands <- rg.max.concurrent.flow.rescale.demands.flow(demands,scalef)
beta <- calcBeta(demands,gdual)
betar <- calcBetaRestricted(demands,gdual)
lambda=NULL
lambda <- calcLambda(demands)
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
retval <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,betar=betar,
lambda=lambda,phases=totalphases,e=e,vlength=vlength)
if(progress != FALSE)
close(pb)
retval
}
### As the R version see above Tested 21/5/2010 performs the same,
### however some rounding differences mean there will be differences
### in actual result from R version. A small rounding error can
### influence the route (shortest path) so differences could be
### substantial, however, lambda and beta should be within the bounds
### set by e
rg.fleischer.max.concurrent.flow.restricted.c <- function(g,demands,e=0.1,updateflow=TRUE,progress=FALSE,bestgamma=-1) {
## note this is not the dual value
calcD <- function() {
sum(vcapacity*vlength)
}
savedemands <- demands
vdemands <- as.double(lapply(demands,"[[","demand"))
vcapacity <- as.double(edgeData(g,attr="capacity"))
vlength <- rep(0.0,length(vcapacity))
vdemandflow <- rep(0.0,length(vcapacity))
L <- length(vlength)
delta <- (L / (1-e)) ^ (-1/e)
vlength <- delta / vcapacity
edgeMap <- adjMatrix(g)
demands.paths <- as.integer(c())
## code the paths as integers
## [demandno(e.g=1) numdempaths lengthpath1 path1[1] path1[2] ...
## lengthpath2 path2[1] path2[2]....demandno(e.g=2)....]
rdemandpaths <- list();
for(d in 1:length(demands)) {
rdemandpaths[[d]] <- list()
demands.paths <- append(demands.paths,d-1)
demands.paths <- append(demands.paths,length(demands[[d]]$paths))
for(p in 1:length(demands[[d]]$paths)) {
pathi <-
as.integer(strsplit
(names(demands[[d]]$paths[p]),"|",fixed=TRUE)[[1]])
pathi <- pathi -1
rdemandpaths[[d]][[p]] <- pathi
demands.paths <- append(demands.paths,length(pathi))
demands.paths <- append(demands.paths,pathi)
}
}
demandpaths <- list()
j <- 1
for(d in demands) {
demandpaths[[j]] <- list()
k <- 1
for(p in names(d$paths)) {
pv <- as.vector(strsplit(p,"|",fixed=TRUE)[[1]])
fromlist <- as.integer(pv[1:length(pv)-1])
tolist <- as.integer(pv[2:length(pv)])
me <- rbind(fromlist,tolist)
pv <- c()
for(i in 1:length(fromlist)) {
v <- as.vector(me[,i])
em <- edgeMap[v[1],v[2]]
pv <- append(pv,em)
}
demandpaths[[j]][[k]] <- pv -1
k <- k + 1
}
j <- j + 1
}
m <- length(rg.edgeL(g))
doubreq <- ceiling(2/e * log(m/(1-e),base=(1+e)))
if(progress != FALSE) {
pb <- txtProgressBar(title = "progress bar", min = 0,
max = doubreq, style=3)
} else {
pb <- NULL
}
retlist <- .Call("rg_fleischer_max_concurrent_flow_restricted_c",
demandpaths,
vdemands,
vcapacity,
e,
progress,
pb,
bestgamma,
environment()
);
if( progress != FALSE) {
close(pb)
}
## now need to unpack results
demands <- savedemands
for(n in 1:length(demands)) {
flow <- 0
for(i in 1:length(demands[[n]]$paths)) {
flow <- flow + retlist$pathflows[[n]][[i]]
demands[[n]]$paths[[i]] <- retlist$pathflows[[n]][[i]]
}
demands[[n]]$flow <- flow
}
gdual <- g
gdual <- rg.set.weight(gdual,retlist$vlengths)
scalef <- 1 / log(1/delta,base=(1+e))
demands <- rg.max.concurrent.flow.rescale.demands.flow(demands,scalef)
beta <- calcBeta(demands,gdual)
betar <- calcBetaRestricted(demands,gdual)
lambda=NULL
lambda <- calcLambda(demands)
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
retval <- list(demands=demands,gflow=gflow,gdual=gdual,beta=beta,betar=betar,
lambda=lambda,phases=retlist$totalphases,e=e,vlength=retlist$vlengths,
countgamma=retlist$countgamma,
bestgamma=retlist$bestgamma,
bestpaths=retlist$bestpaths+1)
return(retval)
}
rg.max.concurrent.flow.analyse <- function(res) {
demands <- res$demands
for(d in demands) {
numpaths = length(d$paths)
cat("demand source =",d$source,"sink =",d$sink,"paths = ",numpaths,"\n")
}
}
rg.fleischer.max.concurrent.flow.stats <- function(reslist) {
gdual <- reslist$gdual
D <- sum(as.double(edgeData(gdual,attr="capacity"))*
as.double(edgeData(gdual,attr="weight")))
cat("D=",D,"\n",sep="")
cat("in",reslist$phases,"phases\n")
e <- reslist$e
beta <- calcBeta(reslist$demands,gdual)
cat("beta=",beta,"\n",sep="")
lambda <- calcLambda(reslist$demands)
cat("lambda=",lambda,"\n",sep="")
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
cat("Ratio dual/primal found =",foundratio,
" Ratio should be less than =",ratiobound,"\n",sep="")
m <- length(rg.edgeL(gdual))
doubreq <- 2/e * log(m/(1-e),base=(1+e))
if(doubreq < reslist$phases)
cat("Doubling was required!\n")
}
### Rescale the demands by scalef
rg.max.concurrent.flow.rescale.demands.flow <- function(demands,scalef) {
for(dn in names(demands)) {
demands[[dn]]$flow <- demands[[dn]]$flow * scalef
for(en in names(demands[[dn]]$paths)) {
demands[[dn]]$paths[[en]] <- demands[[dn]]$paths[[en]] * scalef
}
}
demands
}
rg.rescale.demands <- function(demands,scalef,integer=FALSE) {
count <- 1
newdemands <- list()
for(dn in names(demands)) {
if(integer) {
# careful here, rounding to down to zero causes unexpected results!
# hence do not include demands with zero
if(rbinom(1,1,demands[[dn]]$demand %% 1)) {
demands[[dn]]$demand <- ceiling(demands[[dn]]$demand * scalef)
} else {
demands[[dn]]$demand <- floor(demands[[dn]]$demand * scalef)
}
if(demands[[dn]]$demand > 0) {
newdemands[[count]] <- demands[[dn]]
count <- count + 1
}
} else {
demands[[dn]]$demand <- demands[[dn]]$demand * scalef
}
}
if(integer)
return(newdemands)
else
return(demands)
}
rg.set.all.graph.edge.weights <- function(g,val=0.0) {
fromedges <- edgeMatrix(g)[1,]
toedges <- edgeMatrix(g)[2,]
nodes=nodes(g)
edgeData(g,
nodes[fromedges],
nodes[toedges],
"weight") <- val
g
}
### Utililty function used by rg.fleischer.max.concurrent.flow
### given a graph with no edge weights set, set the edge
### weights given edge flows in demand
###
### graph - GraphNEL with empty edge weights (or ignored if they are set)
### demands - each demand path value used to update graph edge weight
### return - new graph with edge weights set
rg.max.concurrent.flow.graph <- function(g,demands) {
g <- rg.set.all.graph.edge.weights(g)
for(d in demands) {
for(p in names(d$paths)) {
pv <- as.vector(strsplit(p,"|",fixed=TRUE)[[1]])
fromlist <- pv[1:length(pv)-1]
tolist <- pv[2:length(pv)]
weights <- as.double(edgeData(g,from=fromlist,
to=tolist,att="weight"))
weights <- weights + d$paths[[p]]
edgeData(g,from=fromlist,
to=tolist,
att="weight") <- weights
}
}
g
}
### Calculate the gamma of a graph (minimum proportion of free edge capacity)
### gflow - GraphNEL with edge attr weight and capacity set
### lambda - defaults to 1 else divide weight by lambda
### this is useful for output of max concurrent flow
### use lambda=1 for gflow from minimim.congestion.flow
rg.mcf.find.gamma <- function(gflow,lambda=1.0) {
flows <- as.double(edgeData(gflow,attr="weight"))/lambda
capacities <- as.double(edgeData(gflow,attr="capacity"))
gamma <- min ( (capacities - flows)/ capacities)
gamma
}
### Compute the minimum congestion flow using the max concurrent flow
### and rescale demands by 1/lambda
### g - GraphNEL with attr capacity
### demands - list with source, sink, demand
### e - approximation 0 < e < 1.0
### progress - bool show progress bar default FALSE
### return list
### gflow - GraphNEL with attr weight set
### demands - list with paths and edges of solution
### gamma - minimum proportion of free edge capacity
### NOTE TWO VERSIONS! this one was the original, but now c code does prescaling!
rg.minimum.congestion.flow.test <- function(g,demands,e=0.1,progress=FALSE,permutation="random",deltaf=1.0,ccode=TRUE) {
if(!isDirected(g)) {
print("g must be directed")
return(NULL)
}
res <- rg.max.concurrent.flow.prescaled(g,demands,e,progress=progress,ccode,permutation=permutation,deltaf=1.0)
res$demands <- rg.max.concurrent.flow.rescale.demands.flow(res$demands,1/res$lambda)
res$gflow <- rg.max.concurrent.flow.graph(res$gflow,res$demands)
res$gamma <- rg.mcf.find.gamma(res$gflow)
res
}
rg.minimum.congestion.flow <- function(g,demands,e=0.1,progress=FALSE,permutation="random",deltaf=1.0,ccode=TRUE) {
if(!isDirected(g)) {
print("g must be directed")
return(NULL)
}
res <- rg.fleischer.max.concurrent.flow.c(g,demands,e,progress=progress,ccode,permutation=permutation,deltaf=1.0)
res$demansunscaled <- res$demands
res$demands <- rg.max.concurrent.flow.rescale.demands.flow(res$demands,1/res$lambda)
res$gflow <- rg.max.concurrent.flow.graph(res$gflow,res$demands)
res$gamma <- rg.mcf.find.gamma(res$gflow)
res
}
### Truncated quantile function for an arbitrary distribution
### spec = distribution name
### a = negative truncation (must be greater than this)
### b = positive truncation (must be less/= to this)
### .... arbitrary arguments to send to the distribution
qtrunc <- function(p, spec, a = -Inf, b = Inf, ...)
{
tt <- p
G <- get(paste("p", spec, sep = ""), mode = "function")
Gin <- get(paste("q", spec, sep = ""), mode = "function")
tt <- Gin(G(a, ...) + p*(G(b, ...) - G(a, ...)), ...)
return(tt)
}
### Random number generator using a random distribution which
### is optionally truncated
### spec = name of distribution
### a = lower truncation
### b = upper truncation
### ... arguments to be sent to probability distibution
rtrunc <- function(n, spec, a = -Inf, b = Inf, ...)
{
x <- u <- runif(n, min = 0, max = 1)
x <- qtrunc(u, spec, a = a, b = b,...)
return(x)
}
### Generate a random graph with a range of node degree
### uses igraph routines
### n - number of nodes
### mindeg - minimum node degree default = 2
### maxdeg - maximum node degree default = 20
### dist - the name of the distrubution for the outdegree
### dist1 - the first parameter used by dist
### dist2 - the second parameter used by dist
### return GraphNEL
rg.generate.random.graph <- function(n=10,mindeg=2,maxdeg=20,
dist="weibull",
dist1=0.42,dist2=1,retry=10) {
if(n< maxdeg -1) {
maxdeg=n-2
}
## note this needs a bit more work - not quite right
## as the truncation gives an error - but good enough
## for generating some "random" graphs
if(maxdeg==Inf)
degrees <- ceiling(dist(n,dist1,dist2))
else
degrees <- floor(rtrunc(n,dist,a=mindeg,b=maxdeg,dist1,dist2))
## note sum(degrees) has to be even, add one to smallest
## node degree of first node found.
if(sum(degrees) %% 2 != 0) {
minval <- min(degrees)
pos <- match(minval,degrees)
degrees[pos] <- degrees[pos] +1
}
gi <- NULL
tryCatch(gi <- degree.sequence.game(degrees,method="vl"),
error=function(e) { print("trying graph generation again") })
if(is.null(gi)) {
for(i in 1:10) {
degrees <- floor(rtrunc(n,dist,a=mindeg,b=maxdeg,dist1,dist2))
## note sum(degrees) has to be even, add one to smallest
## node degree of first node found.
if(sum(degrees) %% 2 != 0) {
minval <- min(degrees)
pos <- match(minval,degrees)
degrees[pos] <- degrees[pos] +1
}
tryCatch(gi <- degree.sequence.game(degrees,method="vl"),
error=function(e) { print("trying graph generation again") })
if(!is.null(gi)) break
}
}
gi <- as.directed(gi)
# Finally convert to RBGL - my preferred library for graphs
G <- rg.relabel(igraph.to.graphNEL(gi))
return(G)
}
rg.augment.graph.for.wavelengths <- function # Augment graph for wavelengths
### Augments a directed graph with a copy for each wavelength. Source and sink
### nodes are created to attach the demands to.
(g, ##<< graphNEL object to augment
count ##<< number of wavelengths (same on each link)
) {
## create as many graph as there are wavelengths as simple copies,
## they are not connected at the moment
edgeL <- list()
nodeL <- c()
linkgroupmap <- list()
for( i in 1:count ) {
inedgeL <- mapply(paste0,graph::edges(g),MoreArgs=list("L",i),SIMPLIFY=FALSE)
innodeL <- as.vector(paste0(nodes(g),"L",i))
names(inedgeL) <- innodeL
edgeL <- append(edgeL,inedgeL)
nodeL <- append(nodeL,innodeL)
}
edgenames <- names(edgeData(g))
for(e in edgenames) {
for( i in 1:count ) {
ed <- sapply(strsplit(e,"\\|"),function(x) paste0(x,"L",i))[,1]
circuit.name <- paste0(ed[[1]],"|",ed[2])
linkgroupmap[[circuit.name]] <- e
}
}
#nodeL <- append(nodeL,nodes(g))
ga <- new("graphNEL",nodeL,edgeL,"directed")
edgeDataDefaults(ga,attr="capacity") <- 1.0
ga <- rg.set.weight(ga,1.0)
ga <- rg.set.capacity(ga,1.0)
## now connect the graphs to nodes of the same name as the original nodes
## but with nodes split into source and sink by appending s and t to names
ga <- addNode(as.vector(paste0(nodes(g),"s")),ga)
ga <- addNode(as.vector(paste0(nodes(g),"t")),ga)
for(v in nodes(g)) {
for( i in 1:count ) {
node <- paste0(v,"L",i)
ga <- addEdge(paste0(v,"s"),node,ga)
edgeData(ga,paste0(v,"s"),node,attr="capacity") <- as.integer(graph::degree(g,v)$inDegree)
ga <- addEdge(node,paste0(v,"t"),ga)
edgeData(ga,node,paste0(v,"t"),attr="capacity") <- as.integer(graph::degree(g,v)$inDegree)
}
}
return(list(ga=ga,linkgroupmap=linkgroupmap))
### graphNEL object augmented with count copies of the original
### graph, an original node n for the wavelength l is labelled as
### nLl. Additional nodes nt and ns are created for the source/sink
### at the orignal node n. Now we have edges ns -> nLl and nLl -> nt
### as well as the original edges between the nodes (albeit in
### multiple copies of the graph) This is better understood using a
### picture...
}
### Calculate the path cost in the a graph
### gdual - GraphNEL edge attr weight used for path cost
### path - path as "|" separated nodes e.g. "14|7|5"
### return - path cost as double
rg.path.cost <- function(gdual,path) {
pv <- as.vector(strsplit(path,"|",fixed=TRUE)[[1]])
from <- pv[1:length(pv)-1]
to <- pv[2:length(pv)]
cost <- sum(as.double(edgeData(gdual,from=from,to=to,att="weight")))
return(cost)
}
### Obtain the edge attribute along the path cin the a graph
### g - GraphNEL
### path - path as "|" separated nodes e.g. "14|7|5"
### return - attribute vector same length as path
rg.path.attr <- function(g,path,attr="weight") {
pv <- as.vector(strsplit(path,"|",fixed=TRUE)[[1]])
from <- pv[1:length(pv)-1]
to <- pv[2:length(pv)]
return(as.double(edgeData(g,from=from,to=to,att=attr)))
}
### Attempt to calculate integer min congestion flow
### does not work!
### res - output from rg.integer.min.congestion.flow.ga()
rg.integer.iterative <- function(res) {
dem <- rg.max.concurrent.flow.rescale.demands.flow(res$splitflow$demands,res$splitflow$lambda)
g <- res$splitflow$gflow
e <- res$splitflow$e
fromedges <- edgeMatrix(g)[1,]
toedges <- edgeMatrix(g)[2,]
edgeData(g,
as.character(fromedges),
as.character(toedges),
"weight") <- 0.0
outdem <- list()
demsav <- dem
gdual <- res$splitflow$gdual
gflow <- rg.max.concurrent.flow.graph(res$splitflow$gflow,dem)
beta <- calcBeta(dem,gdual)
betaa <- calcBetaRestricted(dem,gdual)
lambda <- calcLambda(dem)
gap <- lambda / beta
mgap <- (1-res$splitflow$e)^3
cat("Initial: beta=",beta,betaa,"lambda=",lambda,"gap=",gap,"mgap=",mgap,"\n")
multFlows <- FALSE
for(d in names(dem)) {
if(length(dem[[d]]$paths) > 1 ) {
multFlows <- TRUE
break
}
}
for(d in names(dem)) {
dem[[d]]$original <- d
dem[[d]]$paths <- NULL
}
while(length(dem) > 0) {
## calculate mcf
## browser()
mcf <- rg.fleischer.max.concurrent.flow.c(g,dem,e)
demands <- mcf$demands
gdual <- mcf$gdual
lowp <- ""
val <- Inf
lowd <- ""
for(d in names(demands)) {
if(length(demands[[d]]$paths) > 1 ) {
for(p in names(demands[[d]]$paths)) {
cost <- rg.path.cost(gdual,p)
if (val > cost) {
val <- cost
lowp <- p
lowd <- d
}
flow <- demands[[d]]$paths[[p]]
}
}
}
cat("lowest demand=",lowd,"lowest path=",lowp,"\n")
## nail chosen demand on chosen path of output dem
orignum <- dem[[lowd]]$original
outdem[[orignum]]$source <- dem[[lowd]]$source
outdem[[orignum]]$sink <- dem[[lowd]]$sink
outdem[[orignum]]$demand <- dem[[lowd]]$demand
outdem[[orignum]]$paths <- list()
outdem[[orignum]]$paths[[lowp]] <- dem[[lowd]]$demand
## DO WE NEED TO FILL IN EDGES AS WELL?
## update graph
pv <- as.vector(strsplit(lowp,"|",fixed=TRUE)[[1]])
fromlist <- pv[1:{length(pv)-1}]
tolist <- pv[2:{length(pv)}]
# print(fromlist)
# print(tolist)
oldvals <- as.double(edgeData(g,
as.character(fromlist),
as.character(tolist),
"capacity"))
newvals <- oldvals - outdem[[orignum]]$demand
if( min(newvals) < 0 ) {
print("WARNING SET EDGE LESS THAN ZERO")
}
## remove from working demands
dem[[lowd]] <- NULL
if (length(dem) > 0) {
names(dem) <- as.character(1:length(dem))
}
}
gflow <- rg.set.all.graph.edge.weights(res$splitflow$gflow)
for(d in names(outdem)) {
p <- names(outdem[[d]]$paths[1])
pathv <- strsplit(p,"|",fixed=TRUE)
from <- pathv[[1]][1:length(pathv[[1]])-1]
to <- pathv[[1]][2:length(pathv[[1]])]
edgeData(gflow,from=from,to=to,attr="weight") <-
as.double(edgeData(gflow,from=from,to=to,attr="weight")) + outdem[[d]]$demand
}
ga.gamma <- rg.mcf.find.gamma(res$gflow)
gamma <- rg.mcf.find.gamma(gflow)
cat("ga.gamma=",ga.gamma,"gamma=",gamma,"\n")
res$demands <- outdem
res$gflow <- gflow
return(res)
}
### Attempt to calculate integer min congestion flow
### does not work!
### res - output from rg.integer.min.congestion.flow.ga()
rg.integer.rounding <- function(res) {
dem <- rg.max.concurrent.flow.rescale.demands.flow(res$splitflow$demands,res$splitflow$lambda)
gdual <- res$splitflow$gdual
gflow <- rg.max.concurrent.flow.graph(res$splitflow$gflow,dem)
beta <- calcBeta(dem,gdual)
betaa <- calcBetaRestricted(dem,gdual)
lambda <- calcLambda(dem)
gap <- lambda / beta
mgap <- (1-res$splitflow$e)^3
cat("Initial: beta=",beta,betaa,"lambda=",lambda,"gap=",gap,"mgap=",mgap,"\n")
multFlows <- FALSE
for(d in names(dem)) {
if(length(dem[[d]]$paths) > 1 ) {
multFlows <- TRUE
break
}
}
while(multFlows) {
lowp <- ""
val <- Inf
lowd <- ""
for(d in names(dem)) {
if(length(dem[[d]]$paths) > 1 ) {
data <- data.frame(Paths=character(0),cost=numeric(0),flow=numeric(0),highest=numeric(0))
count <- 1
lowc <- 1
for(p in names(dem[[d]]$paths)) {
cost <- rg.path.cost(gdual,p)
if (val > cost) {
val <- cost
lowp <- p
lowc <- count
lowd <- d
}
flow <- dem[[d]]$paths[[p]]
## data <- rbind(data,data.frame(Paths=p,cost=cost,flow=flow,highest=0))
count <- count + 1
}
## data[[lowc,4]] <- 1
## data <- data[do.call(order,data[3]),]
## print(data)
}
}
cat("lowest demand=",lowd,"lowest path=",lowp,"\n")
## work out where to put flow
## the one that make gamma highest
gt <- Inf
gp <- ""
lowestload <- 0
free <- 0
for(p in names(dem[[lowd]]$paths)) {
if (p != lowp) {
pv <- as.vector(strsplit(p,"|",fixed=TRUE)[[1]])
from <- pv[1:length(pv)-1]
to <- pv[2:length(pv)]
# load <- as.double(edgeData(gflow,from=from,to=to,att="weight"))
# capacities <- as.double(edgeData(gflow,from=from,to=to,att="capacity"))
# print(p)
maxratio <- 0
free <- 0
for(i in seq(1:length(from))) {
# cat(from[i],to[i],"\n")
load <- as.double(edgeData(gflow,from=from[i],to=to[i],att="weight"))
capacity <- as.double(edgeData(gflow,from=from[i],to=to[i],att="capacity"))
if (load/capacity > maxratio) {
maxratio <- load/capacity
free <- capacity - load
# if (free < 0) {
# print("Error free less than zero")
# return(0)
# }
}
}
#lowestload <- max(load/capacities)
lowestload <- maxratio
if(lowestload < gt) {
gt <- lowestload
gp <- p
}
}
}
## update flow
lowflow <- dem[[lowd]]$paths[[lowp]]
addval <- lowflow
# if (addval > free) {
# addval <- free
# dem[[lowd]]$flow <- dem[[lowd]]$flow - lowflow + addval
#
# }
## first add flow to lowest constrained path in demand
pv <- as.vector(strsplit(gp,"|",fixed=TRUE)[[1]])
from <- pv[1:length(pv)-1]
to <- pv[2:length(pv)]
weights <- as.double(edgeData(gflow,
from=from,
to=to,
attr="weight"))
dem[[lowd]]$paths[[gp]] <- dem[[lowd]]$paths[[gp]] + addval
weights <- weights + addval
edgeData(gflow,
from=from,
to=to,
attr="weight") <- weights
## Now take it away from the one that was the lowest path cost
pv <- as.vector(strsplit(lowp,"|",fixed=TRUE)[[1]])
from <- pv[1:length(pv)-1]
to <- pv[2:length(pv)]
weights <- as.double(edgeData(gflow,
from=from,
to=to,
attr="weight"))
weights <- weights - lowflow
edgeData(gflow,
from=from,
to=to,
attr="weight") <- weights
dem[[lowd]]$paths[[lowp]] <- NULL
## update lengths on the lowest path cost
## lengths <- lengths * (1 + (e*mincap) / caponpath)
if(FALSE) {
lengths <- as.double(edgeData(gdual,
from=from,
to=to,
attr="weight"))
caponpath <- as.double(edgeData(gdual,
from=from,
to=to,
attr="capacity"))
lengths <- lengths * ( 1 + addval/caponpath)
edgeData(gdual,
from=from,
to=to,
attr="weight") <- lengths
}
## calculate beta, gamma and lambda
beta <- calcBetaRestricted(dem,gdual)
lambda <- calcLambda(dem)
gap <- lambda / beta
mgap <- (1-res$splitflow$e)^3
cat("beta=",beta,"lambda=",lambda,"gap=",gap,"mgap=",mgap,"\n")
multFlows <- FALSE
for(d in names(dem)) {
if(length(dem[[d]]$paths) > 1 ) {
multFlows <- TRUE
break
}
}
}
for(d in names(dem)) {
dem[[d]]$flow <- dem[[d]]$demand
dem[[d]]$paths[1] <- dem[[d]]$demand
}
gflow <- rg.max.concurrent.flow.graph(gflow,dem)
load <- as.double(edgeData(gflow,attr="weight"))
capacity <- as.double(edgeData(gflow,attr="capacity"))
print(load/capacity)
gamma(1- max(load/capacity))
dem$gflow <- gflow
return(dem)
}
### Working function to calculate stats from a run of results
### from rg.integer.min.congestion.flow.ga()
### path - file path
rg.test.idea.stats <- function(path) {
files <- Sys.glob(paste(path,"run[0-9]*",sep=""))
files <- files[order(as.numeric(gsub("^.*run([0-9]*).*$","\\1",files,files)))]
for(file in files) {
load(file)
e <- res$splitflow$e
n <- length(nodes(res$splitflow$gflow))
nd <- length(res$demands)
lambda <- res$splitflow$lambda
gamma <- res$splitflow$gamma
capacities <- as.double(edgeData(res$splitflow$gflow,attr="capacity"))
dual <- res$splitflow$gdual
lengths <- as.double(edgeData(dual,attr="weight"))
lengths <- lengths / (1 - gamma)
dual <- rg.set.all.graph.edge.weights(dual,lengths)
numpaths <- 0
for( i in lapply(res$splitflow$demands,"[[","paths")) {
numpaths <- numpaths + length(names(i))
}
scap <- capacities / lambda
min <- 1- max( scap /capacities)
igamma <- rg.mcf.find.gamma(res$gflow)
minEval <- min(res$ga$evaluations)
beta <- res$splitflow$beta
# cat("n=",n,"e=",e,"nd=",nd,"lambda=",lambda,"\n")
print(file)
cat("n=",n,"nd=",nd,"beta=",beta,"lambda=",lambda,"gamma=",igamma,"numpaths=",numpaths,"\n")
# cat("maxl=",maxl,"lambda=",lambda,"lambda2",res2$lambda,"gamma=",gamma,"igamma=",igamma,"min=",min,"\n")
}
}
### Solve the integer min congestion flow using a GA
### uses the rg.minimum.congestion.flow() to give starting
### value. GA permutes various paths from this non-integer solution
### rather than using "all possible paths".
### uses rbga.bin() from the genalg package
### g - GraphNEL attr capacity set
### demands - list with source, sink, demand set for each demand
### e - approximation value used for the internal rg.minimum.congestion.flow()
### return - list
### - gflow integer flow GraphNEL with attr weight set
### - splitflow output from rg.minimum.congestion.flow
### - val 1- minEval (minEval is minimum ga evaluation)
### - ga output from rbga.bin
### - demands integer solution with paths set (but not edges)
rg.integer.min.congestion.flow.ga <- function(g,demands,e=0.1) {
monitor <- function(obj) {
## minEval = min(1/obj$evaluations);
## plot(obj, type="hist");
print(min(obj$evaluations))
}
evaluate <- function(string=c()) {
chromasome$string <- string
paths <- rg.demands.paths.from.chromasome(chromasome)
g <- rg.set.all.graph.edge.weights(chromasome$g)
j <- 1
valid <- TRUE
for(p in paths) {
demands[[j]]$paths <- list()
demands[[j]]$paths[[p]] <- demands[[j]]$demand
if ( p == "NULL" ) {
valid <- FALSE
break
} else {
d <- chromasome$demands[[j]]$demand
j <- j + 1
pathv <- strsplit(p,"|",fixed=TRUE)
from <- pathv[[1]][1:length(pathv[[1]])-1]
to <- pathv[[1]][2:length(pathv[[1]])]
edgeData(g,from=from,to=to,attr="weight") <-
as.double(edgeData(g,from=from,to=to,attr="weight")) + d
}
}
if (valid) {
load <- as.double(edgeData(g,attr="weight"))
capacity <- as.double(edgeData(g,attr="capacity"))
val <- max(load / capacity)
} else {
val <- Inf
}
val
}
cat("Calculating split flow\n")
splitflow <- rg.minimum.congestion.flow(g,demands,e,progress=FALSE)
chromasome <- rg.demands.paths.as.chromasome(splitflow$demands)
chromasome$g <- g
res <- list()
cat("Starting GA\n")
res$ga <- rbga.bin(size=chromasome$totallength,
popSize=200,
iters=100,
mutationChance=1/(chromasome$totallength + 1),
elitism=as.integer(chromasome$totallength * 0.2),
evalFunc=evaluate,
##monitorFunc=monitor,
verbose=TRUE)
cat("Done GA\n")
save(list=ls(all=TRUE),file="rg.integer.min.congestion.flow.ga.robj")
minEval <- min(res$ga$evaluations)
filter <- res$ga$evaluations == minEval
bestSolutions <- unique(res$ga$population[filter,])
res$bestSolutions <- bestSolutions
res$splitflow <- splitflow
chromasome$string <- bestSolutions[1,]
paths <- rg.demands.paths.from.chromasome(chromasome)
g <- rg.set.all.graph.edge.weights(g)
j <- 1
valid <- TRUE
for(p in paths) {
demands[[j]]$paths <- list()
demands[[j]]$paths[[p]] <- demands[[j]]$demand
j <- j + 1
}
res$demands <- demands
gflow <- rg.integer.min.congestion.flow.ga.graph(bestSolutions[1,],splitflow)
res$gflow <- gflow
res$val <- 1 - minEval
res
}
### Utility function used by rg.integer.min.congestion.flow.ga()
### generates a blank chromasome with gene lengths correct
### for the demands paths
### demands - input demands list from max concurrent flow
### paths is converted to chromasome (each a gene)
### return - chromasome each gene represents one of a possible
### path for each demand. Gene length has to be power
### of two - so might be more bits than paths
rg.demands.paths.as.chromasome <- function(demands) {
chromasome <- list()
chromasome$numgenes <- length(demands)
chromasome$genelengths <- c()
chromasome$pathlengths <- c()
chromasome$string <- c()
j <- 1
totallength <- 0
for(i in demands) {
chromasome$pathlengths[j] <- length(i$paths)
chromasome$genelengths[j] <- ceiling(log(length(i$paths),2))
totallength <- totallength + ceiling(log(length(i$paths),2))
j <- j + 1
}
chromasome$totallength <- totallength
chromasome$demands <- demands
chromasome
}
### Utility function used by rg.integer.min.congestion.flow.ga()
### given a populated chromasome return the paths represented
### by this chromasome
### chromosome - chromasome generated using rg.demands.paths.as.chromasome()
### chromasome$string populated randomly by rbga.bin
### return - list each element a path (for each demand)
rg.demands.paths.from.chromasome <- function(chromasome) {
start <- 1
j <- 1
paths <- list()
for(i in names(chromasome$demands)) {
finish <- chromasome$genelengths[j] + start - 1
gene <- chromasome$string[start:finish]
pathselector <- rg.binary.to.integer(gene) + 1
## possible problem here, once got
## "Error in if (pathselector <= chromasome$pathlengths[j]) { :
## missing value where TRUE/FALSE needed"
##cat("pathselector",pathselector,"\n")
if ( pathselector <= chromasome$pathlengths[j] ) {
paths[[i]] <- names(chromasome$demands[[i]]$paths)[[pathselector]]
} else {
paths[[i]] <- "NULL"
}
start <- finish + 1
j <- j + 1
}
paths
}
### Utility function used by rg.demands.paths.from.chromasome()
### string - a vector of binary digits
### return - value of vector of binary digits as an integer
rg.binary.to.integer <- function(string=c(0)) {
sum(string*2^(rev(seq(along.with=string))-1))
}
### Utility function used by rg.integer.min.congestion.flow.ga()
### given a chromasome string and the output from rg.minimum.congestion.flow()
### calculate the graph represented by the string.
### string - a chromasome$string - string is generated by rbga.bin()
### split - the output from rg.minimum.congestion.flow() which
### supplies the demand paths and graph
### return - GraphNEL with attr weight set with integer flows for the demand
rg.integer.min.congestion.flow.ga.graph <- function(string,split) {
chromasome <- rg.demands.paths.as.chromasome(split$demands)
chromasome$string <- string
paths <- rg.demands.paths.from.chromasome(chromasome)
g <- rg.set.all.graph.edge.weights(split$gflow)
j <- 1
for(p in paths) {
d <- chromasome$demands[[j]]$demand
j <- j + 1
pathv <- strsplit(p,"|",fixed=TRUE)
from <- pathv[[1]][1:length(pathv[[1]])-1]
to <- pathv[[1]][2:length(pathv[[1]])]
edgeData(g,from=from,to=to,attr="weight") <-
as.double(edgeData(g,from=from,to=to,attr="weight")) + d
}
g
}
rg.count.accepted.demands <- function # Count the number of accepted demands
### Note that due to numerical accuracy comparing flow == demand
### is potentially risky. This actually checks if flow >= demand - 1e-03 to
### allow for rounding errors. This limit can be set
(demands, ##<< demands to check
limit=1e-03 ##<< tolerance on equality of flow == demand to allow for rounding errors
) {
accepted <- sapply(demands,function(i) {i$flow >= i$demand - limit})
return(length(accepted[accepted==TRUE]))
### number of accepted demands
}
### Returns a graph with number of flows in each edge
rg.count.edge.flows <- function(g,demands) {
for(i in 1:length(demands)) {
for(j in names(demands[[i]]$paths)) {
##results$demands[[i]]$paths <- 1.0
demands[[i]]$paths[[j]] <- 1.0
}
}
gcount <- rg.max.concurrent.flow.graph(g,demands)
return(gcount)
}
### Calculate the integer minimum congestion flow. It uses the max
### concurrent flow (fractional) to try various combinations and records the best
### NOTE! Probably better to use rg.max.concurrent.flow.int() instead
### if you want to find the best integer flows that meet the maximum
### capacity. This function will overbook traffic (for negative gamma and lambda)
rg.max.concurrent.flow.int.c <- function # Integer minimum congestion flow
(g, ##<< graphNEL to optimise
demands, ##<< demands in a list ("1"=list("source","sink","demand","flow","paths")
## paths is a list of paths from a fractional flow result
e=0.1, ##<< approximation limit 0 means exact, infinitely slow, 1.0 inexact but fast
## best to choose a value between 0.05-0.1
eInternal=NULL, ##<< change e value used internally, best left NULL
updateflow=TRUE, ##<< update the flow (slightly fast if FALSE, but not useful!)
progress=FALSE, ##<< create a progress bar, not working in this version
scenario=NULL, ##<< use a previous scenario to seed the paths used to attempt
permutation="random", ##<< how the demands are chosen can be
## either c(....) integers specifying demand order
## or "fixed" done in fixed order
## "random" done in random order
## "lowest" done in lowest cost (lowest dual path) order
deltaf=1.0, ##<< fix the update to start with a higher value to try speeding up
## best left alone, but if used make it higher than 1
linkgroupmap=NULL ##<< used to group links which are multiple wavelengths on a fibre
## not working yet, leave NULL
) {
## This is a bit of a mess. RBGL only understands indexes for nodes
## so all the names need to be mapped to indices. This is for nodes, edges
## and the linkgroup map. They will be remapped at the end.
link2linkgroup <- c(-1)
linkgroupcap <- c(-1)
if(!is.null(linkgroupmap)) {
link2name <- names(edgeData(g))
linkgroup2name <- unique(as.character(linkgroupmap))
link2linkgroup <- rep(NA,length(link2name))
linkgroupcap <- rep(0.0,length(linkgroup2name))
for(n in names(linkgroupmap)) {
i <- linkgroupmap[[n]]
linkgroup <- which(linkgroup2name == i)
edgepair <- strsplit(n,"\\|")[[1]]
linkgroupcap[linkgroup] <- linkgroupcap[linkgroup] +
as.double(edgeData(g,from=edgepair[[1]],to=edgepair[[2]],attr="capacity"))
link2linkgroup[which(link2name == n)] <-
linkgroup
}
link2linkgroup[is.na(link2linkgroup)] <- 0
## Not sure about below!
link2linkgroup <- link2linkgroup - 1
}
nodelabels <- nodes(g)
demands <- rg.demands.relable.to.indices(demands,nodelabels)
g <- rg.relabel(g)
## OK all the relabeling is done, phew
## note this is not the dual value
calcD <- function() {
sum(vcapacity*vlength)
}
if(is.null(eInternal))
eInternal <- e
if(is.numeric(permutation))
permutation <- permutation - 1
else if(permutation == "fixed")
permutation <- 0:(length(demands)-1)
else if(permutation == "random")
permutation <- -1
else if(permutation == "lowest")
permutation <- -2
savedemands <- demands
vdemands <- as.double(lapply(demands,"[[","demand"))
vcapacity <- as.double(edgeData(g,attr="capacity"))
vlength <- rep(0.0,length(vcapacity))
vdemandflow <- rep(0.0,length(vcapacity))
L <- length(vlength)
delta <- deltaf * (L / (1-e)) ^ (-1/e)
vlength <- delta / vcapacity
edgeMap <- adjMatrix(g)
demands.paths <- as.integer(c())
bestpaths <- as.integer(c())
rdemandpaths <- list();
if(!is.null(scenario)) {
bestgamma=scenario$intgammalist[[1]]
for(d in 1:length(demands)) {
rdemandpaths[[d]] <- list()
demands.paths <- append(demands.paths,d-1)
demands.paths <- append(demands.paths,length(demands[[d]]$paths))
missing <- TRUE
for(p in 1:length(demands[[d]]$paths)) {
pathi <-
as.integer(strsplit
(names(demands[[d]]$paths[p]),"|",fixed=TRUE)[[1]])
pathi <- pathi -1
if(identical(names(demands[[d]]$paths[p]),
names(scenario$intdemands[[d]]$paths))) {
cat("best path in",d,names(scenario$intdemands[[d]]$paths),", ",p,"\n")
missing <- FALSE
bestpaths[d] <- p
}
rdemandpaths[[d]][[p]] <- pathi
demands.paths <- append(demands.paths,length(pathi))
demands.paths <- append(demands.paths,pathi)
}
if(missing)
cat("best path in",d,"is MISSING!\n")
}
print(bestpaths)
} else {
bestgamma <- Inf
}
## code the paths as integers
## [demandno(e.g=1) numdempaths lengthpath1 path1[1] path1[2] ...
## lengthpath2 path2[1] path2[2]....demandno(e.g=2)....]
demandpaths <- list()
j <- 1
for(d in demands) {
demandpaths[[j]] <- list()
k <- 1
for(p in names(d$paths)) {
pv <- as.vector(strsplit(p,"|",fixed=TRUE)[[1]])
fromlist <- as.integer(pv[1:length(pv)-1])
tolist <- as.integer(pv[2:length(pv)])
me <- rbind(fromlist,tolist)
pv <- c()
for(i in 1:length(fromlist)) {
v <- as.vector(me[,i])
em <- edgeMap[v[1],v[2]]
pv <- append(pv,em)
}
demandpaths[[j]][[k]] <- pv -1
k <- k + 1
}
j <- j + 1
}
m <- length(rg.edgeL(g))
# old for ori
# doubreq <- 2 * ceiling(1/e * log(m/(1-e),base=(1+e)))
doubreq <- 2* ceiling(log(1.0/delta,1+eInternal))
if(progress != FALSE) {
pb <- txtProgressBar(title = "progress bar", min = 0,
max = doubreq, style=3)
} else {
pb <- NULL
}
retlist <- .Call("rg_max_concurrent_flow_int_c",
demandpaths,
vdemands,
vcapacity,
e,
eInternal,
progress,
pb,
bestgamma,
environment(),
permutation,
deltaf,
link2linkgroup,
linkgroupcap
);
if( progress != FALSE) {
close(pb)
}
foundbestpaths <- retlist$bestpaths + 1
## now need to unpack results
demands <- savedemands
for(n in 1:length(demands)) {
flow <- 0
for(i in 1:length(demands[[n]]$paths)) {
flow <- flow + retlist$pathflows[[n]][[i]]
demands[[n]]$paths[[i]] <- retlist$pathflows[[n]][[i]]
}
demands[[n]]$flow <- flow
}
gdual <- g
gdual <- rg.set.weight(gdual,retlist$vlengths)
scalef <- 1 / log(1/delta,base=(1+e))
demands <- rg.max.concurrent.flow.rescale.demands.flow(demands,scalef)
beta <- calcBeta(demands,gdual)
betar <- calcBetaRestricted(demands,gdual)
lambda=NULL
lambda <- calcLambda(demands)
foundratio <- beta / lambda
ratiobound <- (1-e)^-3
gflow <- rg.max.concurrent.flow.graph(gdual,demands)
if(FALSE) {
for(d in 1:length(demandpaths)) {
cat("demand ",d,": ")
for(p in 1:length(demandpaths[[d]])) {
mincap = Inf
for(ed in demandpaths[[d]][[p]]) {
if(vcapacity[ed+1] < mincap) {
mincap = vcapacity[ed+1]
}
}
cat(" ",retlist$pathcount[[d]][[p]])
if(mincap < savedemands[[d]]$demand) {
cat("u")
} else {
cat("o")
}
if(foundbestpaths[[d]] == p)
cat("F")
if(identical(names(demands[[d]]$paths[p]),
names(scenario$intdemands[[d]]$paths))) {
#if(bestpaths[[d]] == p)
cat("X")
}
}
cat("\n")
}
}
intdemands <- list()
bestpaths=retlist$bestpaths+1
for(i in names(demands)) {
intdemands[[i]]$source <- demands[[i]]$source
intdemands[[i]]$sink <- demands[[i]]$sink
intdemands[[i]]$demand <- demands[[i]]$demand
intdemands[[i]]$flow <- demands[[i]]$demand
intdemands[[i]]$paths <- list()
intdemands[[i]]$paths[[names(demands[[i]]$paths[bestpaths[as.integer(i)]])]] <-
demands[[i]]$demand
}
##put back the demands labels instead of indices
demands <- rg.demands.relable.from.indices(demands,nodelabels)
intdemands <- rg.demands.relable.from.indices(intdemands,nodelabels)
nodes(gflow) <- nodelabels
nodes(gdual) <- nodelabels
gflowint <- rg.max.concurrent.flow.graph(gdual,intdemands)
##value<< Returns a list with many elements, the most useful are
retval <- list(demands=demands,
gflow=gflow,
gdual=gdual,
beta=beta,
betar=betar,
lambda=lambda,
phases=retlist$totalphases,
e=e,
vlength=retlist$vlengths,
countgamma=retlist$countgamma,
bestgamma=retlist$bestgamma,
bestpaths=bestpaths,
pathdiffcount=retlist$pathdiffcount,
phasepathdiffcount=retlist$phasepathdiffcount,
gammavals=retlist$gammavals,
betavals=retlist$betavals,
lambdavals=retlist$lambdavals,
intdemands=intdemands, ##<< intdemands - the output demands list with the "optimal" path for each demand
gflowint=gflowint ##<< gflowint - graphNEL with edge weight equal to assigned flow
)
return(retval)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.