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

it.unibo.alchemist.model.reactions.AbstractReaction 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.reactions;

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;
import it.unibo.alchemist.model.Context;
import it.unibo.alchemist.model.Dependency;
import it.unibo.alchemist.model.Environment;
import it.unibo.alchemist.model.Molecule;
import it.unibo.alchemist.model.Node;
import it.unibo.alchemist.model.Reaction;
import it.unibo.alchemist.model.Time;
import it.unibo.alchemist.model.TimeDistribution;
import org.danilopianini.util.ArrayListSet;
import org.danilopianini.util.Hashes;
import org.danilopianini.util.ImmutableListSet;
import org.danilopianini.util.LinkedListSet;
import org.danilopianini.util.ListSet;
import org.danilopianini.util.ListSets;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * The type which describes the concentration of a molecule.
 * This class offers a partial implementation of Reaction. In particular, it
 * allows to write new reaction specifying only which distribution time to adopt
 * 
 * @param  concentration type
 */
public abstract class AbstractReaction implements Reaction {

    /**
     * How bigger should be the StringBuffer with respect to the previous
     * interaction.
     */
    private static final byte MARGIN = 20;
    private static final ListSet EVERYTHING = ImmutableListSet.of(Dependency.EVERYTHING);
    private static final long serialVersionUID = 1L;
    private final int hash;
    private List> actions = new ArrayList<>(0);
    private List> conditions = new ArrayList<>(0);
    private Context incontext = Context.LOCAL, outcontext = Context.LOCAL;
    private ListSet outbound = new LinkedListSet<>();
    private ListSet inbound = new LinkedListSet<>();
    private int stringLength = Byte.MAX_VALUE;
    private final TimeDistribution timeDistribution;
    private final Node node;

    /**
     * Builds a new reaction, starting at time t.
     * 
     * @param node
     *            the node this reaction belongs to
     * @param timeDistribution
     *            the time distribution this reaction should follow
     */
    @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "This is intentional")
    public AbstractReaction(final Node node, final TimeDistribution timeDistribution) {
        hash = Hashes.hash32(node.hashCode(), node.getMoleculeCount(), node.getReactions().size());
        this.timeDistribution = timeDistribution;
        this.node = node;
    }

    /**
     * Allows subclasses to add influenced molecules.
     *
     * @param m
     *            the influenced molecule
     */
    protected final void addOutboundDependency(final Dependency m) {
        outbound.add(m);
    }

    /**
     * Allows subclasses to add influencing molecules.
     *
     * @param m
     *            the molecule to add
     */
    protected final void addInboundDependency(final Dependency m) {
        inbound.add(m);
    }

    /**
     * The default implementation verifies if all the conditions are valid.
     *
     * @return true if the reaction can execute right now.
     */
    @Override
    public boolean canExecute() {
        if (conditions == null) {
            return true;
        }
        int i = 0;
        while (i < conditions.size() && conditions.get(i).isValid()) {
            i++;
        }
        return i == conditions.size();
    }

    @Override
    public final int compareTo(final Actionable o) {
        return getTau().compareTo(o.getTau());
    }

    @Override
    public final boolean equals(final Object o) {
        return this == o;
    }

    /**
     * The default execution iterates all the actions in order and executes them. Override to change the behaviour.
     */
    @Override
    public void execute() {
        for (final Action a : actions) {
            a.execute();
        }
    }

    /**
     * Override only if you need to implement extremely tricky behaviours. Must be overridden along with
     * {@link #setActions(List)}.
     *
     * @return the list of {@link Action}s.
     */
    @Nonnull
    @Override
    public List> getActions() {
        return Collections.unmodifiableList(actions);
    }

    /**
     * Override only if you need to implement extremely tricky behaviours. Must be overridden along with
     * {@link #setConditions(List)}.
     *
     * @return the list of {@link Condition}s.
     */
    @Nonnull
    @Override
    public List> getConditions() {
        return Collections.unmodifiableList(conditions);
    }

    @Nonnull
    @Override
    public final ListSet getOutboundDependencies() {
        return optionallyImmodifiableView(outbound);
    }

    @Nonnull
    @Override
    public final ListSet getInboundDependencies() {
        return optionallyImmodifiableView(inbound);
    }

    @Nonnull
    @Override
    public final Context getInputContext() {
        return incontext;
    }

    @Nonnull
    @Override
    public final Context getOutputContext() {
        return outcontext;
    }

    /**
     * @return a {@link String} representation of the rate
     */
    protected String getRateAsString() {
        return Double.toString(timeDistribution.getRate());
    }

    /**
     * This method is used to provide a reaction name in toString().
     *
     * @return the name for this reaction.
     */
    protected String getReactionName() {
        return getClass().getSimpleName();
    }

    @Nonnull
    @Override
    public final Time getTau() {
        return timeDistribution.getNextOccurence();
    }

    @Nonnull
    @Override
    public final TimeDistribution getTimeDistribution() {
        return timeDistribution;
    }

    @Override
    public final int hashCode() {
        return hash;
    }

    @Override
    public void initializationComplete(@Nonnull final Time atTime, @Nonnull final Environment environment) { }

    /**
     * This method provides facility to clone reactions. Given a constructor in
     * form of a {@link Supplier}, it populates the actions and conditions with
     * cloned version of the ones registered in this reaction.
     *
     * @param builder
     *            the supplier
     *
     * @param 
     *            The reaction type
     * @return the populated cloned reaction
     */
    protected > R makeClone(final Supplier builder) {
        final R res = builder.get();
        final Node n = res.getNode();
        final ArrayList> c = new ArrayList<>(conditions.size());
        for (final Condition cond : getConditions()) {
            c.add(cond.cloneCondition(n, res));
        }
        final ArrayList> a = new ArrayList<>(actions.size());
        for (final Action act : getActions()) {
            a.add(act.cloneAction(n, res));
        }
        res.setActions(a);
        res.setConditions(c);
        return res;
    }

    private static ListSet computeDependencies(final Stream stream) {
        final Iterator fromStream = stream.iterator();
        boolean everyMolecule = false;
        final ListSet result = new ArrayListSet<>();
        while (fromStream.hasNext()) {
            final Dependency dependency = fromStream.next();
            if (dependency.equals(Dependency.EVERYTHING)) {
                return EVERYTHING;
            }
            if (dependency.equals(Dependency.EVERY_MOLECULE) && !everyMolecule) {
                result.removeIf(it -> it instanceof Molecule);
                everyMolecule = true;
                result.add(Dependency.EVERY_MOLECULE);
            } else if (!(everyMolecule && dependency instanceof Molecule)) {
                result.add(dependency);
            }
        }
        return result;
    }

    /**
     * This should get overridden only if very tricky behaviours are implemented, such that the default Alchemist
     * action addition model is no longer usable. Must be overridden along with {@link #getActions()}.
     *
     * @param actions the actions to set
     */
    @Override
    public void setActions(@Nonnull final List> actions) {
        this.actions = Objects.requireNonNull(actions, "The actions list can't be null");
        setOutputContext(actions.stream().map(Action::getContext).reduce(Context.LOCAL, Context::getWider));
        outbound = computeDependencies(actions.stream().map(Action::getOutboundDependencies).flatMap(List::stream));
    }

    /**
     * This should get overridden only if very tricky behaviours are implemented, such that the default Alchemist
     * condition addition model is no longer usable. Must be overridden along with {@link #getConditions()}.
     *
     * @param conditions the actions to set
     */
    @Override
    public void setConditions(@Nonnull final List> conditions) {
        this.conditions = Objects.requireNonNull(conditions, "The conditions list can't be null");
        setInputContext(conditions.stream().map(Condition::getContext).reduce(Context.LOCAL, Context::getWider));
        inbound = computeDependencies(conditions.stream().map(Condition::getInboundDependencies).flatMap(List::stream));
    }

    /**
     * Used by subclasses to set their input context.
     * 
     * @param c
     *            the new input context
     */
    protected final void setInputContext(final Context c) {
        incontext = c;
    }

    /**
     * Used by subclasses to set their output context.
     * 
     * @param c
     *            the new input context
     */
    protected final void setOutputContext(final Context c) {
        outcontext = c;
    }

    /**
     * @return the default implementation returns a String in the form
     * className@timeScheduled[Conditions]-rate->[Actions]
     */
    @Override
    public String toString() {
        final StringBuilder tot = new StringBuilder(stringLength + MARGIN)
            .append(getReactionName())
            .append('@')
            .append(getTau())
            .append(':')
            .append(getConditions())
            .append('-')
            .append(getRateAsString())
            .append("->")
            .append(getActions());
        stringLength = tot.length();
        return tot.toString();
    }

    @Override
    public final void update(
        @Nonnull final Time currentTime,
        final boolean hasBeenExecuted,
        @Nonnull final Environment environment
    ) {
        updateInternalStatus(currentTime, hasBeenExecuted, environment);
        timeDistribution.update(currentTime, hasBeenExecuted, getRate(), environment);
    }

    /**
     * This method gets called as soon as
     * {@link #update(Time, boolean, Environment)} is called. It is useful to
     * update the internal status of the reaction.
     * 
     * @param currentTime
     *            the current simulation time
     * @param hasBeenExecuted
     *            true if this reaction has just been executed, false if the
     *            update has been triggered due to a dependency
     * @param environment
     *            the current environment
     */
    protected abstract void updateInternalStatus(
            Time currentTime,
            boolean hasBeenExecuted,
            Environment environment
    );

    @Nonnull
    @Override
    @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "This is intentional")
    public final Node getNode() {
        return node;
    }


    private static  ListSet optionallyImmodifiableView(final ListSet in) {
        return in == null ? null : ListSets.unmodifiableListSet(in);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy