it.unibo.alchemist.modelchecker.AlchemistASMC.java Source code

Java tutorial

Introduction

Here is the source code for it.unibo.alchemist.modelchecker.AlchemistASMC.java

Source

/*
 * Copyright (C) 2010-2014, Danilo Pianini and contributors
 * listed in the project's pom.xml 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.modelchecker;

import it.unibo.alchemist.core.executors.MultithreadedExecutor;
import it.unibo.alchemist.core.interfaces.ISimulation;
import it.unibo.alchemist.external.cern.jet.random.engine.MersenneTwister;
import it.unibo.alchemist.external.cern.jet.random.engine.RandomEngine;
import it.unibo.alchemist.language.EnvironmentBuilder;
import it.unibo.alchemist.modelchecker.interfaces.ASMCListener;
import it.unibo.alchemist.modelchecker.interfaces.Property;
import it.unibo.alchemist.modelchecker.interfaces.PropertyAggregator;
import it.unibo.alchemist.modelchecker.interfaces.PropertyAggregatorVariance;
import it.unibo.alchemist.utils.L;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.math3.analysis.UnivariateFunction;
import org.apache.commons.math3.analysis.solvers.BisectionSolver;
import org.apache.commons.math3.distribution.TDistribution;
import org.apache.commons.math3.util.FastMath;
import org.xml.sax.SAXException;

/**
 * 
 * Approximate Model Checker. See Alchemist Manual for statistical reference.
 * This is an abstract class. Use AlchemistASMCBoolean to deal with boolean
 * values, or AlchemistASMCNumeric to deal with numbers.
 * 
 * @author Danilo Pianini
 * @author Davide Ensini
 * 
 * 
 * @param <T>
 *            Concentration type
 * @param <D>
 *            Property result type
 * @param <R>
 *            Final result type
 */
public abstract class AlchemistASMC<T, D, R> {

    private static final double LOG_MUL = 0.5;
    private final PropertyAggregator<R, D> aggregator;
    private final double a;
    private final double d;
    private final Semaphore exec = new Semaphore(0);
    private final int minN;
    private final int maxN;
    private static final int SAMPLESTEP = 30;
    private final MultithreadedExecutor<T> mx = new MultithreadedExecutor<T>() {
        @Override
        protected void configureSimulation(final ISimulation<T> s) {
            final Property<T, ?, D> pclone = property.clone();
            s.addOutputMonitor(pclone);
            getpList().add(pclone);
        }
    };
    private int nr;
    private final List<Property<T, ?, D>> pList;
    private final Property<T, ?, D> property;
    private final List<ASMCListener> listeners = new LinkedList<>();

    /**
     * Given the approximation and the number of runs to execute, computes a
     * lower bound for confidence (see manual).
     * 
     * @param delta
     *            approximation
     * @param n
     *            number of runs
     * @return confidence
     */
    public static double computeAlphaUB(final double delta, final int n) {
        return 2 * Math.exp(-delta * delta * n / LOG_MUL);
    }

    /**
     * Given the runs and the confidence, computes an upper bound for the
     * approximation (see manual).
     * 
     * @param n
     *            number of runs
     * @param alpha
     *            confidence
     * @return the approximation
     */
    public static double computeDeltaUB(final int n, final double alpha) {
        return Math.sqrt((LOG_MUL * Math.log(2 / alpha)) / n);
    }

    /**
     * Given the approximation and the confidence, computes an upper bound for
     * the number of runs to execute (see manual).
     * 
     * @param delta
     *            approximation
     * @param alpha
     *            confidence
     * @return number of runs
     */
    public static int computeSampleSizeUB(final double delta, final double alpha) {
        return (int) Math.ceil(LOG_MUL * Math.log(2 / alpha) / (delta * delta));
    }

    /**
     * Construct an instance with given parameters.
     * 
     * @param delta
     *            approximation
     * @param alpha
     *            confidence
     * @param p
     *            property to verify
     * @param pa
     *            property aggregator to use
     */
    protected AlchemistASMC(final double delta, final double alpha, final Property<T, ?, D> p,
            final PropertyAggregator<R, D> pa) {
        this(delta, alpha, p, pa, Math.min(computeMinimum(delta, alpha), computeSampleSizeUB(delta, alpha)));
    }

    /**
     * Construct an instance with given parameters, specifiyng minimum sample
     * size.
     * 
     * @param delta
     *            approximation
     * @param alpha
     *            confidence
     * @param p
     *            property to verify
     * @param pa
     *            property aggregator to use
     * @param min
     *            Minimum sample size
     */
    protected AlchemistASMC(final double delta, final double alpha, final Property<T, ?, D> p,
            final PropertyAggregator<R, D> pa, final int min) {
        d = delta;
        a = alpha;
        minN = min;
        maxN = computeSampleSizeUB(delta, alpha);
        property = p;
        aggregator = pa;
        pList = Collections.synchronizedList(new ArrayList<Property<T, ?, D>>(min));
    }

    /**
     * @param xmlFilePath
     *            Alchemist XML specification to execute
     * @param steps
     *            maximum length of the simulation in steps
     * @param finalTime
     *            maximum length of the simulation in simulated time units
     */
    public void execute(final String xmlFilePath, final long steps, final double finalTime) {
        final ExecutorService ex = Executors.newSingleThreadExecutor();
        ex.execute(new Runnable() {
            public void run() {
                try {
                    nr = minN;
                    final byte[] ba = mx.serializedEnvironment(xmlFilePath);
                    EnvironmentBuilder<T> ebo;
                    ebo = new EnvironmentBuilder<>(xmlFilePath);
                    ebo.buildEnvironment();
                    final RandomEngine random = new MersenneTwister();
                    random.setSeed(ebo.getRandomEngine().getSeed());

                    mx.addJob(ba, random, minN, steps, finalTime);
                    mx.waitForCompletion();
                    notifyASMCListeners();
                    while (!stopCondition()) {
                        nr += SAMPLESTEP;
                        // pList.ensureCapacity(INCREASE);
                        mx.addJob(ba, random, SAMPLESTEP, steps, finalTime);
                        mx.waitForCompletion();
                        notifyASMCListeners();
                    }
                    mx.destroy();
                    exec.release();
                } catch (InstantiationException | IllegalAccessException | InvocationTargetException
                        | ClassNotFoundException | SAXException | IOException | ParserConfigurationException e) {
                    L.error(e);
                }
            }

        });

    }

    /**
     * @return the confidence
     */
    public double getAlpha() {
        return a;
    }

    /**
     * @return the approximation
     */
    public double getDelta() {
        return d;
    }

    /**
     * @return sample size (so far)
     */
    public int getN() {
        return nr;
    }

    /**
     * Waits for all the simulations to finish and returns the aggregated
     * result.
     * 
     * @return the final aggregated result
     */
    public R getResult() {
        waitForCompletion();
        return aggregator.aggregate(getpList());
    }

    /**
     * Waits until all the queued jobs are completed.
     */
    public void waitForCompletion() {
        try {
            exec.acquire();
        } catch (InterruptedException e) {
            L.error(e);
        }
    }

    private static int computeMinimum(final double interval, final double confidence) {
        final UnivariateFunction f = new UnivariateFunction() {
            @Override
            public double value(final double n) {
                double t;
                if (Math.ceil(n) == FastMath.floor(n)) {
                    t = new TDistribution((int) n).inverseCumulativeProbability(1 - confidence / 2);
                } else {
                    double t1 = new TDistribution((int) FastMath.ceil(n))
                            .inverseCumulativeProbability((1 - confidence / 2)) * (n - Math.floor(n));
                    double t2 = new TDistribution((int) FastMath.floor(n))
                            .inverseCumulativeProbability((1 - confidence / 2)) * (Math.ceil(n) - n);
                    t = t1 + t2;
                }
                double value = 2 * t / n;
                return value - interval;
            }
        };
        final BisectionSolver bs = new BisectionSolver();
        return (int) Math.ceil(bs.solve(Integer.MAX_VALUE, f, 1, Integer.MAX_VALUE));
    }

    /**
     * Checks whether obtained confidence interval size meets requirement.
     * 
     * @return True iff interval size is little enough
     */
    protected boolean intervalSizeReached() {
        final double s = ((PropertyAggregatorVariance<R, D>) aggregator).getS(getpList());
        final double d0 = computeDeltaDynamic(s, nr, a);
        return d0 < d;
    }

    /**
     * Computes interval size according to dynamic criterion (see manual). A
     * lower size interval could be obtained with computeDeltaStatic.
     * 
     * @param sPar
     *            Value of variance function s (see manual)
     * @param sampleSize
     *            The number of simulated instances
     * @param alpha
     *            confidence
     * @return Interval size
     */
    public static double computeDeltaDynamic(final double sPar, final int sampleSize, final double alpha) {
        final double t = new TDistribution(sampleSize - 1).inverseCumulativeProbability(1 - alpha / 2);
        return 2 * t * sPar / Math.sqrt(sampleSize);
    }

    /**
     * Computes interval size according to static criterion (see manual). A
     * lower size interval could be obtained with computeDeltaDynamic.
     * 
     * @param sampleSize
     *            The number of simulated instances
     * @param alpha
     *            confidence
     * @return Interval size
     */
    public static double computeDeltaStatic(final int sampleSize, final double alpha) {
        return computeDeltaUB(sampleSize, alpha);
    }

    /**
     * @return true when there's no need to execute more simulations
     */
    protected abstract boolean stopCondition();

    /**
     * @return minimum sample size, wheter computed or imposed by user
     */
    public int getMinN() {
        return minN;
    }

    /**
     * @return maximum sample size from the upper bound
     */
    public int getMaxN() {
        return maxN;
    }

    /**
     * @return the property aggregator in use
     */
    public PropertyAggregator<R, D> getAggregator() {
        return aggregator;
    }

    /**
     * @param l
     *            The ASMCListener to register
     */
    public void addASMCListener(final ASMCListener l) {
        getListeners().add(l);
    }

    /**
     * 
     */
    protected abstract void notifyASMCListeners();

    /**
     * @return the pList
     */
    protected List<Property<T, ?, D>> getpList() {
        return pList;
    }

    /**
     * @return the listeners
     */
    protected List<ASMCListener> getListeners() {
        return listeners;
    }
}