All Downloads are FREE. Search and download functionalities are using the official Maven repository.

protelis.coord.spreading.pt Maven / Gradle / Ivy

There is a newer version: 17.6.0
Show newest version
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)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy