net.sf.markov4jmeter.behaviormodelextractor.extraction.transformation.clustering.AbstractClusteringStrategy.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.markov4jmeter.behaviormodelextractor.extraction.transformation.clustering.AbstractClusteringStrategy.java

Source

/***************************************************************************
 * Copyright (c) 2016 the WESSBAS project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

package net.sf.markov4jmeter.behaviormodelextractor.extraction.transformation.clustering;

import java.io.File;
import java.math.BigDecimal;
import java.util.LinkedList;
import java.util.List;

import net.sf.markov4jmeter.behavior.BehaviorFactory;
import net.sf.markov4jmeter.behavior.BehaviorMix;
import net.sf.markov4jmeter.behavior.BehaviorMixEntry;
import net.sf.markov4jmeter.behavior.BehaviorModelAbsolute;
import net.sf.markov4jmeter.behavior.BehaviorModelRelative;
import net.sf.markov4jmeter.behavior.Transition;
import net.sf.markov4jmeter.behavior.UseCase;
import net.sf.markov4jmeter.behavior.UseCaseRepository;
import net.sf.markov4jmeter.behavior.Vertex;
import net.sf.markov4jmeter.behaviormodelextractor.CommandLineArgumentsHandler;
import net.sf.markov4jmeter.behaviormodelextractor.extraction.ExtractionException;
import net.sf.markov4jmeter.behaviormodelextractor.util.MathUtil;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.ArffSaver;
import weka.filters.Filter;

/**
 * Abstract base class for all clustering strategies.
 * 
 * <p>
 * This class defines the abstract
 * {@link #apply(BehaviorModelRelative[], UseCaseRepository)} method, which has
 * to be implemented by each subclass, according to the associated clustering
 * strategy. Furthermore, helping methods for searching and creating Behavior
 * Model elements are provided.
 * 
 * @author Eike Schulz (esc@informatik.uni-kiel.de)
 * @version 1.0
 */
public abstract class AbstractClusteringStrategy {

    /* ***************************** constants **************************** */

    /**
     * Generic name for generated Behavior Models; if multiple models are
     * generated, an index will be appended to this name.
     */
    protected final static String GENERIC_BEHAVIOR_MODEL_NAME = "gen_behavior_model";

    /* ************************** public methods ************************** */

    /**
     * Applies the associated clustering strategy to a given set of Behavior
     * Models.
     * 
     * @param behaviorModelsRelative
     *            set of input Behavior Models on which the clustering strategy
     *            shall be applied.
     * @param useCaseRepository
     *            use case repository which provides all available use cases;
     *            the use cases generally indicate the states ("vertices") of
     *            each Behavior Model, with a dedicated final state in addition.
     *            Final states are not associated with any use case, indicated
     *            by their regarding attribute being set to <code>null</code>.
     * 
     * @return the resulting Behavior Mix; each Behavior Mix entry includes a
     *         (generated) name of its related Behavior Model, a computed
     *         relative frequency and a reference to the Behavior Model itself.
     * 
     * @throws ExtractionException
     *             if any clustering error occurs.
     */
    public abstract BehaviorMix apply(final BehaviorModelAbsolute[] behaviorModelsAbsolute,
            final UseCaseRepository useCaseRepository) throws ExtractionException;

    /* ************************* protected methods ************************ */

    /**
     * Searches for a Behavior Model transition between two use cases,
     * respectively their associated vertices.
     * 
     * @param behaviorModelRelative
     *            Behavior Model to be searched through.
     * @param srcUseCaseId
     *            identifier of the use case which is associated with the source
     *            vertex.
     * @param dstUseCaseId
     *            identifier of the use case which is associated with the target
     *            vertex; <code>null</code> might be passed for the final state.
     * 
     * @return a matching transition, or <code>null</code> if such a transition
     *         does not exist.
     */
    protected Transition findTransitionByUseCaseIDs(final BehaviorModelAbsolute behaviorModelAbsolute,
            final String srcUseCaseId, final String dstUseCaseId) {

        final Vertex srcVertex = this.findVertexByUseCaseId(behaviorModelAbsolute, srcUseCaseId);

        if (srcVertex != null) {

            for (final Transition transition : srcVertex.getOutgoingTransitions()) {

                final UseCase useCase = transition.getTargetVertex().getUseCase();

                if ((useCase != null && useCase.getId().equals(dstUseCaseId))
                        || (useCase == null && dstUseCaseId == null)) {

                    return transition;
                }
            }
        }

        return null; // no matching transition found;
    }

    /**
     * Searches for a Behavior Model transition between two use cases,
     * respectively their associated vertices.
     * 
     * @param behaviorModelRelative
     *            Behavior Model to be searched through.
     * @param srcUseCaseId
     *            identifier of the use case which is associated with the source
     *            vertex.
     * @param dstUseCaseId
     *            identifier of the use case which is associated with the target
     *            vertex; <code>null</code> might be passed for the final state.
     * 
     * @return a matching transition, or <code>null</code> if such a transition
     *         does not exist.
     */
    protected Transition findTransitionByUseCaseIDs(final BehaviorModelRelative behaviorModelRelative,
            final String srcUseCaseId, final String dstUseCaseId) {

        final Vertex srcVertex = this.findVertexByUseCaseId(behaviorModelRelative, srcUseCaseId);

        if (srcVertex != null) {

            for (final Transition transition : srcVertex.getOutgoingTransitions()) {

                final UseCase useCase = transition.getTargetVertex().getUseCase();

                if ((useCase != null && useCase.getId().equals(dstUseCaseId))
                        || (useCase == null && dstUseCaseId == null)) {

                    return transition;
                }
            }
        }

        return null; // no matching transition found;
    }

    /**
     * Searches for a Behavior Model vertex which is associated with a specific
     * use case or with the final state.
     * 
     * @param behaviorModelRelative
     *            Behavior Model whose vertices shall be searched through.
     * @param useCaseId
     *            an identifier of the use case which is associated with the
     *            vertex to be searched, or <code>null</code> for searching the
     *            vertex that represents the final state.
     * 
     * @return a matching vertex, or <code>null</code> if such a vertex does not
     *         exist.
     */
    protected Vertex findVertexByUseCaseId(final BehaviorModelRelative behaviorModelRelative,
            final String useCaseId) {

        final List<Vertex> vertices = behaviorModelRelative.getVertices();

        for (final Vertex vertex : vertices) {

            final UseCase vertexUseCase = vertex.getUseCase();

            if (vertexUseCase != null) {

                final String vertexUseCaseId = vertexUseCase.getId();

                if (useCaseId.equals(vertexUseCaseId)) {

                    return vertex;
                }

            } else { // vertexUseCase == null --> final state found;

                if (useCaseId == null) { // searching for final state?

                    return vertex;
                }
            }
        }

        return null; // no matching vertex found;
    }

    /**
     * Searches for a Behavior Model Absolut vertex which is associated with a
     * specific use case or with the final state.
     * 
     * @param behaviorModelAbsolut
     *            Behavior Model whose vertices shall be searched through.
     * @param useCaseId
     *            an identifier of the use case which is associated with the
     *            vertex to be searched, or <code>null</code> for searching the
     *            vertex that represents the final state.
     * 
     * @return a matching vertex, or <code>null</code> if such a vertex does not
     *         exist.
     */
    protected Vertex findVertexByUseCaseId(final BehaviorModelAbsolute behaviorModelAbsolute,
            final String useCaseId) {

        final List<Vertex> vertices = behaviorModelAbsolute.getVertices();

        for (final Vertex vertex : vertices) {

            final UseCase vertexUseCase = vertex.getUseCase();

            if (vertexUseCase != null) {

                final String vertexUseCaseId = vertexUseCase.getId();

                if (useCaseId.equals(vertexUseCaseId)) {

                    return vertex;
                }

            } else { // vertexUseCase == null --> final state found;

                if (useCaseId == null) { // searching for final state?

                    return vertex;
                }
            }
        }

        return null; // no matching vertex found;
    }

    /**
     * Creates a Behavior Model which includes the vertices for set of given use
     * cases.
     * 
     * @param useCases
     *            set of use cases which indicate the vertices to be included to
     *            the Behavior Model; a vertex that represents the final state
     *            will be added explicitly.
     * 
     * @return the newly created Behavior Model.
     */
    protected BehaviorModelRelative createBehaviorModelRelativeWithoutTransitions(final List<UseCase> useCases) {

        final BehaviorFactory factory = BehaviorFactory.eINSTANCE;

        final BehaviorModelRelative behaviorModel = factory.createBehaviorModelRelative(); // to be returned;

        final List<Vertex> vertices = behaviorModel.getVertices();

        Vertex vertex;

        for (final UseCase useCase : useCases) {

            vertex = factory.createVertex();

            vertex.setUseCase(useCase);
            vertices.add(vertex);
        }

        // add a vertex for the final state at last;

        vertex = factory.createVertex();

        vertex.setUseCase(null); // no use case associated with final state;
        vertices.add(vertex);

        return behaviorModel;
    }

    /**
     * Creates a Behavior Model which includes the vertices for set of given use
     * cases.
     * 
     * @param useCases
     *            set of use cases which indicate the vertices to be included to
     *            the Behavior Model; a vertex that represents the final state
     *            will be added explicitly.
     * 
     * @return the newly created Behavior Model.
     */
    protected BehaviorModelAbsolute createBehaviorModelAbsoluteWithoutTransitions(final List<UseCase> useCases) {

        final BehaviorFactory factory = BehaviorFactory.eINSTANCE;

        final BehaviorModelAbsolute behaviorModel = factory.createBehaviorModelAbsolute(); // to be returned;

        final List<Vertex> vertices = behaviorModel.getVertices();

        Vertex vertex;

        for (final UseCase useCase : useCases) {

            vertex = factory.createVertex();

            vertex.setUseCase(useCase);
            vertices.add(vertex);
        }

        // add a vertex for the final state at last;

        vertex = factory.createVertex();

        vertex.setUseCase(null); // no use case associated with final state;
        vertices.add(vertex);

        return behaviorModel;
    }

    /**
     * Creates an empty <code>BehaviorMix</code> instance.
     * 
     * @return the newly created instance without any Behavior Mix entries.
     */
    protected BehaviorMix createBehaviorMix() {

        final BehaviorFactory factory = BehaviorFactory.eINSTANCE;

        return factory.createBehaviorMix();
    }

    /**
     * Creates a Behavior Mix entry for a given Behavior Model; the entry will
     * include the model's (generated) name, a relative frequency and the model
     * itself.
     * 
     * @param behaviorModelName
     *            the (generated) name of the Behavior Model.
     * @param frequency
     *            a relative frequency.
     * @param behaviorModel
     *            the Behavior Model itself.
     * 
     * @return the newly created instance.
     */
    protected BehaviorMixEntry createBehaviorMixEntry(final String behaviorModelName, final double frequency,
            final BehaviorModelRelative behaviorModel) {

        final BehaviorFactory factory = BehaviorFactory.eINSTANCE;

        final BehaviorMixEntry behaviorMixEntry = factory.createBehaviorMixEntry(); // to be returned;

        behaviorMixEntry.setBehaviorModelName(behaviorModelName);
        behaviorMixEntry.setRelativeFrequency(MathUtil.round(frequency));
        behaviorMixEntry.setBehaviorModel(behaviorModel);

        return behaviorMixEntry;
    }

    /**
     * Installs a new transition between two vertices; the (newly created)
     * transition will be added to the outgoing transitions of the source
     * vertex.
     * 
     * @param sourceVertex
     *            source vertex of the transition.
     * @param targetVertex
     *            target vertex of the transition.
     * 
     * @return the installed transition.
     */
    protected Transition installTransition(final Vertex sourceVertex, final Vertex targetVertex) {

        final BehaviorFactory factory = BehaviorFactory.eINSTANCE;
        final Transition transition = factory.createTransition();

        transition.setTargetVertex(targetVertex);
        transition.setSourceVertex(sourceVertex);
        sourceVertex.getOutgoingTransitions().add(transition);

        return transition;
    }

    /**
     * This method creates a new instance set based on the available
     * behaviorModelsAbsolute.
     * 
     * @param behaviorModelsAbsolute
     * @return instance set
     */
    protected Instances getInstances(BehaviorModelAbsolute[] behaviorModelsAbsolute) throws Exception {

        // init the fastVector with attributesNames from the first
        // behaviorModel.
        FastVector fastVector = getFastVector(behaviorModelsAbsolute[0]);

        // create empty instance set with the number of behaviorModelsRelative.
        Instances instances = new Instances("BehaviorModelAbsoluteInstanceSet", fastVector,
                behaviorModelsAbsolute.length);

        // set the last attribute as class index
        instances.setClassIndex(instances.numAttributes() - 1);

        // Each behaviorModelsRelative will be transformed to an instance. To do
        // that, that transition matrix will be
        // transformed in a vector. Set number of attributes of instance: n x (n
        // +1) exit state
        // Matrix.
        for (BehaviorModelAbsolute behaviorModelAbsolute : behaviorModelsAbsolute) {
            // retieve instance from behaviorModelRelative
            Instance instance = getInstance(behaviorModelAbsolute, instances);
            // add instance to instanceset, at the end of the set
            instances.add(instance);
        }

        // save input data as arff file. This arff file can be opened with weka
        // application.
        ArffSaver saver = new ArffSaver();
        saver.setInstances(instances);
        saver.setFile(new File(CommandLineArgumentsHandler.getOutputDirectory() + "/data_clustering.arff"));
        saver.writeBatch();

        // Remove UseLess
        // weka.filters.unsupervised.attribute.RemoveUseless filterUseLess = new
        // weka.filters.unsupervised.attribute.RemoveUseless();
        // filterUseLess.setInputFormat(instances);
        // Instances returnInstances = Filter.useFilter(instances,
        // filterUseLess);

        // filter instances
        weka.filters.unsupervised.attribute.Remove filter = new weka.filters.unsupervised.attribute.Remove();
        filter.setAttributeIndices("" + (instances.classIndex() + 1));
        filter.setInputFormat(instances);
        Instances filteredInstances = Filter.useFilter(instances, filter);

        return filteredInstances;
    }

    /**
     * Returns a instance as vector based on a single BehaviorModelAbsolute.
     * 
     * @param behaviorModelsAbsolute
     * @return a instance, transitions of behaviorModelAbsolute as input vector
     */
    protected Instance getInstance(BehaviorModelAbsolute behaviorModelAbsolute, Instances instances) {

        // create new instance with size n x (n + 1)
        int nrVertices = behaviorModelAbsolute.getVertices().size() - 1;
        Instance instance = new Instance((nrVertices * (nrVertices + 1)) + 1);
        final List<Vertex> vertices = behaviorModelAbsolute.getVertices();
        int indexOfAttribute = 0;
        for (final Vertex srcVertex : vertices) {
            if (srcVertex.getUseCase() != null) { // no final state
                // for each transition set the value of the instance vector
                for (final Vertex dstVertex : vertices) {
                    final UseCase srcUseCase = srcVertex.getUseCase();
                    final UseCase dstUseCase = dstVertex.getUseCase();
                    final String srcUseCaseId = srcUseCase.getId();

                    // if dstUseCase is null, its vertex denotes the final state
                    // (no ID);
                    final String dstUseCaseId = (dstUseCase != null) ? dstUseCase.getId() : null;
                    Transition transition = this.findTransitionByUseCaseIDs(behaviorModelAbsolute, srcUseCaseId,
                            dstUseCaseId);

                    if (transition != null) {
                        instance.setValue(indexOfAttribute, transition.getValue());
                    } else {
                        instance.setValue(indexOfAttribute, 0);
                    }
                    indexOfAttribute++;
                }
            } else {
                continue; // skip final state ("$");
            }
        }

        instance.setDataset(instances);

        // set attribute for classification
        instance.setValue(indexOfAttribute, behaviorModelAbsolute.getTransactionType());
        return instance;
    }

