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

it.unibo.alchemist.model.linkingrules.ClosestN Maven / Gradle / Ivy

Go to download

Abstract, incarnation independent implementations of the Alchemist's interfaces. Provides support for those who want to write incarnations.

There is a newer version: 35.0.1
Show newest version
/*
 * Copyright (C) 2010-2023, Danilo Pianini and contributors
 * listed, for each module, in the respective subproject's build.gradle.kts file.
 *
 * This file is part of Alchemist, and is distributed under the terms of the
 * GNU General Public License, with a linking exception,
 * as described in the file LICENSE in the Alchemist distribution's top directory.
 */
package it.unibo.alchemist.model.linkingrules;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.MinMaxPriorityQueue;
import com.google.common.primitives.Doubles;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import it.unibo.alchemist.util.BugReporting;
import it.unibo.alchemist.model.neighborhoods.Neighborhoods;
import it.unibo.alchemist.model.Environment;
import it.unibo.alchemist.model.LinkingRule;
import it.unibo.alchemist.model.Neighborhood;
import it.unibo.alchemist.model.Node;
import it.unibo.alchemist.model.Position;
import org.apache.commons.math3.util.FastMath;
import org.danilopianini.util.stream.SmallestN;
import org.jooq.lambda.tuple.Tuple2;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Non local-consistent rule that connect the closest N nodes together.
 * Two nodes get connected if either one belongs to the set of the ten devices closest to the other.
 * 
 * @param  Concentration type
 * @param 

{@link Position} type */ public class ClosestN> implements LinkingRule { private static final long serialVersionUID = 2L; private static final double CONNECTION_RANGE_TOLERANCE = 1.1; private final int n, expectedNodes, maxNodes; @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") private transient Cache, Double> ranges; /** * @param n * neighbors * @param expectedNodes * how many nodes are expected to be inserted in the environment * (used for optimization) * @param maxNodes * the maximum number of nodes for which the connection range * will be cached */ public ClosestN(final int n, final int expectedNodes, final int maxNodes) { if (n < 1) { throw new IllegalArgumentException("The parameter must be an integer greater than 0"); } this.n = n; this.expectedNodes = expectedNodes; this.maxNodes = maxNodes; } /** * @param n * neighbors * @param expectedNodes * how many nodes are expected to be inserted in the environment * (used for optimization) */ public ClosestN(final int n, final int expectedNodes) { this(n, expectedNodes, expectedNodes); } /** * @param n * neighbors */ public ClosestN(final int n) { this(n, 0); } private Cache, Double> ranges() { if (ranges == null) { ranges = CacheBuilder.newBuilder() .maximumSize(maxNodes) .build(); } return ranges; } @Override public final Neighborhood computeNeighborhood(final Node center, final Environment environment) { if (environment.getNodeCount() < expectedNodes || !nodeIsEnabled(center)) { return Neighborhoods.make(environment, center); } return Neighborhoods.make(environment, center, Stream.concat( closestN(center, environment), /* * Of all nodes but myself... */ environment.getNodes().parallelStream() /* * ...select those for which I'm on the closest n */ .filter(node -> !center.equals(node) && closestN(node, environment).anyMatch(center::equals) ) ) .sequential() .collect(Collectors.toCollection(LinkedHashSet::new))); } private Stream> closestN(final Node center, final Environment environment) { if (!nodeIsEnabled(center)) { return Stream.empty(); } double currentRange = getRange(environment, center); Set> inRange; final double maxRange = Doubles.max(environment.getSizeInDistanceUnits()) * 2; do { inRange = (environment.getNodeCount() > n && currentRange < maxRange ? nodesInRange(environment, center, currentRange).stream() : environment.getNodes().stream()) .filter(n -> !n.equals(center) && nodeIsEnabled(n)) .collect(Collectors.toCollection(LinkedHashSet::new)); currentRange *= 2; } while (inRange.size() < n && inRange.size() < environment.getNodeCount() - 1 && currentRange < maxRange * 2); if (inRange.isEmpty()) { return Stream.empty(); } final MinMaxPriorityQueue>> closestN = inRange.stream() .map(node -> new Tuple2<>(environment.getDistanceBetweenNodes(center, node), node)) .collect(new SmallestN<>(n)); final var farthestNode = closestN.peekLast(); if (farthestNode == null) { final var debugDetails = new HashMap(); debugDetails.put("farthestNode", null); debugDetails.put("inRange", inRange); debugDetails.put("closestN", closestN); BugReporting.reportBug( "neighbors were found, but no neighbor was included in the list", debugDetails ); } setRange(center, Math.max(Double.MIN_VALUE, farthestNode.v1()) * CONNECTION_RANGE_TOLERANCE); return closestN.stream().map(Tuple2::v2); } /** * The set of nodes within the comunication range. * @param environment the {@link Environment} * @param node the {@link Node} * @param range the communication range * @return the set of nodes within the communication range */ protected final Set> nodesInRange( final Environment environment, final Node node, final double range ) { return environment.getNodesWithinRange(node, range); } /** * This method always return true. Subclasses can override it. * * @param node * the node * @return true if the node is enabled (can be linked). */ protected boolean nodeIsEnabled(final Node node) { return true; } /** * Gets the communication range of a node. * * @param environment * the environment * @param center * the node * @return the communication range */ protected final double getRange(final Environment environment, final Node center) { try { /* * Range estimation: twice the radius of a circle with an area that * would, on average, contain the number of required devices */ return ranges().get(center, () -> { final int nodes = environment.getNodeCount(); if (nodes < n || nodes < 10) { return Double.MAX_VALUE; } final double[] size = environment.getSizeInDistanceUnits(); final double x = size[0]; final double y = size[1]; final double density = x * y / nodes; return Math.max(Double.MIN_VALUE, Math.min(2 * FastMath.sqrt(density / Math.PI * n), Double.MAX_VALUE) ); }); } catch (ExecutionException e) { throw new IllegalStateException("Couldn't compute ranges. This is most likely a bug.", e); } } /** * Sets a communication range for a node. Used for optimization purposes. * * @param center * the node * @param range * the range */ protected final void setRange(final Node center, final double range) { ranges().put(center, range); } @Override public final boolean isLocallyConsistent() { return false; } /** * @return the number of neighbors */ protected final int getN() { return n; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy