Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Commit

Permalink
Merge branch 'master' into 1162-copyright-doc
Browse files Browse the repository at this point in the history
  • Loading branch information
pdaulbyscottlogic authored Jul 29, 2019
2 parents 674cce1 + 73cfa29 commit 225c0bd
Show file tree
Hide file tree
Showing 21 changed files with 455 additions and 96 deletions.
9 changes: 9 additions & 0 deletions docs/developer/decisionTreeWalkers/DecisionBasedWalker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The Decision Based Tree solver generates row specs by:
1. choosing and removing a decision from the tree
2. selecting an option from that decision
3. adding the constraints from the chosen option to the root of the tree
- adding the sub decisions from the chosen option to the root of the tree
4. "pruning" the tree by removing any options from the tree that contradict with the new root node
- any decisions that only have 1 remaining option will have that option also moved up the tree, and pruned again.
5. restarting from 1, until there are no decision left
6. creating a rowspec from the constraints in the remaining root node.
13 changes: 10 additions & 3 deletions docs/developer/decisionTreeWalkers/TreeWalkerTypes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@ The generator transforms each profile into one or more [decision trees](../decis

The following walker strategies exist:

* Cartesian product (default)
* Decision based (default)
* Cartesian product
* Reductive

## Decision Based
This is a recursive algorithm that selects an option from the decision tree, then reduces the tree for the constraints in that option.

See [Decision Based Walker](DecisionBasedWalker.md) for more details.

## Cartesian product
This strategy is the a recursive algorithm which will 'multiply' each leaf node of the tree against every other leaf node of the decision tree in order to generate data. As such it can create vast numbers of permutations. This strategy makes no attempt to overcome the chance of a combinatorial explosion which can occur with relatively few rules and constraints.

This strategy uses certain methods of the Java Streams API which are known to block rather than be lazy (`flatMap`) which means the data may be prevented from being emitted until all permutations have been calculated.

This strategy is also known to have limited-to-no intelligence when it comes to [contradictions](../../user/Contradictions.md). The strategy will back track when they are found, but makes no attempt to preemptively check for them or prevent the walker from entering a contradictory path of the tree.

## Reductive (default)
This strategy takes a different approach to the others above and follows the following process. The strategy focuses on reducing the size of the problem (the tree) progressively until it cannot be any further (then back-tracking occurs) or sufficient information is known (then row/s can be emitted.)
## Reductive
The strategy selects a value for a field, then reduces the size of the problem (the tree) progressively until it cannot be any further (then back-tracking occurs) or sufficient information is known (then row/s can be emitted.)

See [Reductive tree walker](ReductiveTreeWalker.md) for more details.

Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@

public enum TreeWalkerType {
CARTESIAN_PRODUCT,
REDUCTIVE
REDUCTIVE,
DECISION_BASED
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,41 @@

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.scottlogic.deg.common.ValidationException;
import com.scottlogic.deg.generator.config.detail.DataGenerationType;
import com.scottlogic.deg.generator.generation.GenerationConfigSource;
import com.scottlogic.deg.generator.walker.*;
import com.scottlogic.deg.generator.walker.rowspec.RandomRowSpecDecisionTreeWalker;
import com.scottlogic.deg.generator.walker.rowspec.RowSpecDecisionTreeWalker;

public class DecisionTreeWalkerProvider implements Provider<DecisionTreeWalker> {
private final ReductiveDecisionTreeWalker reductiveDecisionTreeWalker;
private final RowSpecDecisionTreeWalker rowSpecDecisionTreeWalker;
private final RandomReductiveDecisionTreeWalker randomReductiveDecisionTreeWalker;
private final RowSpecDecisionTreeWalker rowSpecDecisionTreeWalker;
private final RandomRowSpecDecisionTreeWalker randomRowSpecDecisionTreeWalker;
private final GenerationConfigSource configSource;

@Inject
public DecisionTreeWalkerProvider(
ReductiveDecisionTreeWalker reductiveDecisionTreeWalker,
RowSpecDecisionTreeWalker rowSpecDecisionTreeWalker,
RandomReductiveDecisionTreeWalker randomReductiveDecisionTreeWalker,
RandomRowSpecDecisionTreeWalker randomRowSpecDecisionTreeWalker,
GenerationConfigSource configSource) {
this.reductiveDecisionTreeWalker = reductiveDecisionTreeWalker;
this.rowSpecDecisionTreeWalker = rowSpecDecisionTreeWalker;
this.randomReductiveDecisionTreeWalker = randomReductiveDecisionTreeWalker;
this.randomRowSpecDecisionTreeWalker = randomRowSpecDecisionTreeWalker;
this.configSource = configSource;
}

@Override
public DecisionTreeWalker get() {
switch(configSource.getWalkerType()) {
case CARTESIAN_PRODUCT:
case DECISION_BASED:
if (configSource.getGenerationType() == DataGenerationType.RANDOM)
return randomRowSpecDecisionTreeWalker;
return rowSpecDecisionTreeWalker;

case REDUCTIVE:
Expand All @@ -54,7 +62,7 @@ public DecisionTreeWalker get() {
return reductiveDecisionTreeWalker;

default:
return reductiveDecisionTreeWalker;
throw new ValidationException("no WalkerType selected");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.scottlogic.deg.generator.generation.combinationstrategies.CombinationStrategy;
import com.scottlogic.deg.generator.inputs.validation.ProfileValidator;
import com.scottlogic.deg.generator.utils.JavaUtilRandomNumberGenerator;
import com.scottlogic.deg.generator.walker.decisionbased.OptionPicker;
import com.scottlogic.deg.generator.walker.rowspec.CartesianProductRowSpecTreeSolver;
import com.scottlogic.deg.generator.walker.DecisionTreeWalker;
import com.scottlogic.deg.generator.walker.ReductiveWalkerRetryChecker;
Expand Down Expand Up @@ -60,6 +61,8 @@ protected void configure() {
bind(ReductiveDataGeneratorMonitor.class).toProvider(MonitorProvider.class).in(Singleton.class);
bind(IterationVisualiser.class).toProvider(IterationVisualiserProvider.class);
bind(CombinationStrategy.class).toProvider(CombinationStrategyProvider.class);
bind(OptionPicker.class).toProvider(OptionPickerProvider.class);
bind(RowSpecTreeSolver.class).toProvider(RowSpecTreeSolverProvider.class);

// bind config directly
bind(DataGenerationType.class).toInstance(generationConfigSource.getGenerationType());
Expand All @@ -73,7 +76,6 @@ protected void configure() {
bind(DataGenerator.class).to(DecisionTreeDataGenerator.class);
bind(DecisionTreeFactory.class).to(MaxStringLengthInjectingDecisionTreeFactory.class);
bind(FieldValueSourceEvaluator.class).to(StandardFieldValueSourceEvaluator.class);
bind(RowSpecTreeSolver.class).to(CartesianProductRowSpecTreeSolver.class);
bind(ReductiveWalkerRetryChecker.class).toInstance(new ReductiveWalkerRetryChecker(10000));

bind(JavaUtilRandomNumberGenerator.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.scottlogic.deg.generator.guice;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.scottlogic.deg.generator.config.detail.DataGenerationType;
import com.scottlogic.deg.generator.generation.GenerationConfigSource;
import com.scottlogic.deg.generator.walker.decisionbased.OptionPicker;
import com.scottlogic.deg.generator.walker.decisionbased.RandomOptionPicker;
import com.scottlogic.deg.generator.walker.decisionbased.SequentialOptionPicker;

public class OptionPickerProvider implements Provider<OptionPicker> {

private final GenerationConfigSource config;
private final RandomOptionPicker randomOptionPicker;
private final SequentialOptionPicker sequentialOptionPicker;

@Inject
public OptionPickerProvider(GenerationConfigSource config, RandomOptionPicker randomOptionPicker, SequentialOptionPicker sequentialOptionPicker){
this.config = config;
this.randomOptionPicker = randomOptionPicker;
this.sequentialOptionPicker = sequentialOptionPicker;
}

@Override
public OptionPicker get() {
if (config.getGenerationType() == DataGenerationType.RANDOM){
return randomOptionPicker;
}

return sequentialOptionPicker;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.scottlogic.deg.generator.guice;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.scottlogic.deg.common.ValidationException;
import com.scottlogic.deg.generator.generation.GenerationConfigSource;
import com.scottlogic.deg.generator.walker.decisionbased.DecisionBasedSolver;
import com.scottlogic.deg.generator.walker.rowspec.CartesianProductRowSpecTreeSolver;
import com.scottlogic.deg.generator.walker.rowspec.RowSpecTreeSolver;

public class RowSpecTreeSolverProvider implements Provider<RowSpecTreeSolver> {

private final GenerationConfigSource config;
private final CartesianProductRowSpecTreeSolver cartesianProductRowSpecTreeSolver;
private final DecisionBasedSolver decisionBasedSolver;

@Inject
public RowSpecTreeSolverProvider(GenerationConfigSource config, CartesianProductRowSpecTreeSolver cartesianProductRowSpecTreeSolver, DecisionBasedSolver decisionBasedSolver) {
this.config = config;
this.cartesianProductRowSpecTreeSolver = cartesianProductRowSpecTreeSolver;
this.decisionBasedSolver = decisionBasedSolver;
}

@Override
public RowSpecTreeSolver get() {
switch (config.getWalkerType()) {
case REDUCTIVE:
case DECISION_BASED:
return decisionBasedSolver;
case CARTESIAN_PRODUCT:
return cartesianProductRowSpecTreeSolver;
default:
throw new ValidationException("no WalkerType selected");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.scottlogic.deg.generator.walker.decisionbased;

import com.google.inject.Inject;
import com.scottlogic.deg.common.profile.Field;
import com.scottlogic.deg.common.profile.ProfileFields;
import com.scottlogic.deg.common.profile.constraints.atomic.AtomicConstraint;
import com.scottlogic.deg.common.util.FlatMappingSpliterator;
import com.scottlogic.deg.generator.decisiontree.ConstraintNode;
import com.scottlogic.deg.generator.decisiontree.DecisionNode;
import com.scottlogic.deg.generator.decisiontree.DecisionTree;
import com.scottlogic.deg.generator.fieldspecs.FieldSpec;
import com.scottlogic.deg.generator.fieldspecs.RowSpec;
import com.scottlogic.deg.generator.reducer.ConstraintReducer;
import com.scottlogic.deg.generator.walker.reductive.Merged;
import com.scottlogic.deg.generator.walker.reductive.ReductiveTreePruner;
import com.scottlogic.deg.generator.walker.rowspec.RowSpecTreeSolver;

import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DecisionBasedSolver implements RowSpecTreeSolver {

private final ConstraintReducer constraintReducer;
private final ReductiveTreePruner reductiveTreePruner;
private final OptionPicker optionPicker;

@Inject
public DecisionBasedSolver(ConstraintReducer constraintReducer, ReductiveTreePruner reductiveTreePruner, OptionPicker optionPicker) {
this.constraintReducer = constraintReducer;
this.reductiveTreePruner = reductiveTreePruner;
this.optionPicker = optionPicker;
}

@Override
public Stream<RowSpec> createRowSpecs(DecisionTree tree) {
return reduceToRowNodes(tree.rootNode)
.map(rootNode -> toRowspec(tree.fields, rootNode));
}

private RowSpec toRowspec(ProfileFields fields, ConstraintNode rootNode){
return constraintReducer
.reduceConstraintsToRowSpec(fields, rootNode.getAtomicConstraints()).get();
}

private Stream<ConstraintNode> reduceToRowNodes(ConstraintNode rootNode){
if (rootNode.getDecisions().isEmpty()){
return Stream.of(rootNode);
}

DecisionNode decisionNode = optionPicker.pickDecision(rootNode);
ConstraintNode rootWithoutDecision = rootNode.removeDecisions(Collections.singleton(decisionNode));

Stream<ConstraintNode> rootOnlyConstraintNodes = optionPicker.streamOptions(decisionNode)
.map(option -> combineWithRootNode(rootWithoutDecision, option))
.filter(newNode -> !newNode.isContradictory())
.map(Merged::get);

return FlatMappingSpliterator.flatMap(
rootOnlyConstraintNodes,
this::reduceToRowNodes);
}

private Merged<ConstraintNode> combineWithRootNode(ConstraintNode rootNode, ConstraintNode option) {
ConstraintNode constraintNode = rootNode
.addDecisions(option.getDecisions())
.addAtomicConstraints(option.getAtomicConstraints());

return reductiveTreePruner.pruneConstraintNode(constraintNode, getFields(option));
}

private Map<Field, FieldSpec> getFields(ConstraintNode option) {
return option.getAtomicConstraints().stream()
.map(AtomicConstraint::getField)
.distinct()
.collect(Collectors.toMap(
Function.identity(),
field-> FieldSpec.Empty));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.scottlogic.deg.generator.walker.decisionbased;

import com.scottlogic.deg.generator.decisiontree.ConstraintNode;
import com.scottlogic.deg.generator.decisiontree.DecisionNode;

import java.util.stream.Stream;

public interface OptionPicker {
DecisionNode pickDecision (ConstraintNode constraintNode);
Stream<ConstraintNode> streamOptions(DecisionNode decisionNode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.scottlogic.deg.generator.walker.decisionbased;

import com.google.inject.Inject;
import com.scottlogic.deg.generator.decisiontree.ConstraintNode;
import com.scottlogic.deg.generator.decisiontree.DecisionNode;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;

public class RandomOptionPicker implements OptionPicker {
private final Random random;

public RandomOptionPicker() {
this.random = new Random();
}

@Override
public DecisionNode pickDecision(ConstraintNode constraintNode) {
return constraintNode.getDecisions().stream()
.skip(random.nextInt(constraintNode.getDecisions().size()))
.findFirst().get();
}

@Override
public Stream<ConstraintNode> streamOptions(DecisionNode decisionNode) {
List<ConstraintNode> options = new ArrayList<>(decisionNode.getOptions());
Collections.shuffle(options);
return options.stream();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.scottlogic.deg.generator.walker.decisionbased;

import com.scottlogic.deg.generator.decisiontree.ConstraintNode;
import com.scottlogic.deg.generator.decisiontree.DecisionNode;

import java.util.stream.Stream;

public class SequentialOptionPicker implements OptionPicker {
@Override
public DecisionNode pickDecision(ConstraintNode constraintNode) {
return constraintNode.getDecisions().iterator().next();
}

@Override
public Stream<ConstraintNode> streamOptions(DecisionNode decisionNode) {
return decisionNode.getOptions().stream();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.scottlogic.deg.generator.walker.reductive;

import com.scottlogic.deg.generator.decisiontree.ConstraintNode;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
Expand Down
Loading

0 comments on commit 225c0bd

Please sign in to comment.