    /**
     * Returns a FactVector which defines the attributes of the Instance set.
     * 
     * @param behaviorModelRelative
     * @return FastVector defines the attributes of the Instance set
     */
    protected FastVector getFastVector(BehaviorModelAbsolute behaviorModelAbsolute) {
        FastVector fastVector = new FastVector();
        final List<Vertex> vertices = behaviorModelAbsolute.getVertices();
        for (final Vertex srcVertex : vertices) {
            if (srcVertex.getUseCase() != null) { // no final state

                for (final Vertex dstVertex : vertices) {
                    final UseCase srcUseCase = srcVertex.getUseCase();
                    final String srcUseCaseName = srcUseCase.getName();
                    final UseCase dstUseCase = dstVertex.getUseCase();
                    final String dstUseCaseName = (dstUseCase != null) ? dstUseCase.getName() : "exit";

                    // set AttributeName
                    fastVector.addElement(new Attribute(srcUseCaseName + "_" + dstUseCaseName, fastVector.size()));
                }
            } else {
                continue; // skip final state ("$");
            }
        }

        // Add dummyClassification. A valid instance set always seams to need a
        // classification attribute. Otherwise the clustering is not working.
        FastVector classification = new FastVector(3);
        classification.addElement("doBrowseVehicles-1");
        classification.addElement("doManageInventory-1");
        classification.addElement("doPurchaseVehicles-1");
        classification.addElement("noSessionType");
        Attribute classAttribute = new Attribute("theClass", classification);
        fastVector.addElement(classAttribute);

        return fastVector;
    }

    /**
     * Create new Transitions with the values calculated by the clustering.
     * 
     * @param behaviorModel
     * @param centroid
     * @param centroidInstances
     * @param centroidIndex
     */
    protected void installTransitions(final BehaviorModelAbsolute[] behaviorModelsAbsolute,
            final BehaviorModelAbsolute behaviorModel, final Instance centroid, final int[] assignments,
            final int centroidIndex) {

        // List<Transition> transitionList = new ArrayList<Transition>();
        // int transitionCountAll = 0;

        final List<Vertex> vertices = behaviorModel.getVertices();
        for (final Vertex srcVertex : vertices) {

            if (srcVertex.getUseCase() != null) { // no final state
                for (final Vertex dstVertex : vertices) {

                    Transition newTransition = installTransition(srcVertex, dstVertex);
                    // transitionList.add(newTransition);

                    // CalculateThinkTimeParams. Think Times must be calculated
                    // based on the original behaviorModelsRelative, as the
                    // clusters are only build based on the transition
                    // probabilities.

                    // first get srcUseCaseId and dstUseCaseID
                    final UseCase srcUseCase = srcVertex.getUseCase();
                    final UseCase dstUseCase = dstVertex.getUseCase();
                    final String srcUseCaseId = srcUseCase.getId();
                    // if dstUseCase is null, its vertex denotes the final state
                    // (no ID);
                    final String dstUseCaseId = (dstUseCase != null) ? dstUseCase.getId() : null;

                    final LinkedList<BigDecimal> timeDiffs = new LinkedList<BigDecimal>();
                    double transitionCount = 0;
                    // iterate assignments
                    for (int i = 0; i < assignments.length; i++) {
                        // if assignment at position i is equal to
                        // centroidIndex, then take the behaviorModelsRelative
                        // at index i
                        if (assignments[i] == centroidIndex) {
                            // get transition of behaviorModel which belongs to
                            // the cluster.
                            Transition transition = this.findTransitionByUseCaseIDs(behaviorModelsAbsolute[i],
                                    srcUseCaseId, dstUseCaseId);

                            if (transition != null) {
                                // store tinkTimeDiffs
                                timeDiffs.addAll(transition.getTimeDiffs());
                                transitionCount += transition.getValue();
                                // transitionCountAll += transition.getValue();
                            }
                        }
                    }

                    if (timeDiffs.size() > 0) {
                        newTransition.getTimeDiffs().addAll(timeDiffs);
                    }

                    if (transitionCount > 0) {
                        newTransition.setValue(transitionCount);
                    } else {
                        newTransition.setValue(0);
                    }
                }
            } else {
                continue; // skip final state ("$");
            }
        }

    }

}