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
1d8fd64
feat!: add a reactive engine (#5066)
S-furi Jan 19, 2026
0d42889
docs: improve Javadoc
S-furi Jan 19, 2026
cb42aeb
chore(api): provide a way to register to an observable without trigge…
S-furi Jan 21, 2026
c053a35
chore(biochemestry): port `BiomolPresentInNeighbor` to kotlin
S-furi Jan 21, 2026
bfd73f0
feat!: fully transition to observable neighborhoods
S-furi Jan 21, 2026
a75c7c7
feat!: make node counting observable
S-furi Jan 21, 2026
024753b
fix(biochemistry): use current node count in assertions
S-furi Jan 22, 2026
db6b99a
chore(api): add `updateOrNull` with observable map values
S-furi Jan 22, 2026
4178481
feat!: fully transition to observable positions
S-furi Jan 22, 2026
6b4bfe5
chore(api): add way to register to observables without sending initia…
S-furi Jan 22, 2026
9247008
fix(scafi): avoid depending on observables
S-furi Jan 22, 2026
ebd5384
feat!: replace Engine with reactive counterpart and get rid of depend…
S-furi Jan 22, 2026
0b80ee2
fix: properly implement and call `dispose` on nodes and reactions
S-furi Jan 29, 2026
3f7a92e
chore: try avoid memory leaks for region observers
S-furi Jan 29, 2026
ff850ce
feat(api): improve lapsed listener problem avoidance with `Lifecycle`
S-furi Jan 29, 2026
bc5312a
chore(api): cleanup conditions if conditions are set mulitple times
S-furi Jan 30, 2026
e89d56e
refactor(api): extract lifecycle components into multiple files
S-furi Feb 10, 2026
0f9764f
fix: fix line length
S-furi Feb 10, 2026
af319d5
chore(docs): remove useless whitespace
S-furi Feb 13, 2026
3d0136f
fix(cognitive-agents): use `getCurrentPosition` to retireve node's ac…
S-furi Feb 15, 2026
5947047
test(engine): simplify engine test suite (fixed detekt error)
S-furi Feb 15, 2026
13562ea
chore(implementation-base): suppress `AbstractClassCanBeConcreteClass…
S-furi Feb 15, 2026
2b3c44d
fix(test): retrieve current position from observable when asserting
S-furi Feb 16, 2026
6f618f2
fix(sapere): trigger LSAReaction update only on target molecule chang…
S-furi Feb 19, 2026
70c6c7f
fix(implementation-base): merge conditions into single observable source
S-furi Feb 19, 2026
bf4873f
fix(sapere): use generalised molecules for dsl expressions test failure
S-furi Feb 19, 2026
75ce513
fix(sapere): keep observing for neighbrhoods LSA spaces changes
S-furi Feb 19, 2026
5bd54fb
fix: fix checkstyle error
S-furi Feb 19, 2026
0186db6
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