source: trunk/autoquest-core-testgeneration/src/main/java/de/ugoe/cs/autoquest/testgeneration/HybridGenerator.java @ 922

Last change on this file since 922 was 922, checked in by sherbold, 12 years ago
  • renaming of packages from de.ugoe.cs.quest to de.ugoe.cs.autoquest
  • Property svn:mime-type set to text/plain
File size: 7.0 KB
Line 
1package de.ugoe.cs.autoquest.testgeneration;
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.LinkedHashSet;
6import java.util.LinkedList;
7import java.util.List;
8import java.util.Random;
9import java.util.logging.Level;
10
11import de.ugoe.cs.autoquest.eventcore.Event;
12import de.ugoe.cs.autoquest.usageprofiles.IStochasticProcess;
13import de.ugoe.cs.util.console.Console;
14
15/**
16 * <p>
17 * Generates a test suite with a hybrid approach that is a mixture of random walks and drawing from
18 * all possible sequences.
19 * </p>
20 *
21 * @author Steffen Herbold
22 * @version 1.0
23 */
24public class HybridGenerator {
25
26    /**
27     * <p>
28     * Number of sequences in the test suite.
29     * </p>
30     */
31    private final int numSequences;
32
33    /**
34     * <p>
35     * Length of a test sequence.
36     * </p>
37     */
38    private final int length;
39
40    /**
41     * <p>
42     * Maximal length where it is possible to generate all sequences and draw from them.
43     * </p>
44     */
45    private final int maxLengthAll;
46
47    /**
48     * <p>
49     * In case this member is true, only test cases that end in the global end event
50     * {@link Event#ENDEVENT} are generated. If it is false, the end event can be any event.
51     * </p>
52     */
53    private final boolean validEnd;
54
55    /**
56     * <p>
57     * Constructor. Creates a new HybridGenerator and ensures the validity of the parameters:
58     * <ul>
59     * <li>numSequences must at least be 1
60     * <li>length must be at least 1
61     * </ul>
62     * If one of these conditions is violated an {@link IllegalArgumentException} is thrown.
63     * </p>
64     *
65     * @param numSequences
66     *            number of sequences desired for the test suite
67     * @param length
68     *            length of a test sequence
69     * @param maxLengthAll
70     *            maximal length where it is possible to generate all sequences and draw from them
71     * @param validEnd
72     *            defines if test cases have to end with the global end event {@link Event#ENDEVENT}
73     *            (see {@link #validEnd})
74     */
75    public HybridGenerator(int numSequences, int length, int maxLengthAll, boolean validEnd) {
76        // check validity of the parameters
77        if (numSequences < 1) {
78            throw new IllegalArgumentException("number of sequences must be at least 1 but is " +
79                numSequences);
80        }
81        if (length < 1) {
82            throw new IllegalArgumentException("length of test cases must be at least 1 but is " +
83                length);
84        }
85        this.numSequences = numSequences;
86        this.length = length;
87        this.maxLengthAll = maxLengthAll;
88        this.validEnd = validEnd;
89    }
90
91    /**
92     * <p>
93     * Generates a test suite with a hybrid approach that is a mixture of random walks and drawing
94     * from all possible sequences
95     * </p>
96     *
97     * @param model
98     *            model used to determine the probability of each possible sequence
99     * @return the test suite
100     */
101    public Collection<List<Event>> generateTestSuite(IStochasticProcess model) {
102        if (model == null) {
103            throw new IllegalArgumentException("model must not be null!");
104        }
105
106        Collection<List<Event>> sequences = new LinkedHashSet<List<Event>>();
107
108        List<List<Event>> seqsTmp =
109            new ArrayList<List<Event>>(model.generateSequences(maxLengthAll + 1, true));
110
111        Console.traceln(Level.INFO, "" + seqsTmp.size() + " of length " + maxLengthAll + " possible");
112        List<Double> probabilities = new ArrayList<Double>(seqsTmp.size());
113        double probSum = 0.0;
114        for (List<Event> sequence : seqsTmp) {
115            double prob = model.getProbability(sequence);
116            probabilities.add(prob);
117            probSum += prob;
118        }
119
120        Random r = new Random();
121        int j = 0;
122        while (sequences.size() < numSequences && j <= numSequences * 100) {
123            j++;
124            double randVal = r.nextDouble() * probSum;
125            double sum = 0.0d;
126            int index = -1;
127            while (sum < randVal) {
128                index++;
129                double currentProb = probabilities.get(index);
130                sum += currentProb;
131            }
132            List<Event> seqTmp = seqsTmp.get(index);
133            if (!Event.ENDEVENT.equals(seqTmp.get(seqTmp.size() - 1))) {
134                List<Event> sequence;
135                if (validEnd) {
136                    sequence = finishSequence(seqTmp, model, length + 2, validEnd);
137                    if (sequence != null && sequence.size() != length + 2) {
138                        sequence = null;
139                    }
140                }
141                else {
142                    sequence = finishSequence(seqTmp, model, length + 1, validEnd);
143                    if (sequence != null && sequence.size() != length + 1) {
144                        sequence = null;
145                    }
146                }
147                if (sequence != null) {
148                    sequences.add(sequence);
149                }
150            }
151        }
152
153        return sequences;
154    }
155
156    /**
157     * <p>
158     * Finishes a sequence with a random walk.
159     * </p>
160     *
161     * @param sequence
162     *            sequence to be finished
163     * @param model
164     *            model used for the random walk
165     * @param length
166     *            desired length of the sequence
167     * @param validEnd
168     *            if the sequence should end in {@link Event#ENDEVENT}.
169     * @return finished sequence of the desired length
170     */
171    private List<Event> finishSequence(List<Event> sequence,
172                                       IStochasticProcess model,
173                                       int length,
174                                       boolean validEnd)
175    {
176        Random r = new Random();
177        boolean endFound = false;
178        List<Event> sequenceCopy = new LinkedList<Event>(sequence);
179        final int maxIter = 30000;
180        int iter = 0;
181        while (!endFound && iter < maxIter) {
182            iter++;
183            sequenceCopy = new LinkedList<Event>(sequence);
184            while (!endFound && sequenceCopy.size() <= length) {
185                double randVal = r.nextDouble();
186                double probSum = 0.0;
187                for (Event symbol : model.getEvents()) {
188                    probSum += model.getProbability(sequenceCopy, symbol);
189                    if (probSum >= randVal) {
190                        if (!(Event.STARTEVENT.equals(symbol) || (!validEnd && Event.ENDEVENT
191                            .equals(symbol))))
192                        {
193                            // only add the symbol the sequence if it is not
194                            // START
195                            // or END
196                            sequenceCopy.add(symbol);
197                        }
198                        endFound =
199                            Event.ENDEVENT.equals(symbol) ||
200                                (!validEnd && sequenceCopy.size() == length);
201                        break;
202                    }
203                }
204            }
205        }
206        if (iter == maxIter) {
207            return null;
208        }
209        return sequenceCopy;
210    }
211
212}
Note: See TracBrowser for help on using the repository browser.