protelis.coord.spreading.pt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protelis-lang Show documentation
Show all versions of protelis-lang Show documentation
Essential libraries for the Protelis language
module protelis:coord:spreading
import org.apache.commons.math3.util.FastMath.floor
import protelis:coord:meta
import protelis:lang:utils
import protelis:state:time
import java.lang.Double.POSITIVE_INFINITY
import java.lang.Double.NEGATIVE_INFINITY
/*
* Add v to the field of distances from the current device.
*
* @param v num, distance to add
* @return num, field of distances from the current device
*/
public def addRange(v) {
addRangeWithMetric(v, nbrRange)
}
/*
* Add of 1 the field of distances of the current device's neighbors.
*
* @return num, field of distances from the current device
*/
public def addRangeHop() {
addRangeWithMetric(1, nbrRangeHop)
}
/*
* Add v to the field of distances from the current device.
*
* @param v num, distance to add
* @param metric () -> num, estimate distance from other devices
* @param speed num, communication speed
* @return num, field of distances from the current device
*/
public def addRangeWithLag(v, metric, speed) {
addRangeWithMetric(v, () -> { nbrRangeLag(metric, nbrLag, speed) })
}
/*
* Add v to the field of distances from the current device.
*
* @param v num, distance to add
* @param metric () -> num, estimate distance from other devices
* @return num, field of distances from the current device
*/
public def addRangeWithMetric(v, metric) {
v + metric.apply()
}
/**
* Constrain the spreading of broadcast function only within a region.
*
* @param region bool, whether the function is spreaded or not
* @param null T, default value where f is not spreaded
* @param source bool, whether the current device is a source
* @param T, value what to broadcast
* @return T, broadcast inside the region, return null otherwise
*/
public def boundBroadcast(region, null, source, value) {
boundSpreading(region, () -> { broadcast(source, value) }, null)
}
/**
* Constrain the spreading of G function only within a region.
* @param region bool, whether the function is spreaded or not
* @param null T, default value where f is not spreaded
* @param source bool, whether the current device is a source
* @param initial T, initial value of the computation
* @param metric () -> num, estimate distance from other devices
* @param accumulate (T) -> T, how to manage the value
* @return T, accumulated value
*/
public def boundG(region, null, source, initial, metric, accumulate) {
boundSpreading(region, () -> {
G(source, initial, metric, accumulate)
}, null)
}
/*
* Distance from the current to the closest source.
*
* @param region bool, where to compute distanceTo
* @param source bool, whether the device is a source
* @return num, field of minimum distances to the closest source
*/
public def boundDistanceTo(region, source) {
boundSpreading(region, () -> { distanceTo(source) }, POSITIVE_INFINITY)
}
/*
* Broadcast value across a spanning tree starting from the source.
*
* @param source bool, whether the current device is a source
* @param value T, what to broadcast
* @return T, broadcasted value
*/
public def broadcast(source, value) {
broadcastWithMetric(source, value, nbrRange)
}
/*
* Broadcast value across a spanning tree starting from the source.
*
* @param source bool, whether the current device is a source
* @param value T, what to broadcast
* @param metric () -> num, estimate distance from other devices
* @return T, broadcasted value
*/
public def broadcastWithMetric(source, value, metric) {
G(source, value, metric, identity)
}
/**
* Dynamically computes distributed routes between regions of a network, and
* dynamically adapts to shape and changes of the network topology. The channel
* is a Boolean field that is true for devices near the shortest route from a given
* (distributed) source to a (distributed) destination.
*
* @param source bool, whether the device is a source
* @param dest bool, whether the device is a destination
* @param thr num, threshold for Double comparison (e.g. 0.01)
* @return bool, true if the device is on the shortest path, false otherwise
*/
public def channel(source, dest, thr) {
distanceTo(source) + distanceTo(dest) <= distanceBetween(source, dest) + thr
}
/**
* Dynamically computes distributed routes between regions of a network, and
* dynamically adapts to shape and changes of the network topology. The channel
* is a Boolean field that is true for devices near the shortest route from a given
* (distributed) source to a (distributed) destination.
*
* @param source bool, whether the device is a source
* @param dest bool, whether the device is a destination
* @param obstacle bool, whether the device is an obstacle
* @param thr num, threshold for Double comparison (e.g. 0.01)
* @return bool, true if the device is on the shortest path, false otherwise
*/
public def channelWithObstacle(source, dest, obstacle, thr) {
boundSpreading(!obstacle, () -> { channel(source, dest, thr) }, false)
}
/**
* Channel pattern.
*
* @param source bool, whether the device is a source
* @param dest bool, whether the device is a destination
* @param width num, how much to dilate the channel
* @return bool, true if the device is on the shortest path, false otherwise
*/
public def channel2(source, destination, width) {
channel2WithObstacle(source, destination, false, width)
}
/**
* Channel pattern.
*
* @param source bool, whether the device is a source
* @param dest bool, whether the device is a destination
* @param obstacle bool, whether the device is an obstacle
* @param width num, how much to dilate the channel
* @return bool, true if the device is on the shortest path, false otherwise
*/
public def channel2WithObstacle(source, destination, obstacle, width) {
boundSpreading(!obstacle, () -> {
distanceTo(shortestPath(source, destination)) <= width
}, false)
}
/**
* @param source bool, whether the device is a source
* @param range num, range
* @return bool, true if the device is closer than range to the source
*/
public def closerThan(source, range) {
closerThanWithMetric(source, nbrRange, range)
}
/**
* @param source bool, whether the device is a source
* @param metric () -> num, how to estimate the distance
* @param range num, range
* @return bool, true if the device is closer than range to the source
*/
public def closerThanWithMetric(source, metric, range) {
distanceToWithMetric(source, metric) < range
}
/**
* Self-healing gradient algorithm that reconfigures in O(diameter) time.
* Requires euclidean metric.
*
* @param source bool, whether the device is a source
* @param maxHop num, communication max range
* @return num, minimum distance from the source
*/
public def crfGradient(source, maxHop) {
crfGradientWithMetric(source, nbrRange, maxHop)
}
/**
* Self-healing gradient algorithm that reconfigures in O(diameter) time.
* Requires euclidean metric.
*
* @param source bool, whether the device is a source
* @param metric () -> num, how to estimate distances without lag
* @param maxHop num, communication max range
* @return num, minimum distance from the source
*/
public def crfGradientWithMetric(source, metric, maxHop) {
share (potential, nbrPotential <- [POSITIVE_INFINITY, 0]) {
mux (source) {
[0, 0]
} else {
let d = nbrPotential.get(0);
let dt = self.getDeltaTime();
mux (anyHood(addRangeWithLag(d, metric, potential.get(1)) <= potential.get(0))) {
minHood([d + metric.apply(), 0])
} else {
let v0 = if (dt == 0) { maxHop } else { maxHop / (dt * 12) };
[potential.get(0) + v0 * dt, v0]
}
}
}.get(0)
}
/**
* Self-healing gradient algorithm with single-path reaction speed.
*
* @param source bool, whether the device is a source
* @param radius num, communication max range
* @param speed num, estimate of single-path speed
* @return num, minimum distance from the source
*/
public def bisGradient(source, radius, speed) {
bisGradientWithMetric(source, nbrRange, radius, speed)
}
/**
* Self-healing gradient algorithm with single-path reaction speed.
*
* @param source bool, whether the device is a source
* @param metric () -> num, how to estimate distances without lag
* @param radius num, communication max range
* @param speed num, estimate of single-path speed
* @return num, minimum distance from the source
*/
public def bisGradientWithMetric(source, metric, radius, speed) {
let val = if (source) {0} else {POSITIVE_INFINITY};
let loc = [val, val];
share (nbrOld <- loc) {
let nx = nbrOld.get(0) + metric.apply();
let nt = nbrOld.get(1) + self.nbrLag();
min(minHood([max(nx, nt*speed-radius), nt]), loc)
}.get(0)
}
/**
* Dilate a spatial region.
*
* @param region bool, whether the device is inside the region
* @param width num, how much to dilate
* @return bool, dilated region
*/
public def dilate(region, width) {
dilateWithMetric(region, nbrRange, width)
}
/**
* Dilate a spatial region.
*
* @param region bool, whether the device is inside the region
* @param metric () -> num, how to estimate the distance between devices
* @param width num, how much to dilate
* @return bool, dilated region
*/
public def dilateWithMetric(region, metric, width) {
distanceToWithMetric(region, metric) < width
}
/**
* Forecast obstacles along a path to the source.
*
* @param source bool, whether the device is a source
* @param obstacle bool, whether the device is an obstacle
* @return bool, true if the device encounter an obstacle within the shortest
* path towards the closest source, false otherwise
*/
public def directProjection(source, obstacle) {
directProjectionWithMetric(source, obstacle, nbrRange)
}
/**
* Forecast obstacles along a path to the source.
*
* @param source bool, whether the device is a source
* @param obstacle bool, whether the device is an obstacle
* @param metric () -> num, how to estimate neighbors distances
* @return bool, true if the device encounter an obstacle within the shortest
* path towards the closest source, false otherwise
*/
public def directProjectionWithMetric(source, obstacle, metric) {
G(source, obstacle, metric, (v) -> { obstacle || Gnull(v, false) })
}
/**
* Smallest distance between source and destination devices.
*
* @param source bool, whether the current device is a source
* @param dest bool, whether the current device is a destination
* @return num, smallest distance between source and destination devices
*/
public def distanceBetween(source, dest) {
distanceBetweenWithMetric(source, dest, nbrRange)
}
/**
* Smallest distance between source and destination devices.
*
* @param source bool, whether the current device is a source
* @param dest bool, whether the current device is a destination
* @param metric () -> num, estimate distance from other devices
* @return num, smallest distance between source and destination devices
*/
public def distanceBetweenWithMetric(source, dest, metric) {
broadcastWithMetric(source, distanceToWithMetric(dest, metric), metric)
}
/*
* Distance from the current to the closest source.
*
* @param source bool, whether the current device is a source
* @return num, distance to the closest source
*/
public def distanceTo(source) {
distanceToWithMetric(source, nbrRange)
}
/*
* Distance from the current to the closest source.
*
* @param source bool, whether the device is a source
* @param metric () -> num, estimate distance from other devices
* @return num, field of minimum distances to the closest source
*/
public def distanceToWithMetric(source, metric) {
G(source, if (source) {0} else {POSITIVE_INFINITY}, metric, (v) -> { v + metric.apply() })
}
/**
* Estimate worst propagation time considering a message round trip.
*
* @param d num, network diameter
* @param k num, number of replicas. Must be greater than 1.
*/
public def roundTripTime(d) {
4 * d * self.getDeltaTime()
}
// ---- Used by flexGradient
def flex_distorce(minD, metric) {
max(metric.apply(), minD)
}
def flex_slope(d, nbrD, minD, metric) {
let slope = maxHood([(d - nbrD) / flex_distorce(minD, metric), nbr(d), flex_distorce(minD, metric)]);
max(slope, [NEGATIVE_INFINITY, POSITIVE_INFINITY, 0])
}
/**
* Flex-Gradient.
*
* @param source bool, whether the device is a source
* @param epsilon num, tolerance
* @param rate num, communication rate
* @param range num, communication range
* @param distortion num, distortion
*/
public def flexGradient(source, epsilon, rate, range, distortion) {
flexGradientWithMetric(source, epsilon, rate, range, distortion, nbrRange)
}
/**
* Flex-Gradient.
*
* @param source bool, whether the device is a source
* @param epsilon num, tolerance
* @param rate num, communication rate
* @param range num, communication range
* @param distortion num, distortion
* @param metric () -> num, estimate distance from other devices
*/
public def flexGradientWithMetric(source, epsilon, rate, range, distortion, metric) {
share (d, nbrD <- POSITIVE_INFINITY) {
mux (source) { 0 }
else {
let dist = range * distortion;
let slope = flex_slope(d, nbrD, dist, metric);
let ct = minHood(nbrD + flex_distorce(dist, metric));
if (d == ct || (d > range && d > ct * 2) || cyclicTimer(rate)) {
ct
} else {
if (slope.get(0) > (1 + epsilon)) {
slope.get(1) + slope.get(2) * (1 + epsilon)
} else {
if (slope.get(0) < (1 - epsilon)) {
slope.get(1) + slope.get(2) * (1 - epsilon)
} else { d }
}
}
}
}
}
/*
* Propagate values across a spanning tree starting from the closest source.
* If there are no sources and no neighbors, default to initial value
*
* @param source bool, whether the current device is a source
* @param initial T, initial value of the computation
* @param metric () -> num, estimate distance from other devices
* @param accumulate (T) -> T, how to accumulate the value
* @return T, accumulated value
*/
public def G(source, initial, metric, accumulate) {
share (nbrDistanceValue <- [POSITIVE_INFINITY, initial]) {
mux (source) {
// If the device is a source then G return a 0 potential
// and the initial value
[0, initial]
} else {
// G returns the value related to the minimum potential
let minTuple =
minHood([
// potential estimation
nbrDistanceValue.get(0) + metric.apply(),
// values estimation
accumulate.apply(nbrDistanceValue.get(1))
]);
if (minTuple.get(0) == POSITIVE_INFINITY) { // if no neighbors or no path to source
[POSITIVE_INFINITY, initial] // default: reset to initial value
} else {
minTuple
}
}
}.get(1) // return the accumulated value
}
/**
* Wrapper for G error.
* For example: G(aBool, aBool, aMetric, (v) -> { Gnull(v, false) ... }
* assure that G is not broken in case minHood([..., ...]) returns Infinity.
*
* @param v T, value to be checked
* @param default T, default value to avoid to broke G
* @return T, v or default
*/
public def Gnull(v, default) {
mux (v == POSITIVE_INFINITY) {
default
} else {
v
}
}
/**
* Gradcast.
*
* @param source bool, whether the device is a source
* @param local T, local value
* @param gradient num, gradient to follow
* @param accumulate (T) -> T, how to accumulate local values
* @return T, accumulated value
*/
public def gradcast(source, local, gradient, accumulate) {
share (nbrValue <- local) {
mux (source) {
local
} else {
minHood([nbr(gradient), accumulate.apply(nbrValue)]).get(1)
}
}
}
/**
* Spread and extend information with distance from the source region.
*
* @param source bool, whether the current device is a source
* @param init T, initial value of the computation
* @param metric () -> num, estimate distance from other devices
* @param accumulate (T) -> T, how to accumulate the value
* @return [num, T], distance value and accumulated value
*/
public def gradient(source, init, metric, accumulate) {
G(source, [0, init], metric,
v -> {
let x = Gnull(v, [POSITIVE_INFINITY, init]);
[ addRangeWithMetric(x.get(0), metric), accumulate.apply(x.get(1)) ]
}
)
}
/*
* Broadcast value across a spanning tree starting from the source.
* For determining information paths, distances are measured in hops.
*
* @param source bool, whether the current device is a source
* @param value T, what to broadcast
* @return T, broadcasted value
*/
public def hopBroadcast(source, value) {
broadcastWithMetric(source, value, () -> { 1 })
}
/*
* Distance from the current to the closest source, measured in hops
*
* @param source bool, whether the current device is a source
* @return num, number of hops to the closest source
*/
public def hopDistanceTo(source) {
distanceToWithMetric(source, () -> {1})
}
/**
* Run an instance of G from every source.
*
* @param sources [deviceUID], list of active sources
* @param init T, initial value of the computation
* @param metric () -> num, estimate distance from other devices
* @param accumulate (T) -> T, how to accumulate the value
* @return [[deviceUID, T]], list of information from multiple sources
*/
public def multiG(sources, init, metric, accumulate) {
multiInstance(
sources,
id -> { G(id == getUID(), init, metric, accumulate) },
init
)
}
/**
* Spread and extend information with distance from multiple source regions.
*
* @param sources [deviceUID], list of active sources
* @param init T, initial value of the computation
* @param metric () -> num, estimate distance from other devices
* @param accumulate (T) -> T, how to accumulate the value
* @return [[deviceUID, [num, T]]], list of information from multiple
* gradients
*/
public def multiGradient(sources, init, metric, accumulate) {
multiInstance(
sources,
id -> { gradient(id == getUID(), init, metric, accumulate) },
[POSITIVE_INFINITY, init]
)
}
/**
* Count the number of neighbors.
*
* @return num, number of neighbors
*/
public def neighborhood() {
sumHood(nbr(1))
}
/**
* Time forward view.
*
* @return num, expected time from the device computation to
* neighbor's next computation incorporating that information.
*/
public def nbrDelay() {
self.nbrDelay()
}
/**
* Time backward view.
*
* @return num, how long ago information from neighbors was received.
*/
public def nbrLag() {
self.nbrLag()
}
/*
* Estimate the distance of the current device.
*
* @return num, field of distances from the current device
*/
public def nbrRange() {
self.nbrRange()
}
/*
* Estimate the distance from the current device.
*
* @return num, field of 1s for each neighbor
*/
public def nbrRangeHop() {
1
}
/*
* Estimate the distance of the current device.
*
* @param metric () -> num, how to estimate distances
* @param lagmetric () -> num, how to estimate latencies
* @param speed num, communication speed
* @return num, field of distances from the current device
*/
public def nbrRangeLag(metric, lagMetric, speed) {
metric.apply() + (lagMetric.apply() + self.getDeltaTime()) * speed
}
/**
* Shortest path according to a potential.
*
* @param source bool, whether the device is a source
* @param potential num, potential
* @return bool, true if the device is in the shortest path
* @see rendezvous
*/
public def descend(source, potential) {
share (nbrPath <- source) {
let nextStep = minHood(nbr([potential, self.getDeviceUID()]));
if (nextStep.size() > 1) {
let candidates = [nbr(nextStep.get(1)), nbrPath];
source || anyHood([self.getDeviceUID(), true] == candidates)
} else { source }
}
}
/**
* Shortest path.
*
* @param source bool, whether the device is the source
* @param dest bool, whether the device is the destination
* @return bool, true if the device is in the shortest path
* @see descend, shortestPath
*/
public def rendezvous(source, dest) {
descend(source, distanceTo(dest))
}
/**
* Dynamically computes the shortest route between regions of a network, and
* dynamically adapts to shape and changes of the network topology. The shortest path
* is a Boolean field that is true for devices near the shortest route from a given
* (distributed) source to a (distributed) destination.
*
* @param source bool, whether the device is the source
* @param dest bool, whether the device is the destination
* @return bool, true if the device is in the shortest path
* @see rendezvous
*/
public def shortestPath(source, destination) {
share (nbrPath <- false) {
mux (source) {
true
} else {
// any device on the shortest path
anyHood(
// check whether a neigh is on the shortest path AND the current device is in the shortest path
nbrPath &&
(distanceTo(destination) == nbr( // if my distance is the same as the collected one
minHood( // get the closest distance
nbr(distanceTo(destination)) // get the neighbors distances from the destination
)
)
)
)
}
}
}
/**
* Entry point for a computation.
*
* @param source bool, whether the device is a source
* @param range num, spreading constraint
* @param f () -> T, entry point
* @param null T, default value
* @return T, apply f if device is inside the spreading region, null outside
*/
public def vm(source, range, f, null) {
vmWithMetric(source, nbrRange, range, f, null)
}
/**
* Entry point for a computation.
*
* @param source bool, whether the device is a source
* @param metric () -> num, how to estimate neighbors distances
* @param range num, spreading constraint
* @param f () -> T, entry point
* @param null T, default value
* @return T, apply f if device is inside the spreading region, null outside
*/
public def vmWithMetric(source, metric, range, f, null) {
boundSpreading(closerThanWithMetric(source, metric, range), f, null)
}
/**
* Computing a Voronoi partition is an operation that is frequently useful in
* distributed systems. Given an initial set of seed devices, a Voronoi
* partition assigns each device to the partition of the nearest seed,
* effectively breaking the network up into zones of influence around key
* elements.
*
* @param source bool, whether the device is a seed
* @param id T, partition id
* @return T, partition of the nearest seed
*/
public def voronoiPatitioning(source, id) {
voronoiPatitioningWithMetric(source, id, nbrRange)
}
/**
* Computing a Voronoi partition is an operation that is frequently useful in
* distributed systems. Given an initial set of seed devices, a Voronoi
* partition assigns each device to the partition of the nearest seed,
* effectively breaking the network up into zones of influence around key
* elements.
*
* @param source bool, whether the device is a seed
* @param id T, partition id
* @param metric () -> num, how to estimate the distances of the neighbors
* @return T, partition of the nearest seed
*/
public def voronoiPatitioningWithMetric(seed, id, metric) {
G(seed, id, metric, identity)
}