Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9a950a8
feat!: add a reactive engine (#5066)
S-furi Jan 19, 2026
aca51b8
docs: improve Javadoc
S-furi Jan 19, 2026
5a0850f
chore(api): provide a way to register to an observable without trigge…
S-furi Jan 21, 2026
4e59b41
chore(biochemestry): port `BiomolPresentInNeighbor` to kotlin
S-furi Jan 21, 2026
29fca73
feat!: fully transition to observable neighborhoods
S-furi Jan 21, 2026
c425552
feat!: make node counting observable
S-furi Jan 21, 2026
5995c80
fix(biochemistry): use current node count in assertions
S-furi Jan 22, 2026
878efbc
chore(api): add `updateOrNull` with observable map values
S-furi Jan 22, 2026
c7d8009
feat!: fully transition to observable positions
S-furi Jan 22, 2026
a65d0eb
chore(api): add way to register to observables without sending initia…
S-furi Jan 22, 2026
bce4e43
fix(scafi): avoid depending on observables
S-furi Jan 22, 2026
99774bd
feat!: replace Engine with reactive counterpart and get rid of depend…
S-furi Jan 22, 2026
443eb01
fix: properly implement and call `dispose` on nodes and reactions
S-furi Jan 29, 2026
02585df
chore: try avoid memory leaks for region observers
S-furi Jan 29, 2026
d5a4786
feat(api): improve lapsed listener problem avoidance with `Lifecycle`
S-furi Jan 29, 2026
e65ac89
chore(api): cleanup conditions if conditions are set mulitple times
S-furi Jan 30, 2026
4a75a51
refactor(api): extract lifecycle components into multiple files
S-furi Feb 10, 2026
327a501
fix: fix line length
S-furi Feb 10, 2026
511af3b
chore(docs): remove useless whitespace
S-furi Feb 13, 2026
217ceb7
fix(cognitive-agents): use `getCurrentPosition` to retireve node's ac…
S-furi Feb 15, 2026
eebb7a7
test(engine): simplify engine test suite (fixed detekt error)
S-furi Feb 15, 2026
9006e78
chore(implementation-base): suppress `AbstractClassCanBeConcreteClass…
S-furi Feb 15, 2026
b4e7e90
fix(test): retrieve current position from observable when asserting
S-furi Feb 16, 2026
04c1930
fix(sapere): trigger LSAReaction update only on target molecule chang…
S-furi Feb 19, 2026
f1cb76e
fix(implementation-base): merge conditions into single observable source
S-furi Feb 19, 2026
0e3490a
fix(sapere): use generalised molecules for dsl expressions test failure
S-furi Feb 19, 2026
72584f5
fix(sapere): keep observing for neighbrhoods LSA spaces changes
S-furi Feb 19, 2026
f0bc2f5
fix: fix checkstyle error
S-furi Feb 19, 2026
af2a3ac
perf: improve speed, SAPERE fine-grained dependencies and memory usage
S-furi Feb 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions alchemist-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ dependencies {
api(libs.jool)
api(libs.listset)
implementation(libs.kotlin.reflect)
api(libs.arrow.core)
api(libs.kotlinx.collections.immutable)
testImplementation(libs.kotlin.test)
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
package it.unibo.alchemist.model;

import it.unibo.alchemist.core.Simulation;
import it.unibo.alchemist.model.observation.Disposable;
import it.unibo.alchemist.model.observation.Observable;
import it.unibo.alchemist.model.observation.ObservableSet;
import org.danilopianini.util.ListSet;

import java.io.Serializable;
Expand All @@ -20,7 +23,7 @@
* @param <T>
* The type which describes the concentration of a molecule
*/
public interface Condition<T> extends Serializable {
public interface Condition<T> extends Serializable, Disposable {

/**
* This method allows cloning this action on a new node. It may result
Expand All @@ -46,6 +49,12 @@ public interface Condition<T> extends Serializable {
*/
ListSet<? extends Dependency> getInboundDependencies();

/**
* @return The set of dependencies which may influence the truth value of this
* condition, as an {@link ObservableSet} of {@link Observable}
*/
ObservableSet<? extends Observable<?>> observeInboundDependencies();

/**
* @return the node this Condition belongs to
*/
Expand All @@ -61,11 +70,24 @@ public interface Condition<T> extends Serializable {
*/
double getPropensityContribution();

/**
* An observable and reactive view of the corresponding {@link #getPropensityContribution()}.
*
* @return an observable view of how this condition may influence the propensity.
*/
Observable<Double> observePropensityContribution();

/**
* @return true if the condition is satisfied in the current environment.
*/
boolean isValid();

/**
* @return an observable that emits true if the condition is satisfied in the
* current environment.
*/
Observable<Boolean> observeValidity();

/**
* This method is called by the {@link Simulation} once the {@link Reaction}
* whose this {@link Condition} belongs to is the next one to be executed, and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

package it.unibo.alchemist.model

import it.unibo.alchemist.model.observation.Disposable
import it.unibo.alchemist.model.observation.Observable
import it.unibo.alchemist.model.observation.lifecycle.LifecycleOwner
import java.io.Serializable
import org.danilopianini.util.ListSet

Expand All @@ -17,13 +20,23 @@ import org.danilopianini.util.ListSet
*/
sealed interface Actionable<T> :
Comparable<Actionable<T>>,
Serializable {
Serializable,
Disposable,
LifecycleOwner {
/**
* @return true if the reaction can be executed (namely, all the conditions
* are satisfied).
*/
fun canExecute(): Boolean

/**
* Observes whether the reaction can be executed. This observable emits updates
* to indicate if the conditions required for execution are satisfied.
*
* @return An [Observable] emitting true if the reaction van be executed, false otherwise.
*/
fun observeCanExecute(): Observable<Boolean>

/**
* Executes the reactions.
*/
Expand Down Expand Up @@ -88,6 +101,12 @@ sealed interface Actionable<T> :
*/
val timeDistribution: TimeDistribution<T>

/**
* Emits when this reaction requests the [Scheduler][it.unibo.alchemist.core.Scheduler]
* to reschedule this reaction.
*/
val rescheduleRequest: Observable<Unit>

/**
* Updates the scheduling of this reaction.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
package it.unibo.alchemist.model

import it.unibo.alchemist.core.Simulation
import it.unibo.alchemist.model.observation.Observable
import it.unibo.alchemist.model.observation.ObservableSet
import java.io.Serializable
import org.danilopianini.util.ListSet

Expand All @@ -18,6 +20,7 @@ import org.danilopianini.util.ListSet
* Every environment must implement this specification.
* [T] is the [Concentration] type, [P] is the [Position] type.
*/
@Suppress("TooManyFunctions")
interface Environment<T, P : Position<out P>> :
Serializable,
Iterable<Node<T>> {
Expand Down Expand Up @@ -91,9 +94,10 @@ interface Environment<T, P : Position<out P>> :
var linkingRule: LinkingRule<T, P>

/**
* Given a [node], this method returns its neighborhood.
* Given a [node], this method returns an observable view of
* its neighborhood.
*/
fun getNeighborhood(node: Node<T>): Neighborhood<T>
fun getNeighborhood(node: Node<T>): Observable<Neighborhood<T>>

/**
* Allows accessing a [Node] in this [Environment] known its [id].
Expand All @@ -108,9 +112,14 @@ interface Environment<T, P : Position<out P>> :
val nodes: ListSet<Node<T>>

/**
* Returns the number of [Node]s currently in the [Environment].
* An [Observable] view of all the [Node]s that exist in current [Environment].
*/
val nodeCount: Int
val observableNodes: ObservableSet<Node<T>>

/**
* Returns an [Observable] view of the number of [Node]s currently in the [Environment].
*/
val nodeCount: Observable<Int>

/**
* Given a [node] this method returns a list of all the surrounding
Expand All @@ -122,6 +131,11 @@ interface Environment<T, P : Position<out P>> :
*/
fun getNodesWithinRange(node: Node<T>, range: Double): ListSet<Node<T>>

/**
* An [Observable] alternative to [getNodesWithinRange].
*/
fun observeNodesWithinRange(node: Node<T>, range: Double): ObservableSet<Node<T>>

/**
* Given a [position] this method returns a list of all the
* surrounding nodes within the given [range].
Expand All @@ -130,6 +144,11 @@ interface Environment<T, P : Position<out P>> :
*/
fun getNodesWithinRange(position: P, range: Double): ListSet<Node<T>>

/**
* An [Observable] alternative to [getNodesWithinRange].
*/
fun observeNodesWithinRange(position: P, range: Double): ObservableSet<Node<T>>

/**
* This method allows to know which are the smallest coordinates represented.
* Return an array of length [dimensions] containing the smallest
Expand All @@ -138,9 +157,14 @@ interface Environment<T, P : Position<out P>> :
val offset: DoubleArray

/**
* Calculates the position of a [node].
* Observe the position of a [node].
*/
fun getPosition(node: Node<T>): Observable<P>

/**
* Retrieves [node]'s current position.
*/
fun getPosition(node: Node<T>): P
fun getCurrentPosition(node: Node<T>): P = getPosition(node).current

/**
* Return the current [Simulation], if present, or throws an [IllegalStateException] otherwise.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface EuclideanEnvironment<T, P> : Environment<T, P> where P : Position<P>,
* method may suffice.
*/
fun moveNode(node: Node<T>, direction: P) {
val oldcoord = getPosition(node)
val oldcoord = getCurrentPosition(node)
moveNodeToPosition(node, oldcoord.plus(direction))
}

Expand Down
37 changes: 36 additions & 1 deletion alchemist-api/src/main/kotlin/it/unibo/alchemist/model/Node.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
*/
package it.unibo.alchemist.model

import arrow.core.Option
import it.unibo.alchemist.model.observation.Disposable
import it.unibo.alchemist.model.observation.Observable
import it.unibo.alchemist.model.observation.ObservableMap
import it.unibo.alchemist.model.observation.lifecycle.LifecycleOwner
import java.io.Serializable
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
Expand All @@ -22,7 +27,9 @@ import kotlin.reflect.jvm.jvmErasure
interface Node<T> :
Serializable,
Iterable<Reaction<T>>,
Comparable<Node<T>> {
Comparable<Node<T>>,
Disposable,
LifecycleOwner {
/**
* Adds a reaction to this node.
* The reaction is added only in the node,
Expand Down Expand Up @@ -60,6 +67,14 @@ interface Node<T> :
*/
operator fun contains(molecule: Molecule): Boolean

/**
* Observes whether a node contains a [Molecule].
*
* @param molecule * the molecule to check
* @return emit true if the molecule is present, false otherwise
*/
fun observeContains(molecule: Molecule): Observable<Boolean>

/**
* Calculates the concentration of a molecule.
*
Expand All @@ -69,11 +84,26 @@ interface Node<T> :
*/
fun getConcentration(molecule: Molecule): T

/**
* Observe the concentration calculated with the given molecule.
* The result of this computation is wrapped into an [Option] and
* [some][arrow.core.Some] if the value is present, otherwise
* [none][arrow.core.None].
*
* @param molecule the molecule whose concentration will be returned
*/
fun observeConcentration(molecule: Molecule): Observable<Option<T>>

/**
* @return the molecule corresponding to the i-th position
*/
val contents: Map<Molecule, T>

/**
* @return an observable view of the molecule corresponding to the i-th position
*/
val observableContents: ObservableMap<Molecule, T>

/**
* @return an univocal id for this node in the environment
*/
Expand All @@ -84,6 +114,11 @@ interface Node<T> :
*/
val moleculeCount: Int

/**
* Observes the count of different molecules in this node.
*/
val observeMoleculeCount: Observable<Int>

/**
* @return a list of the node's properties/capabilities
*/
Expand Down
Loading
Loading