>()
private val nodeToPos = TIntObjectHashMap()
- private val spatialIndex: SpatialIndex> = requireNotNull(internalIndex)
+ private val spatialIndex: SpatialIndex> = internalIndex
- override val layers: ListSet> get() = ArrayListSet(_layers.values)
+// override val layers: Map> get() = _layers
override val globalReactions: ListSet>
get() = ListSets.unmodifiableListSet(_globalReactions)
@@ -105,7 +106,8 @@ abstract class AbstractEnvironment> protected constructor(
}
override fun addLayer(molecule: Molecule, layer: Layer) {
- check(_layers.put(molecule, layer) == null) { "Two layers have been associated to $molecule" }
+ check(molecule !in layers.keys) { "A layer for $molecule was already associated to this environment." }
+ layers += molecule to layer
}
override fun addGlobalReaction(reaction: GlobalReaction) {
@@ -147,11 +149,11 @@ abstract class AbstractEnvironment> protected constructor(
*
* @param node
* the node
- * @param p
+ * @param originalPosition
* the original (requested) position
* @return the actual position where the node should be located
*/
- protected abstract fun computeActualInsertionPosition(node: Node, p: P): P
+ protected abstract fun computeActualInsertionPosition(node: Node, originalPosition: P): P
override fun forEach(action: Consumer?>?) {
nodes.forEach(action)
@@ -169,19 +171,16 @@ abstract class AbstractEnvironment> protected constructor(
private fun getAllNodesInRange(center: P, range: Double): List> {
require(range > 0) { "Range query must be positive (provided: $range)" }
- @Suppress("UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS")
- val validCache =
- cache ?: Caffeine
- .newBuilder()
- .maximumSize(1000)
- .build, List>> { (pos, r) -> runQuery(pos, r) }
- .also { cache = it }
+ val validCache = cache ?: Caffeine.newBuilder()
+ .maximumSize(1000)
+ .build, List>> { (pos, r) -> runQuery(pos, r) }
+ .also { cache = it }
return validCache[center to range]
}
override fun getDistanceBetweenNodes(n1: Node, n2: Node): Double = getPosition(n1).distanceTo(getPosition(n2))
- override fun getLayer(molecule: Molecule): Layer? = _layers[molecule]
+ override fun getLayer(molecule: Molecule): Layer? = layers[molecule]
override fun getNeighborhood(node: Node): Neighborhood {
val result = neighCache[node.id]
diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/environments/EmptyEnvironment.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/environments/EmptyEnvironment.kt
new file mode 100644
index 0000000000..e283bfc1b5
--- /dev/null
+++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/environments/EmptyEnvironment.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010-2026, 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.environments
+
+import it.unibo.alchemist.model.Incarnation
+import it.unibo.alchemist.model.Neighborhood
+import it.unibo.alchemist.model.Node
+import it.unibo.alchemist.model.Position
+import org.danilopianini.util.SpatialIndex
+
+/**
+ * An [AbstractEnvironment] implementation that intentionally provides no spatial functionality.
+ *
+ * This environment is meant as a placeholder or sentinel implementation in contexts where an
+ * [it.unibo.alchemist.model.Environment]
+ * instance is required, but node insertion, movement, neighborhood computation, and position construction must be
+ * disallowed.
+ *
+ * @param incarnation the [Incarnation] associated with this environment.
+ * @param dimensions the number of spatial dimensions exposed by this environment.
+ */
+class EmptyEnvironment>(incarnation: Incarnation, override val dimensions: Int = 2) :
+ AbstractEnvironment(
+ incarnation,
+ object : SpatialIndex> {
+ override val dimensions: Int = dimensions
+ override fun insert(element: Node, vararg position: Double) = nope()
+ override fun remove(element: Node, vararg position: Double) = nope()
+ override fun move(element: Node, start: DoubleArray, end: DoubleArray) = nope()
+ override fun query(vararg parallelotope: DoubleArray) = emptyList>()
+ },
+ ) {
+
+ /**
+ * This environment does not support node insertion positions.
+ *
+ * @throws UnsupportedOperationException always.
+ */
+ override fun computeActualInsertionPosition(node: Node, originalPosition: P): P = nope()
+
+ /**
+ * This environment does not support node insertion.
+ *
+ * @throws UnsupportedOperationException always.
+ */
+ override fun nodeAdded(node: Node, position: P, neighborhood: Neighborhood) = nope()
+
+ /**
+ * The origin (all zeros) for each coordinate axis.
+ *
+ * This value is provided only to satisfy the [AbstractEnvironment] contract and does not imply that
+ * spatial operations are supported.
+ */
+ override val offset: DoubleArray = DoubleArray(dimensions) { 0.0 }
+
+ /**
+ * The (unit) extent for each coordinate axis.
+ *
+ * This value is provided only to satisfy the [AbstractEnvironment] contract and does not imply that
+ * spatial operations are supported.
+ */
+ override val size: DoubleArray = DoubleArray(dimensions) { 1.0 }
+
+ /**
+ * This environment cannot create positions.
+ *
+ * @throws UnsupportedOperationException always.
+ */
+ override fun makePosition(vararg coordinates: Number): P = nope()
+
+ /**
+ * This environment cannot create positions.
+ *
+ * @throws UnsupportedOperationException always.
+ */
+ override fun makePosition(coordinates: DoubleArray): P = nope()
+
+ /**
+ * This environment does not support node movement.
+ *
+ * @throws UnsupportedOperationException always.
+ */
+ override fun moveNodeToPosition(node: Node, newPosition: P) = nope()
+
+ private companion object {
+
+ /**
+ * Throws the failure used by all unsupported operations in [EmptyEnvironment].
+ *
+ * @return never returns normally.
+ * @throws UnsupportedOperationException always.
+ */
+ private fun nope(): Nothing = throw UnsupportedOperationException(
+ "The empty environment cannot generate positions, and does not support the insertion of nodes.",
+ )
+ }
+}
diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/nodes/GenericNode.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/nodes/GenericNode.kt
index 682602a9e7..f4b8715ebc 100644
--- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/nodes/GenericNode.kt
+++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/nodes/GenericNode.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2023, Danilo Pianini and contributors
+ * Copyright (C) 2010-2025, 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
@@ -10,7 +10,6 @@ package it.unibo.alchemist.model.nodes
import com.google.common.collect.MapMaker
import it.unibo.alchemist.model.Environment
-import it.unibo.alchemist.model.Incarnation
import it.unibo.alchemist.model.Molecule
import it.unibo.alchemist.model.Node
import it.unibo.alchemist.model.NodeProperty
@@ -32,10 +31,6 @@ import javax.annotation.Nonnull
open class GenericNode
@JvmOverloads
constructor(
- /**
- * simulation incarnation.
- */
- val incarnation: Incarnation,
/**
* The environment in which the node is places.
*/
@@ -48,9 +43,6 @@ constructor(
val molecules: MutableMap = LinkedHashMap(),
final override val properties: MutableList> = ArrayList(),
) : Node {
- constructor(
- environment: Environment,
- ) : this(environment.incarnation, environment)
final override fun addReaction(reactionToAdd: Reaction) {
reactions.add(reactionToAdd)
@@ -69,7 +61,7 @@ constructor(
/**
* @return an empty concentration
*/
- protected open fun createT(): T = incarnation.createConcentration()
+ protected open fun createT(): T = environment.incarnation.createConcentration()
final override fun equals(other: Any?): Boolean = other is Node<*> && other.id == id
diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/terminators/StableForSteps.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/terminators/StableForSteps.kt
index 6ef6f8ac82..fe00fe05a3 100644
--- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/terminators/StableForSteps.kt
+++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/terminators/StableForSteps.kt
@@ -41,10 +41,10 @@ import java.util.function.Predicate
* @property checkInterval the recurrence of the test
* @property equalIntervals the number of [checkInterval] intervals required to be unchanged for [test] to return true
*/
-data class StableForSteps(private val checkInterval: Long, private val equalIntervals: Long) :
- TerminationPredicate> {
+data class StableForSteps>(private val checkInterval: Long, private val equalIntervals: Long) :
+ TerminationPredicate {
private var success: Long = 0
- private var positions: Map, Position<*>> = emptyMap()
+ private var positions: Map, P> = emptyMap()
private var contents = makeTable(0)
init {
@@ -53,7 +53,7 @@ data class StableForSteps(private val checkInterval: Long, private val
}
}
- override fun invoke(environment: Environment>): Boolean {
+ override fun invoke(environment: Environment): Boolean {
if (environment.simulation.step % checkInterval == 0L) {
val newPositions = environment.associateBy({ it }, { environment.getPosition(it) })
val newContents = makeTable(environment.nodeCount)
@@ -71,7 +71,9 @@ data class StableForSteps(private val checkInterval: Long, private val
private companion object {
private fun makeTable(size: Int): Table, Molecule, T> =
- Tables.newCustomTable(Maps.newLinkedHashMapWithExpectedSize, Map>(size)) {
+ Tables.newCustomTable, Molecule, T>(
+ Maps.newLinkedHashMapWithExpectedSize, Map>(size),
+ ) {
Maps.newLinkedHashMapWithExpectedSize(size)
}
}
diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/MoleculeControlledTimeDistribution.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/MoleculeControlledTimeDistribution.kt
index fead3e8aa0..3612bbde85 100644
--- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/MoleculeControlledTimeDistribution.kt
+++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/MoleculeControlledTimeDistribution.kt
@@ -50,7 +50,6 @@ constructor(
) : AnyRealDistribution(
start,
- @Suppress("OVERRIDE_DEPRECATION")
object : RealDistribution {
/*
* Unknown values
diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/SimpleNetworkArrivals.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/SimpleNetworkArrivals.kt
index 559b5b845e..776b7f4462 100644
--- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/SimpleNetworkArrivals.kt
+++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/model/timedistributions/SimpleNetworkArrivals.kt
@@ -15,7 +15,6 @@ import it.unibo.alchemist.model.Molecule
import it.unibo.alchemist.model.Node
import it.unibo.alchemist.model.Time
import it.unibo.alchemist.model.times.DoubleTime
-import java.lang.IllegalStateException
/**
* This class models a distribution that follows the packet arrival times as described in
@@ -29,14 +28,6 @@ import java.lang.IllegalStateException
* then it will get used as a constant delay. Otherwise, the String will be used within [Incarnation.getProperty]
*
*/
-
-private val Molecule?.isMeaningful: Molecule?
- get() = this?.takeUnless { name.isNullOrBlank() }
-
-/**
- * A time distribution that simulates network packet arrivals based on EdgeCloudSim model.
- * Computes delays based on propagation delay and packet transmission time (packet size / bandwidth).
- */
class SimpleNetworkArrivals private constructor(
/** The incarnation used to resolve properties from nodes. */
val incarnation: Incarnation,
@@ -161,7 +152,7 @@ class SimpleNetworkArrivals private constructor(
).let { bw ->
accessPointIdentificator?.let { id ->
if (node.isAccessPoint || myNeighborhood.isEmpty()) {
- bw / Math.max(myNeighborhood.size, 1)
+ bw / myNeighborhood.size.coerceAtLeast(1)
} else {
val accesspoints = myNeighborhood.filter { it.isAccessPoint }
when (accesspoints.size) {
@@ -175,8 +166,10 @@ class SimpleNetworkArrivals private constructor(
} ?: bw
}
- /** Computes the packet size, defaulting to 1.0 if not specified or invalid. */
- @Suppress("UnreachableCode") // Detekt false positive. Remove once fixed.
+ /**
+ * Computes the packet size from constants or properties.
+ * Defaults to 1.0 if not specified or invalid.
+ */
val packetSize: Double
get() = constantPacketSize
?: incarnation.getProperty(node, packetSizeMolecule, packetSizeProperty).takeIf { it.isFinite() && it >= 0 }
@@ -218,6 +211,11 @@ class SimpleNetworkArrivals private constructor(
)
override fun getRate(): Double = 1 / (propagationDelay + packetSize / bandwidth)
-}
-private operator fun Time.plus(other: Double) = DoubleTime(toDouble() + other)
+ private companion object {
+ private val Molecule?.isMeaningful: Molecule?
+ get() = this?.takeUnless { name.isNullOrBlank() }
+
+ private operator fun Time.plus(other: Double) = DoubleTime(toDouble() + other)
+ }
+}
diff --git a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/util/Environments.kt b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/util/Environments.kt
index 7fad2a21d3..459f87f4a6 100644
--- a/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/util/Environments.kt
+++ b/alchemist-implementationbase/src/main/kotlin/it/unibo/alchemist/util/Environments.kt
@@ -40,12 +40,12 @@ object Environments {
/**
* Computes the diameter of all subnetworks in the environment.
- * The diameter is the longest shortest path between any two nodes,
- * evaluated using the [allShortestHopPaths] method.
- * Returns a [Set] containing the subnetworks.
+ * The diameter is the longest shortest path between any two nodes.
+ * Returns a [Map] containing the subnetwork related to each [Node] of the environment.
*/
- fun Environment.allSubNetworksByNodeWithHopDistance(): Map, Network> =
- allSubNetworksByNode(hopDistance())
+ fun Environment.allSubNetworks(
+ computeDistance: (Node, Node) -> Double = environmentMetricDistance(),
+ ): Set> = allSubNetworksByNode(computeDistance).values.toSet()
/**
* Computes the diameter of all subnetworks in the environment.
@@ -53,13 +53,13 @@ object Environments {
* evaluated using the [allShortestHopPaths] method.
* Returns a [Set] containing the subnetworks.
*/
- fun Environment.allSubNetworksWithHopDistance(): Set> =
- allSubNetworksByNodeWithHopDistance().values.toSet()
+ fun Environment.allSubNetworksByNodeWithHopDistance(): Map, Network> =
+ allSubNetworksByNode(hopDistance())
/**
* Computes the diameter of all subnetworks in the environment.
* The diameter is the longest shortest path between any two nodes.
- * Returns a [Set] containing the subnetworks.
+ * Returns a [Map] mapping each node to the subnetwork it belongs to.
*/
fun Environment.allSubNetworksByNode(
computeDistance: (Node, Node) -> Double = environmentMetricDistance(),
@@ -94,6 +94,7 @@ object Environments {
.maxOrNull()
?: 0.0,
)
+ result[centerNode] = subnetwork
}
}
}
@@ -102,12 +103,12 @@ object Environments {
/**
* Computes the diameter of all subnetworks in the environment.
- * The diameter is the longest shortest path between any two nodes.
- * Returns a [Map] containing the subnetwork related to each [Node] of the environment.
+ * The diameter is the longest shortest path between any two nodes,
+ * evaluated using the [allShortestHopPaths] method.
+ * Returns a [Set] containing the subnetworks.
*/
- fun Environment.allSubNetworks(
- computeDistance: (Node, Node) -> Double = environmentMetricDistance(),
- ): Set> = allSubNetworksByNode(computeDistance).values.toSet()
+ fun Environment.allSubNetworksWithHopDistance(): Set> =
+ allSubNetworksByNodeWithHopDistance().values.toSet()
/**
* Calculates the shortest paths using the Floyd-Warshall algorithm calculating the Hop Distance between nodes.
diff --git a/alchemist-implementationbase/src/test/kotlin/it/unibo/alchemist/util/TestDiameterUtils.kt b/alchemist-implementationbase/src/test/kotlin/it/unibo/alchemist/util/TestDiameterUtils.kt
index 1d087afbef..99bd40b8a0 100644
--- a/alchemist-implementationbase/src/test/kotlin/it/unibo/alchemist/util/TestDiameterUtils.kt
+++ b/alchemist-implementationbase/src/test/kotlin/it/unibo/alchemist/util/TestDiameterUtils.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2025, Danilo Pianini and contributors
+ * Copyright (C) 2010-2026, 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
@@ -11,10 +11,10 @@ package it.unibo.alchemist.util
import it.unibo.alchemist.model.Environment
import it.unibo.alchemist.model.environments.Continuous2DEnvironment
+import it.unibo.alchemist.model.incarnations.ProtelisIncarnation
import it.unibo.alchemist.model.linkingrules.ConnectWithinDistance
import it.unibo.alchemist.model.nodes.GenericNode
import it.unibo.alchemist.model.positions.Euclidean2DPosition
-import it.unibo.alchemist.model.protelis.ProtelisIncarnation
import it.unibo.alchemist.util.Environments.allSubNetworksByNode
import it.unibo.alchemist.util.Environments.allSubNetworksByNodeWithHopDistance
import it.unibo.alchemist.util.Environments.isNetworkSegmented
@@ -28,7 +28,7 @@ import kotlin.test.assertTrue
* Adds a node to the environment at the specified [coordinates].
*/
infix fun Environment.addNodeAt(coordinates: Pair) = addNode(
- GenericNode(ProtelisIncarnation(), this),
+ GenericNode(this),
Euclidean2DPosition(coordinates.first, coordinates.second),
)
diff --git a/alchemist-incarnation-biochemistry/build.gradle.kts b/alchemist-incarnation-biochemistry/build.gradle.kts
index affba6e24c..43d88b022f 100644
--- a/alchemist-incarnation-biochemistry/build.gradle.kts
+++ b/alchemist-incarnation-biochemistry/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2022, Danilo Pianini and contributors
+ * Copyright (C) 2010-2025, 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
@@ -24,14 +24,20 @@ plugins {
dependencies {
antlr(libs.antlr4)
+
+ ksp(alchemist("factories-generator"))
+
api(alchemist("implementationbase"))
api(alchemist("euclidean-geometry"))
api(alchemist("physics"))
+
implementation(libs.apache.commons.lang3)
implementation(libs.boilerplate)
implementation(libs.jirf)
implementation(libs.trove4j)
+
runtimeOnly(libs.antlr4.runtime)
+
testImplementation(alchemist("engine"))
testImplementation(alchemist("loading"))
}
diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/BiochemistryIncarnation.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/BiochemistryIncarnation.java
index 584892a168..48de22ded8 100644
--- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/BiochemistryIncarnation.java
+++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/BiochemistryIncarnation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2023, Danilo Pianini and contributors
+ * Copyright (C) 2010-2026, 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
@@ -59,7 +59,7 @@ public Node createNode(
if (Double.isNaN(diameter)) {
throw new IllegalArgumentException("Invalid diameter: " + parameter);
}
- final Node node = new GenericNode<>(this, environment);
+ final Node node = new GenericNode<>(environment);
if (diameter == 0) {
node.addProperty(new CircularCell(environment, node));
} else {
@@ -108,7 +108,6 @@ public Condition createCondition(
final RandomGenerator randomGenerator,
final Environment environment,
final Node node,
- final TimeDistribution time,
final Actionable actionable,
final @Nullable Object additionalParameters
) {
@@ -120,7 +119,6 @@ public Action createAction(
final RandomGenerator randomGenerator,
final Environment environment,
final Node node,
- final TimeDistribution time,
final Actionable actionable,
final @Nullable Object additionalParameters
) {
diff --git a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/nodes/EnvironmentNodeImpl.java b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/nodes/EnvironmentNodeImpl.java
index 4923333138..7b27c9c6d4 100644
--- a/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/nodes/EnvironmentNodeImpl.java
+++ b/alchemist-incarnation-biochemistry/src/main/java/it/unibo/alchemist/model/biochemistry/nodes/EnvironmentNodeImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2023, Danilo Pianini and contributors
+ * Copyright (C) 2010-2026, 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
@@ -10,7 +10,6 @@
package it.unibo.alchemist.model.biochemistry.nodes;
import it.unibo.alchemist.model.Environment;
-import it.unibo.alchemist.model.Incarnation;
import it.unibo.alchemist.model.Molecule;
import it.unibo.alchemist.model.biochemistry.EnvironmentNode;
import it.unibo.alchemist.model.nodes.GenericNode;
@@ -26,23 +25,13 @@ public final class EnvironmentNodeImpl extends GenericNode implements En
@Serial
private static final long serialVersionUID = 1L;
- /**
- * Create a new environment node.
- *
- * @param incarnation the simulation incarnation
- * @param environment the environment
- */
- public EnvironmentNodeImpl(final Incarnation incarnation, final Environment environment) {
- super(incarnation, environment);
- }
-
/**
* Create a new environment node.
*
* @param environment the environment
*/
public EnvironmentNodeImpl(final Environment environment) {
- super(environment.getIncarnation(), environment);
+ super(environment);
}
@Override
diff --git a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/model/biochemistry/properties/TestDeformableCell.java b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/model/biochemistry/properties/TestDeformableCell.java
index 1b2bd94faf..d06cbaf9a9 100644
--- a/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/model/biochemistry/properties/TestDeformableCell.java
+++ b/alchemist-incarnation-biochemistry/src/test/java/it/unibo/alchemist/model/biochemistry/properties/TestDeformableCell.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2023, Danilo Pianini and contributors
+ * Copyright (C) 2010-2026, 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
@@ -80,7 +80,7 @@ class TestDeformableCell {
private TimeDistribution time;
private Node createDeformableCell(final double maxDiameter, final double rigidity) {
- final Node node = new GenericNode<>(incarnation, environment);
+ final Node node = new GenericNode<>(environment);
node.addProperty(
new CircularDeformableCell(environment, node, maxDiameter, rigidity)
);
diff --git a/alchemist-incarnation-protelis/build.gradle.kts b/alchemist-incarnation-protelis/build.gradle.kts
index 3286a1e7c2..dcb3a8e239 100644
--- a/alchemist-incarnation-protelis/build.gradle.kts
+++ b/alchemist-incarnation-protelis/build.gradle.kts
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2010-2025, 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.
+ */
+
import Libs.alchemist
/*
@@ -14,6 +23,8 @@ plugins {
}
dependencies {
+ // KSP
+ ksp(alchemist("factories-generator"))
// API
api(alchemist("api"))
api(alchemist("implementationbase"))
diff --git a/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/ProtelisIncarnation.kt b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/incarnations/ProtelisIncarnation.kt
similarity index 94%
rename from alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/ProtelisIncarnation.kt
rename to alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/incarnations/ProtelisIncarnation.kt
index 87681433b2..d3a759834c 100644
--- a/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/ProtelisIncarnation.kt
+++ b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/incarnations/ProtelisIncarnation.kt
@@ -1,17 +1,17 @@
/*
- * Copyright (C) 2010-2025, Danilo Pianini and contributors
+ * Copyright (C) 2010-2026, 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.protelis
+
+package it.unibo.alchemist.model.incarnations
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import it.unibo.alchemist.model.Action
import it.unibo.alchemist.model.Actionable
import it.unibo.alchemist.model.Condition
@@ -75,7 +75,6 @@ class ProtelisIncarnation> : Incarnation {
randomGenerator: RandomGenerator,
environment: Environment,
node: Node?,
- time: TimeDistribution,
actionable: Actionable,
additionalParameters: Any?,
): Action {
@@ -133,7 +132,6 @@ class ProtelisIncarnation> : Incarnation {
randomGenerator: RandomGenerator,
environment: Environment,
node: Node?,
- time: TimeDistribution,
actionable: Actionable,
additionalParameters: Any?,
): Condition {
@@ -177,7 +175,7 @@ class ProtelisIncarnation> : Incarnation {
randomGenerator: RandomGenerator,
environment: Environment,
parameter: Any?,
- ): Node = GenericNode(this, environment).apply {
+ ): Node = GenericNode(environment).apply {
addProperty(ProtelisDevice(environment, this))
}
@@ -201,11 +199,11 @@ class ProtelisIncarnation> : Incarnation {
}
parameter?.let {
result.actions =
- listOf(createAction(randomGenerator, environment, node, timeDistribution, result, it))
+ listOf(createAction(randomGenerator, environment, node, result, it))
}
if (isSend) {
result.conditions =
- listOf(createCondition(randomGenerator, environment, node, timeDistribution, result, null))
+ listOf(createCondition(randomGenerator, environment, node, result, null))
}
return result
}
@@ -248,10 +246,11 @@ class ProtelisIncarnation> : Incarnation {
private val nodeRef = WeakReference(node)
private val hash = Objects.hash(molecule, property, node)
- val node: Node get() =
- checkNotNull(nodeRef.get()) {
- "Memory management issue: a Protelis node has been garbage-collected while still in use."
- }
+ val node: Node
+ get() =
+ checkNotNull(nodeRef.get()) {
+ "Memory management issue: a Protelis node has been garbage-collected while still in use."
+ }
override fun equals(other: Any?) = other is CacheKey &&
other.nodeRef.get() === nodeRef.get() &&
@@ -293,17 +292,13 @@ class ProtelisIncarnation> : Incarnation {
}
/**
- * An [ExecutionEnvironment] that can read and shadow the content of a
+ * An [org.protelis.vm.ExecutionEnvironment] that can read and shadow the content of a
* Node, but cannot modify it. This is used to prevent badly written
* properties from interacting with the simulation flow.
*
* @param node the [Node]
*/
- class ProtectedExecutionEnvironment
- @SuppressFBWarnings(value = ["EI_EXPOSE_REP2"], justification = "This is intentional")
- constructor(
- private val node: Node<*>,
- ) : ExecutionEnvironment {
+ class ProtectedExecutionEnvironment(private val node: Node<*>) : ExecutionEnvironment {
private val shadow: ExecutionEnvironment = SimpleExecutionEnvironment()
override fun commit() = Unit
@@ -357,7 +352,7 @@ class ProtelisIncarnation> : Incarnation {
}
}
- private object NoNode : Node {
+ private data object NoNode : Node {
@Serial
private const val serialVersionUID = 1L
@@ -389,10 +384,6 @@ class ProtelisIncarnation> : Incarnation {
override fun setConcentration(molecule: Molecule, concentration: Any) = notImplemented()
- override fun equals(other: Any?): Boolean = other === this
-
- override fun hashCode(): Int = -1
-
override fun addProperty(nodeProperty: NodeProperty) = notImplemented()
/**
diff --git a/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/AlchemistExecutionContext.kt b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/AlchemistExecutionContext.kt
index e28211771b..51376a3d71 100644
--- a/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/AlchemistExecutionContext.kt
+++ b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/AlchemistExecutionContext.kt
@@ -12,7 +12,6 @@ package it.unibo.alchemist.model.protelis
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.hash.Hashing
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import it.unibo.alchemist.model.Environment
import it.unibo.alchemist.model.GeoPosition
import it.unibo.alchemist.model.Molecule
@@ -166,7 +165,6 @@ class AlchemistExecutionContext>(
override fun getCurrentTime() = reaction.tau.toDouble()
- @SuppressFBWarnings(value = ["EI_EXPOSE_REP"], justification = INTENTIONAL)
override fun getDeviceUID(): DeviceUID = protelisDevice
override fun hashCode(): Int {
@@ -304,7 +302,5 @@ class AlchemistExecutionContext
>(
* It only makes sense in case the environment is a [MapEnvironment]
*/
val APPROXIMATE_NBR_RANGE: Molecule = SimpleMolecule("APPROXIMATE_NBR_RANGE")
-
- private const val INTENTIONAL = "This is intentional"
}
}
diff --git a/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/properties/ProtelisDevice.kt b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/properties/ProtelisDevice.kt
index 2dcbd40fcc..ed13f7fcdb 100644
--- a/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/properties/ProtelisDevice.kt
+++ b/alchemist-incarnation-protelis/src/main/kotlin/it/unibo/alchemist/model/protelis/properties/ProtelisDevice.kt
@@ -13,9 +13,10 @@ import it.unibo.alchemist.model.Environment
import it.unibo.alchemist.model.Node
import it.unibo.alchemist.model.NodeProperty
import it.unibo.alchemist.model.Position
+import it.unibo.alchemist.model.Reaction
+import it.unibo.alchemist.model.incarnations.ProtelisIncarnation
import it.unibo.alchemist.model.protelis.AlchemistExecutionContext
import it.unibo.alchemist.model.protelis.AlchemistNetworkManager
-import it.unibo.alchemist.model.protelis.ProtelisIncarnation
import it.unibo.alchemist.model.protelis.actions.RunProtelisProgram
import it.unibo.alchemist.model.protelis.actions.SendToNeighbor
import org.protelis.lang.datatype.DeviceUID
diff --git a/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/model/protelis/TestIncarnation.java b/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/model/protelis/TestIncarnation.java
index ba326ae221..091009b9da 100644
--- a/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/model/protelis/TestIncarnation.java
+++ b/alchemist-incarnation-protelis/src/test/java/it/unibo/alchemist/model/protelis/TestIncarnation.java
@@ -16,6 +16,7 @@
import it.unibo.alchemist.model.Reaction;
import it.unibo.alchemist.model.TimeDistribution;
import it.unibo.alchemist.model.environments.Continuous2DEnvironment;
+import it.unibo.alchemist.model.incarnations.ProtelisIncarnation;
import it.unibo.alchemist.model.positions.Euclidean2DPosition;
import it.unibo.alchemist.model.protelis.actions.SendToNeighbor;
import it.unibo.alchemist.model.protelis.conditions.ComputationalRoundComplete;
diff --git a/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/model/protelis/TestGetPosition.kt b/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/model/protelis/TestGetPosition.kt
index c66e703e7f..b11c409e1e 100644
--- a/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/model/protelis/TestGetPosition.kt
+++ b/alchemist-incarnation-protelis/src/test/kotlin/it/unibo/alchemist/model/protelis/TestGetPosition.kt
@@ -16,6 +16,7 @@ import it.unibo.alchemist.model.Environment
import it.unibo.alchemist.model.Node.Companion.asProperty
import it.unibo.alchemist.model.Time
import it.unibo.alchemist.model.environments.Continuous2DEnvironment
+import it.unibo.alchemist.model.incarnations.ProtelisIncarnation
import it.unibo.alchemist.model.linkingrules.NoLinks
import it.unibo.alchemist.model.positions.Euclidean2DPosition
import it.unibo.alchemist.model.protelis.actions.RunProtelisProgram
diff --git a/alchemist-incarnation-sapere/build.gradle.kts b/alchemist-incarnation-sapere/build.gradle.kts
index 129508c494..89a547679f 100644
--- a/alchemist-incarnation-sapere/build.gradle.kts
+++ b/alchemist-incarnation-sapere/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2022, Danilo Pianini and contributors
+ * Copyright (C) 2010-2025, 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
@@ -14,11 +14,12 @@ plugins {
}
dependencies {
+ ksp(alchemist("factories-generator"))
api(alchemist("api"))
+ api(alchemist("sapere-mathexp"))
implementation(alchemist("implementationbase"))
implementation(alchemist("maps"))
implementation(alchemist("physics"))
- implementation(alchemist("sapere-mathexp"))
implementation(libs.boilerplate)
implementation(libs.trove4j)
}
diff --git a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/SAPEREIncarnation.java b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/incarnations/SAPEREIncarnation.java
similarity index 91%
rename from alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/SAPEREIncarnation.java
rename to alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/incarnations/SAPEREIncarnation.java
index 0c3cdb9cf9..696d465206 100644
--- a/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/sapere/SAPEREIncarnation.java
+++ b/alchemist-incarnation-sapere/src/main/java/it/unibo/alchemist/model/incarnations/SAPEREIncarnation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2023, Danilo Pianini and contributors
+ * Copyright (C) 2010-2026, 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
@@ -7,7 +7,7 @@
* as described in the file LICENSE in the Alchemist distribution's top directory.
*/
-package it.unibo.alchemist.model.sapere;
+package it.unibo.alchemist.model.incarnations;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import it.unibo.alchemist.model.Action;
@@ -20,6 +20,8 @@
import it.unibo.alchemist.model.Position;
import it.unibo.alchemist.model.Reaction;
import it.unibo.alchemist.model.TimeDistribution;
+import it.unibo.alchemist.model.sapere.ILsaMolecule;
+import it.unibo.alchemist.model.sapere.ILsaNode;
import it.unibo.alchemist.model.sapere.actions.LsaAllNeighborsAction;
import it.unibo.alchemist.model.sapere.actions.LsaRandomNeighborAction;
import it.unibo.alchemist.model.sapere.actions.LsaStandardAction;
@@ -37,6 +39,7 @@
import javax.annotation.Nullable;
import java.io.Serial;
import java.io.Serializable;
+import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -70,7 +73,7 @@ public final class SAPEREIncarnation
>
final String matchStart = "(?:\\s*(?<";
final String condition = matchStart + CONDITION_GROUP + ">\\+";
final String action = matchStart + ACTION_GROUP + ">[+*]";
- final String matchEnd = "?\\{[^\\{\\}]+?\\}))";
+ final String matchEnd = "?\\{[^{}]+?}))";
MATCH_CONDITION = Pattern.compile(condition + matchEnd);
MATCH_ACTION = Pattern.compile(action + matchEnd);
final String sequence = matchEnd + "*\\s*";
@@ -204,7 +207,7 @@ public Reaction> createReaction(
final Matcher condMatcher = MATCH_CONDITION.matcher(conditionsSpec);
while (condMatcher.find()) {
final String condition = condMatcher.group(CONDITION_GROUP);
- conditions.add(createCondition(randomGenerator, environment, node, timeDistribution, result, condition));
+ conditions.add(createCondition(randomGenerator, environment, node, result, condition));
}
} else {
illegalSpec(
@@ -219,7 +222,7 @@ public Reaction> createReaction(
final Matcher actMatcher = MATCH_ACTION.matcher(actionsSpec);
while (actMatcher.find()) {
final String action = actMatcher.group(ACTION_GROUP);
- actions.add(createAction(randomGenerator, environment, node, timeDistribution, result, action));
+ actions.add(createAction(randomGenerator, environment, node, result, action));
}
} else {
illegalSpec("not a sequence of valid conditions"
@@ -246,7 +249,6 @@ public Condition> createCondition(
final RandomGenerator randomGenerator,
final Environment, P> environment,
final Node> node,
- final TimeDistribution> time,
final Actionable