/**
* Copyright (c) 2010, 2013 Darmstadt University of Technology.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marcel Bruch - initial API and implementation.
*/
package org.eclipse.recommenders.overrides;
import static org.eclipse.recommenders.utils.Checks.ensureIsGreaterOrEqualTo;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.math.stat.StatUtils;
import org.eclipse.recommenders.jayes.BayesNet;
import org.eclipse.recommenders.jayes.BayesNode;
import org.eclipse.recommenders.utils.names.IMethodName;
import org.eclipse.recommenders.utils.names.ITypeName;
import com.google.common.collect.Lists;
public class JayesOverrideModelBuilder {
private static final double MIN = 0.0001;
private static final double MAX = 1.0 - MIN;
private final ITypeName typeName;
private final BayesNet network;
private final Collection<OverrideObservation> overriddenMethods;
private int totalNumberOfSubtypesFound;
private BayesNode patternNode;
private List<BayesNode> methodNodes;
public JayesOverrideModelBuilder(final ITypeName typeName,
final Collection<OverrideObservation> overriddenMethods) {
this.typeName = typeName;
this.overriddenMethods = overriddenMethods;
// filterInfrequentOverridingPatterns();
ensureIsGreaterOrEqualTo(overriddenMethods.size(), 1, "at least one observation is required");
computeTotalNumberOfSubtypes();
network = new BayesNet();
}
private void computeTotalNumberOfSubtypes() {
for (final OverrideObservation usage : overriddenMethods) {
totalNumberOfSubtypesFound += usage.frequency;
}
}
public IOverrideModel build() {
createPatternNodeInNetwork();
createMethodNodes();
return new JayesOverrideModel(typeName, network, patternNode, methodNodes);
}
private void createPatternNodeInNetwork() {
patternNode = network.createNode("patternNode");
patternNode.addOutcome("none");
final double[] def = new double[1 + overriddenMethods.size()];
def[0] = MIN;
int i = 0;
for (final OverrideObservation obs : overriddenMethods) {
i++;
final String name = "observation_" + i;
patternNode.addOutcome(name);
final double priorPatternProbability = obs.frequency / (double) totalNumberOfSubtypesFound;
def[i] = priorPatternProbability;
}
scaleMaximalValue(def);
patternNode.setProbabilities(def);
}
private void scaleMaximalValue(final double[] subDefinition) {
final double diff = StatUtils.sum(subDefinition) - 1.0;
final double max = StatUtils.max(subDefinition);
final int indexOf = ArrayUtils.indexOf(subDefinition, max);
subDefinition[indexOf] = subDefinition[indexOf] - diff;
}
private void createMethodNodes() {
final Set<IMethodName> methods = collectInvokedMethodsFromPatterns();
methodNodes = Lists.newLinkedList();
for (final IMethodName ref : methods) {
final BayesNode methodNode = network.createNode(ref.getIdentifier());
methodNode.setParents(Lists.newArrayList(patternNode));
methodNode.addOutcome("true");
methodNode.addOutcome("false");
methodNode.setProbabilities(createMethodNodeDefinition(ref));
methodNodes.add(methodNode);
}
}
private double[] createMethodNodeDefinition(final IMethodName ref) {
final double[] definition = new double[2 + 2 * overriddenMethods.size()];
definition[0] = 0.0;
definition[1] = 1.0;
int i = 2;
for (final OverrideObservation pattern : overriddenMethods) {
final boolean overridesMethod = pattern.overriddenMethods.contains(ref);
if (overridesMethod) {
definition[i++] = MAX;
definition[i++] = MIN;
} else {
// just flip
definition[i++] = MIN;
definition[i++] = MAX;
}
}
return definition;
}
private TreeSet<IMethodName> collectInvokedMethodsFromPatterns() {
final TreeSet<IMethodName> methods = new TreeSet<IMethodName>();
for (final OverrideObservation observation : overriddenMethods) {
methods.addAll(observation.overriddenMethods);
}
return methods;
}
}