From 94e9eaaeb00adcc6ced213c1f05f951e0f16734e Mon Sep 17 00:00:00 2001
From: Johannes Mey <johannes.mey@tu-dresden.de>
Date: Tue, 17 Jul 2018 14:28:12 +0200
Subject: [PATCH] initial commit of genetic solver (not working yet)

---
 .../src/main/jastadd/solvers/Helpers.jadd     | 151 +++++++++++++++++-
 .../jastadd/solvers/genetic/Checking.jrag     | 150 +++++++++++++++++
 jastadd-mquat-benchmark/build.gradle          |   1 +
 .../inf/st/mquat/benchmark/SolverFactory.java |   4 +-
 jastadd-mquat-solver-genetic/.gitignore       |   2 +
 jastadd-mquat-solver-genetic/build.gradle     |  24 +++
 .../mquat/solving/genetic/GeneticSolver.java  | 100 ++++++++++++
 .../solving/genetic/opt4j/Opt4jCreator.java   |  43 +++++
 .../solving/genetic/opt4j/Opt4jDecoder.java   |  74 +++++++++
 .../solving/genetic/opt4j/Opt4jEvaluator.java |  34 ++++
 .../solving/genetic/opt4j/Opt4jModule.java    |  75 +++++++++
 .../opt4j/custom/genotypes/TreeGenotype.java  |  59 +++++++
 .../custom/genotypes/TreeGenotypeNode.java    |  75 +++++++++
 .../operator/copy/TreeCopyOperator.java       |  16 ++
 .../operator/copy/TreeCopyOperatorModule.java |  12 ++
 .../crossover/TreeCrossoverOperator.java      |  94 +++++++++++
 .../TreeCrossoverOperatorModule.java          |  21 +++
 .../operator/mutate/TreeMutateOperator.java   |  83 ++++++++++
 .../mutate/TreeMutateOperatorModule.java      |  12 ++
 .../mquat/solving/SimpleHandwrittenTest.java  |  11 ++
 .../st/mquat/solving/SimpleSolverTest.java    |  54 +++++++
 settings.gradle                               |   1 +
 22 files changed, 1094 insertions(+), 2 deletions(-)
 create mode 100644 jastadd-mquat-base/src/main/jastadd/solvers/genetic/Checking.jrag
 create mode 100644 jastadd-mquat-solver-genetic/.gitignore
 create mode 100644 jastadd-mquat-solver-genetic/build.gradle
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/GeneticSolver.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jCreator.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jDecoder.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jEvaluator.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jModule.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/genotypes/TreeGenotype.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/genotypes/TreeGenotypeNode.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/copy/TreeCopyOperator.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/copy/TreeCopyOperatorModule.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/crossover/TreeCrossoverOperator.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/crossover/TreeCrossoverOperatorModule.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/mutate/TreeMutateOperator.java
 create mode 100644 jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/mutate/TreeMutateOperatorModule.java
 create mode 100644 jastadd-mquat-solver-genetic/src/test/java/de/tudresden/inf/st/mquat/solving/SimpleHandwrittenTest.java
 create mode 100644 jastadd-mquat-solver-genetic/src/test/java/de/tudresden/inf/st/mquat/solving/SimpleSolverTest.java

diff --git a/jastadd-mquat-base/src/main/jastadd/solvers/Helpers.jadd b/jastadd-mquat-base/src/main/jastadd/solvers/Helpers.jadd
index b90d02d..8ca652d 100644
--- a/jastadd-mquat-base/src/main/jastadd/solvers/Helpers.jadd
+++ b/jastadd-mquat-base/src/main/jastadd/solvers/Helpers.jadd
@@ -37,4 +37,153 @@ aspect Helpers {
     return assignment;
   }
 
-}
\ No newline at end of file
+
+
+  public Solution Root.createRandomSolution(java.util.Random random) {
+    Solution solution = new Solution();
+
+    // create a resource bucket
+    java.util.List<Resource> resourceList = populateResourceList(this.getHardwareModel().getResourceList(), new ArrayList<>());
+
+    for (Request request : getRequestList()) {
+      solution.addAssignment(createRandomAssignment(true, request, request.getTarget().getRef(),resourceList,  random));
+    }
+
+    return solution;
+  }
+
+
+  private java.util.List<Resource> Root.populateResourceList(List<Resource> resourceList, java.util.List<Resource> list) {
+    for (Resource resource : resourceList) {
+      if (resource.getType().getRef().getContainer()) {
+        list.add(resource);
+      }
+      populateResourceList(resource.getSubResourceList(), list);
+    }
+    return list;
+  }
+
+  public Assignment Root.createRandomAssignment(boolean isTopLevel, Request request, Component component, java.util.List<Resource> resourceList,  java.util.Random random) {
+
+    Implementation implementation = component.getImplementation(random.nextInt(component.getNumImplementation()));
+
+    Assignment assignment = new Assignment();
+    assignment.setImplementation(implementation);
+    assignment.setRequest(request);
+    assignment.setTopLevel(isTopLevel);
+
+    // assign dependent software components
+    for (ComponentRequirement componentRequirement : implementation.getComponentRequirementList()) {
+      Component requiredComponent = componentRequirement.getComponentRef().getRef();
+      for (Instance componentRequirementInstance : componentRequirement.getInstanceList()) {
+        ComponentMapping mapping = new ComponentMapping();
+        mapping.setInstance(componentRequirementInstance);
+        mapping.setAssignment(createRandomAssignment(false, request, requiredComponent, resourceList, random));
+        assignment.addComponentMapping(mapping);
+      }
+    }
+
+    // assign resources
+    ResourceRequirement resourceRequirement = implementation.getResourceRequirement();
+    ResourceType requiredResourceType = resourceRequirement.getResourceTypeRef().getRef();
+    for (Instance instance : resourceRequirement.getInstanceList()) {
+      ResourceMapping mapping = new ResourceMapping();
+      mapping.setInstance(instance);
+      Resource chosenResource;
+      int resourceNumber;
+      do {
+        resourceNumber = random.nextInt(resourceList.size());
+        chosenResource = resourceList.get(resourceNumber);
+      } while (chosenResource.getType().getRef() != requiredResourceType);
+      mapping.setResource(resourceList.remove(resourceNumber));
+
+      assignment.setResourceMapping(mapping);
+    }
+
+
+    return assignment;
+  }
+
+  public java.util.List<Resource> Root.unassignedResources(Solution solution) {
+    java.util.List<Resource> assignedResources = solution.assignedResources();
+    java.util.List<Resource> allResources = getHardwareModel().allContainers();
+
+    allResources.removeAll(assignedResources);
+
+    return allResources;
+  }
+
+  public java.util.List<Resource> HardwareModel.allContainers() {
+    java.util.List<Resource> allContainers = new java.util.ArrayList<>();
+
+    for (Resource resource : getResourceList()) {
+      allContainers.addAll(resource.allContainers());
+    }
+
+    return allContainers;
+  }
+
+  public java.util.List<Resource> Resource.allContainers() {
+
+    java.util.List<Resource> allContainers = null;
+
+    for (Resource subResource : getSubResourceList()) {
+      java.util.List<Resource> subResourceContainers = subResource.allContainers();
+      if (!subResourceContainers.isEmpty()) {
+        if (allContainers == null) {
+          allContainers = new ArrayList<>(subResourceContainers);
+        } else {
+          allContainers.addAll(subResourceContainers);
+        }
+      }
+    }
+
+    if (this.getType().getRef().getContainer()) {
+      if (allContainers == null || allContainers.isEmpty()) {
+        return java.util.Collections.singletonList(this);
+      } else {
+        allContainers.add(this);
+      }
+    }
+
+    return allContainers == null ? java.util.Collections.emptyList() : allContainers;
+  }
+
+  public java.util.List<Resource> Assignment.assignedResources() {
+    java.util.List<Resource> assignedResources = new java.util.ArrayList<>();
+
+    if (this.getResource() != null) {
+      assignedResources.add(this.getResource());
+    }
+
+    for (ComponentMapping mapping : this.getComponentMappingList()) {
+      assignedResources.addAll(mapping.getAssignment().assignedResources());
+    }
+
+    return assignedResources;
+  }
+
+  public java.util.List<Resource> Solution.assignedResources() {
+
+    java.util.List<Resource> assignedResources = new java.util.ArrayList<>();
+
+    for (Assignment assignment : this.getAssignmentList()) {
+      assignedResources.addAll(assignment.assignedResources());
+    }
+
+    return assignedResources;
+  }
+
+
+    inh Solution Assignment.containingSolution();
+  eq Solution.getAssignment().containingSolution() = this;
+
+  public boolean List.contains(ASTNode node) {
+    for (Object o : astChildren()) {
+      if (node == o) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/jastadd-mquat-base/src/main/jastadd/solvers/genetic/Checking.jrag b/jastadd-mquat-base/src/main/jastadd/solvers/genetic/Checking.jrag
new file mode 100644
index 0000000..8ab4e34
--- /dev/null
+++ b/jastadd-mquat-base/src/main/jastadd/solvers/genetic/Checking.jrag
@@ -0,0 +1,150 @@
+aspect Checking {
+
+  syn int Solution.evaluateValidity() {
+
+    int faults = evaluateSoftwareCompletenessa();
+
+    Set<Request> requestSet = new HashSet<>();
+    Set<Resource> resourceSet = new HashSet<>();
+
+    // check assignments
+    Iterator<Assignment> assignmentIterator = this.assignmentIterator();
+    while (assignmentIterator.hasNext()) {
+      Assignment assignment = assignmentIterator.next();
+      faults += assignment.evaluateValidity();
+
+
+      if (assignment.getImplementation().containingComponent() ==
+          assignment.getRequest().getTarget().getRef()) {
+        requestSet.add(assignment.getRequest());
+      }
+      if (resourceSet.contains(assignment.getResource())) {
+        // double usage of resources
+        faults++;
+      }
+      resourceSet.add(assignment.getResource());
+
+    }
+
+    return faults;
+  }
+
+  syn int Assignment.evaluateValidity() {
+
+    int faults = 0;
+
+    if (getResource() == null) {
+      faults++;
+    }
+    for (Clause clause : getImplementation().requirementClauses()) {
+      if (!clause.checkUsing(this)) {
+        faults++;
+      }
+    }
+    // if this is a "top-level" assignment, check the properties from the request
+    if (this.getRequest().getTarget().getRef().equals(getImplementation().containingComponent())) {
+      for (Clause clause : getRequest().getConstraintList()) {
+        if (clause.isRequiringClause() && !clause.checkUsing(this)) {
+          faults++;
+        }
+      }
+    }
+    return faults;
+  }
+
+  syn int Solution.evaluateSoftwareValidity() {
+
+    int faults = evaluateSoftwareCompletenessa();
+
+    // check assignments
+    Iterator<Assignment> assignmentIterator = this.assignmentIterator();
+    while (assignmentIterator.hasNext()) {
+      faults += assignmentIterator.next().evaluateSoftwareValidity();
+    }
+
+    return faults;
+  }
+
+  syn int Assignment.evaluateSoftwareValidity() {
+
+    int faults = 0;
+
+    for (Clause clause : getImplementation().requirementClauses()) {
+      if (clause.getDesignator().isSoftwareDesignator()) {
+        if (!clause.checkUsing(this)) {
+          faults++;
+        }
+      }
+
+    }
+
+    // if this is a "top-level" assignment, check the properties from the request
+    if (this.getRequest().getTarget().getRef().equals(getImplementation().containingComponent())) {
+      for (Clause clause : getRequest().getConstraintList()) {
+        if (clause.isRequiringClause() && clause.getDesignator().isSoftwareDesignator()) {
+          if (!clause.checkUsing(this)) {
+            faults++;
+          }
+        }
+      }
+    }
+
+    return faults;
+  }
+
+  syn int Solution.evaluateSoftwareCompletenessa() {
+
+    int faults = 0;
+
+    java.util.List<Component> targets = new java.util.ArrayList<>();
+    for (Request request : this.getModel().getRequestList()) {
+      targets.add(request.getTarget().getRef());
+    }
+
+    for (Assignment assignment : getAssignmentList()) {
+      faults += assignment.evaluateSoftwareCompletenessa();
+      targets.remove(assignment.getImplementation().containingComponent());
+    }
+
+    for (Component target : targets) {
+//      System.out.println("[SOFTWARE SOLUTION STRUCTURAL CHECK] request for component " + target.getName().getName() + " has no assignment.");
+    }
+
+    return faults + targets.size();
+  }
+
+  syn int Assignment.evaluateSoftwareCompletenessa() {
+    int faults = 0;
+
+    if (getRequest() == null) {
+      faults++;
+//      System.out.println("[SOFTWARE SOLUTION STRUCTURAL CHECK] assignment " + this.name() + " has no request.");
+    }
+    if (getImplementation() == null) {
+      faults++;
+//      System.out.println("[SOFTWARE SOLUTION STRUCTURAL CHECK] assignment " + this.name() + " has no implementation.");
+    }
+
+    for (ComponentRequirement requirement : getImplementation().getComponentRequirementList()) {
+      for (Instance instance : requirement.getInstanceList()) {
+        boolean foundInstance = false;
+        for (ComponentMapping mapping : this.getComponentMappingList()) {
+          if (mapping.getInstance() == instance) {
+            if (foundInstance == true) {
+              faults++;
+//              System.out.println("[SOFTWARE SOLUTION STRUCTURAL CHECK] instance " + instance.getName().getName() + " is contained twice in the component mapping.");
+            }
+            foundInstance = true;
+          }
+        }
+        if (!foundInstance) {
+          faults++;
+//          System.out.println("[SOFTWARE SOLUTION STRUCTURAL CHECK] instance " + instance.getName().getName() + " is not contained in the assignment.");
+        }
+      }
+    }
+
+    return faults;
+  }
+
+}
diff --git a/jastadd-mquat-benchmark/build.gradle b/jastadd-mquat-benchmark/build.gradle
index 173dc28..19d6281 100644
--- a/jastadd-mquat-benchmark/build.gradle
+++ b/jastadd-mquat-benchmark/build.gradle
@@ -15,6 +15,7 @@ dependencies {
     compile project(':jastadd-mquat-solver-aco')
     compile project(':jastadd-mquat-solver-ilp')
     compile project(':jastadd-mquat-solver-emfer')
+    compile project(':jastadd-mquat-solver-genetic')
     compile project(':jastadd-mquat-solver-simple')
     compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.8.8.1'
     compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
diff --git a/jastadd-mquat-benchmark/src/main/java/de/tudresden/inf/st/mquat/benchmark/SolverFactory.java b/jastadd-mquat-benchmark/src/main/java/de/tudresden/inf/st/mquat/benchmark/SolverFactory.java
index 402cad7..cdfe604 100644
--- a/jastadd-mquat-benchmark/src/main/java/de/tudresden/inf/st/mquat/benchmark/SolverFactory.java
+++ b/jastadd-mquat-benchmark/src/main/java/de/tudresden/inf/st/mquat/benchmark/SolverFactory.java
@@ -3,6 +3,7 @@ package de.tudresden.inf.st.mquat.benchmark;
 import de.tudresden.inf.st.mquat.solving.BenchmarkableSolver;
 import de.tudresden.inf.st.mquat.solving.ilp.ILPDirectSolver;
 import de.tudresden.inf.st.mquat.solving.ilp.ILPExternalSolver;
+import de.tudresden.inf.st.mquat.solving.genetic.GeneticSolver;
 import de.tudresden.inf.st.mquat.solving.simple.SimpleSolver;
 import ir.ac.ui.eng.ACOSolver;
 import uniks.EMFeRSolver;
@@ -28,7 +29,8 @@ public class SolverFactory {
           new EMFeRSolver(),
           new ILPExternalSolver(),
           new ILPDirectSolver(),
-          new SimpleSolver()
+          new SimpleSolver(),
+          new GeneticSolver()
       ).collect(Collectors.toMap(BenchmarkableSolver::getName, Function.identity()));
     }
     return availableSolvers;
diff --git a/jastadd-mquat-solver-genetic/.gitignore b/jastadd-mquat-solver-genetic/.gitignore
new file mode 100644
index 0000000..3cdae3a
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/.gitignore
@@ -0,0 +1,2 @@
+build/
+log/
\ No newline at end of file
diff --git a/jastadd-mquat-solver-genetic/build.gradle b/jastadd-mquat-solver-genetic/build.gradle
new file mode 100644
index 0000000..4ff9f9b
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/build.gradle
@@ -0,0 +1,24 @@
+
+apply plugin: 'java'
+
+sourceCompatibility = 1.8
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
+    compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
+    compile project(':jastadd-mquat-base')
+    compile project(':jastadd-mquat-solver')
+    testCompile group: 'junit', name: 'junit', version: '4.12'
+    testCompile project(path: ':jastadd-mquat-solver', configuration: 'testArtifacts')
+
+    compile group: 'org.opt4j', name: 'opt4j-core', version: '3.1.4'
+    compile group: 'org.opt4j', name: 'opt4j-optimizers', version: '3.1.4'
+    compile group: 'org.opt4j', name: 'opt4j-operators', version: '3.1.4'
+    compile group: 'org.opt4j', name: 'opt4j-satdecoding', version: '3.1.4'
+    compile group: 'org.opt4j', name: 'opt4j-viewer', version: '3.1.4'
+    compile group: 'org.opt4j', name: 'opt4j-benchmarks', version: '3.1.4'
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/GeneticSolver.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/GeneticSolver.java
new file mode 100644
index 0000000..bb07c44
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/GeneticSolver.java
@@ -0,0 +1,100 @@
+package de.tudresden.inf.st.mquat.solving.genetic;
+
+import de.tudresden.inf.st.mquat.jastadd.model.Root;
+import de.tudresden.inf.st.mquat.jastadd.model.Solution;
+import de.tudresden.inf.st.mquat.solving.BenchmarkableSolver;
+import de.tudresden.inf.st.mquat.solving.Solver;
+import de.tudresden.inf.st.mquat.solving.SolvingException;
+import de.tudresden.inf.st.mquat.utils.StopWatch;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class GeneticSolver implements BenchmarkableSolver {
+
+  private static final Logger logger = LogManager.getLogger(GeneticSolver.class);
+
+  private Solution lastSolution;
+  private long lastSolvingTime;
+
+  private int solutionCounter;
+
+  private StopWatch stopWatch;
+
+  private long maxSolvingTime;
+  private boolean timedOut;
+
+  public GeneticSolver() {
+    this(Long.MAX_VALUE);
+  }
+
+  public GeneticSolver(long maxSolvingTime) {
+    this.maxSolvingTime = maxSolvingTime;
+    reset();
+  }
+
+  @Override
+  public Solution solve(Root model) throws SolvingException {
+    reset();
+    if (model.getNumRequest() == 0) {
+      return Solution.emptySolutionOf(model);
+    }
+    stopWatch = StopWatch.start();
+
+    List<Solution> solutions = new ArrayList<>();
+
+    // do some solvin'
+
+    if (solutions.size() > 0) {
+      lastSolution = solutions.get(solutions.size() - 1);
+    } else {
+      lastSolution = Solution.emptySolutionOf(model);
+      logger.warn("Found no solution!");
+    }
+
+    lastSolvingTime = stopWatch.time(TimeUnit.MILLISECONDS);
+
+    return lastSolution;
+  }
+
+  private void reset() {
+    this.lastSolution = null;
+    this.solutionCounter = 0;
+    this.lastSolvingTime = 0;
+    this.timedOut = false;
+  }
+
+  @Override
+  public String getName() {
+    return "genetic";
+  }
+
+  @Override
+  public long getLastSolvingTime() {
+    return lastSolvingTime;
+  }
+
+  @Override
+  public double getLastObjective() {
+    if (lastSolution != null) {
+      return lastSolution.computeObjective();
+    } else {
+      // TODO throw exception or do something reasonable
+      return 0d;
+    }
+  }
+
+  @Override
+  public Solver setTimeout(long timeoutValue, TimeUnit timeoutUnit) {
+    this.maxSolvingTime = timeoutUnit.toMillis(timeoutValue);
+    return this;
+  }
+
+  @Override
+  public boolean hadTimeout() {
+    return this.timedOut;
+  }
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jCreator.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jCreator.java
new file mode 100644
index 0000000..948c888
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jCreator.java
@@ -0,0 +1,43 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j;
+
+import de.tudresden.inf.st.mquat.jastadd.model.*;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotype;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotypeNode;
+import org.opt4j.core.problem.Creator;
+
+import java.util.Random;
+
+public class Opt4jCreator implements Creator<TreeGenotype> {
+
+  public static TreeGenotypeNode<Request, Instance, Resource, Implementation> createNode(Assignment assignment, TreeGenotype<Request, Instance, Resource, Implementation> container) {
+
+    TreeGenotypeNode<Request, Instance, Resource, Implementation> node = new TreeGenotypeNode<>(container);
+    node.setImpl(assignment.getImplementation());
+    node.setResource(assignment.getResource());
+    for (ComponentMapping subAssignment : assignment.getComponentMappingList()) {
+      node.getSubStructure().put(subAssignment.getInstance(), createNode(subAssignment.getAssignment(), container));
+    }
+
+    return node;
+  }
+
+  public TreeGenotype<Request, Instance, Resource, Implementation> create() {
+
+    // TODO this method create a phenotype and translates it to a genotype. this is not nice!
+
+    Root model = Opt4jModule.getModel();
+
+    Solution phenotype = model.createRandomSolution(new Random());
+
+    TreeGenotype<Request, Instance, Resource, Implementation> genotype = new TreeGenotype<>(model.getHardwareModel().allContainers());
+
+    for (Assignment a : phenotype.getAssignmentList()) {
+      if (a.getTopLevel() && a.getImplementation().getName().getName().length() > 19) {
+        throw new RuntimeException();
+      }
+      genotype.getRequestMap().put(a.getRequest(), createNode(a, genotype));
+    }
+
+    return genotype;
+  }
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jDecoder.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jDecoder.java
new file mode 100644
index 0000000..6eee40f
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jDecoder.java
@@ -0,0 +1,74 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j;
+
+import de.tudresden.inf.st.mquat.jastadd.model.*;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotype;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotypeNode;
+import org.opt4j.core.problem.Decoder;
+
+import java.util.Map;
+
+public class Opt4jDecoder implements Decoder<TreeGenotype<Request, Instance, Resource, Implementation>, Solution> {
+
+
+  private static void populateResourceMapping(ResourceMapping mapping, ResourceRequirement requirement,
+                                              Resource resource) {
+    for (ResourceRequirement subRequirement : requirement.getResourceRequirementList()) {
+      int fittingResourceCount = 0;
+      for (int currentInstance = 0; currentInstance < subRequirement.getNumInstance(); currentInstance++) {
+        for (int currentResource = 0; currentResource < resource.getNumSubResource(); currentResource++) {
+          Resource subResource = resource.getSubResource(currentResource);
+          if (subResource.getType().getRef() == subRequirement.getResourceTypeRef().getRef()) {
+            if (currentInstance == fittingResourceCount) {
+              ResourceMapping newMapping = new ResourceMapping();
+              Instance instance = subRequirement.getInstance(currentInstance);
+              newMapping.setInstance(instance);
+              newMapping.setResource(subResource);
+              mapping.addResourceMapping(newMapping);
+              populateResourceMapping(newMapping, subRequirement, subResource);
+              fittingResourceCount++;
+            }
+            currentInstance++;
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public Solution decode(TreeGenotype<Request, Instance, Resource, Implementation> genotype) {
+    Solution phenotype = new Solution();
+    phenotype.setModel(Opt4jModule.getModel());
+    for (Map.Entry<Request, TreeGenotypeNode<Request, Instance, Resource, Implementation>> e : genotype.getRequestMap()
+        .entrySet()) {
+
+      phenotype.addAssignment(decodeNode(true, e.getKey(), e.getValue()));
+    }
+
+    return phenotype;
+  }
+
+  private Assignment decodeNode(boolean isTopLevel, Request request, TreeGenotypeNode<Request, Instance, Resource, Implementation> node) {
+
+    Assignment assignment = new Assignment();
+
+    assignment.setTopLevel(isTopLevel);
+    assignment.setRequest(request);
+    assignment.setImplementation(node.getImpl());
+
+    // fix the resource mapping
+    // we assume that exactly one resource is required
+    Instance resourceInstance = node.getImpl().getResourceRequirement().getInstance(0);
+    ResourceMapping rm = new ResourceMapping();
+    rm.setInstance(resourceInstance);
+    rm.setResource(node.getResource());
+    populateResourceMapping(rm, node.getImpl().getResourceRequirement(), node.getResource());
+    assignment.setResourceMapping(rm);
+
+    for (Map.Entry<Instance, TreeGenotypeNode<Request, Instance, Resource, Implementation>> e : node.getSubStructure()
+        .entrySet()) {
+
+      assignment.addComponentMapping(new ComponentMapping(e.getKey(), decodeNode(false, request, e.getValue())));
+    }
+    return assignment;
+  }
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jEvaluator.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jEvaluator.java
new file mode 100644
index 0000000..26bfffd
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jEvaluator.java
@@ -0,0 +1,34 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j;
+
+import de.tudresden.inf.st.mquat.jastadd.model.Solution;
+import org.opt4j.core.Objective.Sign;
+import org.opt4j.core.Objectives;
+import org.opt4j.core.problem.Evaluator;
+
+public class Opt4jEvaluator implements Evaluator<Solution> {
+
+  @Override
+  public Objectives evaluate(Solution phenotype) {
+    Objectives objectives = new Objectives();
+    objectives.add("Validity Errors", Sign.MIN, phenotype.evaluateValidity() * 1000);
+    objectives.add("Software Validity Errors", Sign.MIN, phenotype.evaluateSoftwareValidity() * 1000);
+    objectives.add("Structural Errors", Sign.MIN, phenotype.evaluateSoftwareCompletenessa() * 1000);
+
+    double objective = phenotype.computeObjective();
+
+    if (phenotype.evaluateValidity() != 0) {
+      objective *= 10;
+    }
+    if (phenotype.evaluateSoftwareValidity() != 0) {
+      objective *= 10;
+    }
+    if (phenotype.evaluateSoftwareCompletenessa() != 0) {
+      objective *= 10;
+    }
+
+    objectives.add("Energy", Sign.MIN, objective);
+
+    return objectives;
+  }
+
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jModule.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jModule.java
new file mode 100644
index 0000000..86f1ebc
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/Opt4jModule.java
@@ -0,0 +1,75 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j;
+
+import de.tudresden.inf.st.mquat.generator.ScenarioDescription;
+import de.tudresden.inf.st.mquat.jastadd.model.Root;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.copy.TreeCopyOperatorModule;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.crossover.TreeCrossoverOperatorModule;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.mutate.TreeMutateOperatorModule;
+import org.opt4j.core.Individual;
+import org.opt4j.core.optimizer.Archive;
+import org.opt4j.core.problem.ProblemModule;
+import org.opt4j.core.start.Opt4JTask;
+import org.opt4j.optimizers.ea.EvolutionaryAlgorithmModule;
+import org.opt4j.viewer.ViewerModule;
+
+public class Opt4jModule extends ProblemModule {
+
+  private static Root model;
+
+  public static Root getModel() {
+    return model;
+  }
+
+  public static void setModel(Root model) {
+    Opt4jModule.model = model;
+  }
+
+  public static void main(String[] args) {
+
+    final ScenarioDescription SCENARIO_DESCRIPTION = new ScenarioDescription(1, 2, 0, 0, 0, 2, 3, 1.5, 1, 1, 0);
+    de.tudresden.inf.st.mquat.generator.ScenarioGenerator sc = new de.tudresden.inf.st.mquat.generator.ScenarioGenerator(
+        SCENARIO_DESCRIPTION);
+
+    Opt4jModule.setModel(sc.generate());
+
+    EvolutionaryAlgorithmModule ea = new EvolutionaryAlgorithmModule();
+    ea.setGenerations(100000);
+
+    // set population size
+    ea.setAlpha(100);
+
+    Opt4jModule testModule = new Opt4jModule();
+
+    TreeCrossoverOperatorModule crossover = new TreeCrossoverOperatorModule();
+    TreeMutateOperatorModule mutate = new TreeMutateOperatorModule();
+    TreeCopyOperatorModule copy = new TreeCopyOperatorModule();
+
+    // testModule.setFunction(JohannesModule.Function.dtlz);
+    ViewerModule viewer = new ViewerModule();
+    viewer.isCloseOnStop();
+
+    Opt4JTask task = new Opt4JTask(false);
+    task.init(ea, testModule, viewer, crossover, mutate, copy);
+    try {
+      task.execute();
+      Archive archive = task.getInstance(Archive.class);
+      for (Individual individual : archive) {
+        // obtain the phenotype and objective, etc. of each individual
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      task.close();
+    }
+  }
+
+  @Override
+  protected void configure() {
+    bindProblem(Opt4jCreator.class, Opt4jDecoder.class, Opt4jEvaluator.class);
+  }
+
+  @Override
+  protected void config() {
+  }
+
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/genotypes/TreeGenotype.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/genotypes/TreeGenotype.java
new file mode 100644
index 0000000..abf0112
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/genotypes/TreeGenotype.java
@@ -0,0 +1,59 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes;
+
+import org.opt4j.core.Genotype;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class TreeGenotype<RqT, InT, ReT, ImT> implements Genotype {
+
+  private List<ReT> resources;
+  private Map<RqT, TreeGenotypeNode<RqT, InT, ReT, ImT>> requestMap = new HashMap<>();
+
+  public TreeGenotype(List<ReT> resources) {
+    this.resources = resources;
+  }
+
+  public List<ReT> getResources() {
+    return resources;
+  }
+
+  @Override
+  public int size() {
+    // TODO recurse to find out size
+    return 0;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public TreeGenotype<RqT, InT, ReT, ImT> newInstance() {
+    try {
+      Constructor<? extends TreeGenotype> cstr = this.getClass().getConstructor(List.class);
+      return (TreeGenotype<RqT, InT, ReT, ImT>) cstr.newInstance(getResources());
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public Map<RqT, TreeGenotypeNode<RqT, InT, ReT, ImT>> getRequestMap() {
+    return requestMap;
+  }
+
+  @Override
+  public String toString() {
+    return "[i=" + requestMap + "]";
+  }
+
+  public TreeGenotype<RqT, InT, ReT, ImT> deepCopy() {
+    TreeGenotype<RqT, InT, ReT, ImT> copy = this.newInstance();
+
+    for (RqT request : requestMap.keySet()) {
+      copy.getRequestMap().put(request, this.getRequestMap().get(request).deepCopy());
+    }
+
+    return copy;
+  }
+
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/genotypes/TreeGenotypeNode.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/genotypes/TreeGenotypeNode.java
new file mode 100644
index 0000000..7767486
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/genotypes/TreeGenotypeNode.java
@@ -0,0 +1,75 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes;
+
+import java.util.*;
+
+public class TreeGenotypeNode<RqT, InT, ReT, ImT> {
+  private ImT impl;
+  private ReT res;
+  private Map<InT, TreeGenotypeNode<RqT, InT, ReT, ImT>> subNodes;
+  private TreeGenotype<RqT, InT, ReT, ImT> containingTreeGenotype;
+
+  public TreeGenotypeNode(TreeGenotype genotype) {
+    this.containingTreeGenotype = genotype;
+    this.subNodes = new HashMap<>();
+  }
+
+  public ImT getImpl() {
+    return impl;
+  }
+
+  public void setImpl(ImT i) {
+    this.impl = i;
+  }
+
+  public TreeGenotype getContainingGenotype() {
+    return containingTreeGenotype;
+  }
+
+  public void setContainingGenotype(TreeGenotype genotype) {
+    this.containingTreeGenotype = genotype;
+    for (TreeGenotypeNode subnode : subNodes.values()) {
+      subnode.setContainingGenotype(genotype);
+    }
+  }
+
+  public ReT getResource() {
+    return res;
+  }
+
+  public void setResource(ReT r) {
+    this.res = r;
+  }
+
+  public Map<InT, TreeGenotypeNode<RqT, InT, ReT, ImT>> getSubStructure() {
+    return subNodes;
+  }
+
+  @Override
+  public String toString() {
+    return "[i=" + impl + ", r=" + res + ", c=" + subNodes + "]";
+  }
+
+  public TreeGenotypeNode<RqT, InT, ReT, ImT> deepCopy() {
+
+    TreeGenotypeNode<RqT, InT, ReT, ImT> copy = new TreeGenotypeNode<>(getContainingGenotype());
+
+    copy.setResource(this.getResource());
+    copy.setImpl(this.getImpl());
+
+    for (InT instance : this.getSubStructure().keySet()) {
+      copy.getSubStructure().put(instance, this.getSubStructure().get(instance).deepCopy());
+    }
+    return copy;
+  }
+
+  public Collection<ReT> containedResources() {
+
+    List<ReT> resources = new ArrayList<>();
+    resources.add(this.getResource());
+    for (TreeGenotypeNode subNode : subNodes.values()) {
+      resources.addAll(subNode.containedResources());
+    }
+
+    return resources;
+  }
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/copy/TreeCopyOperator.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/copy/TreeCopyOperator.java
new file mode 100644
index 0000000..979f951
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/copy/TreeCopyOperator.java
@@ -0,0 +1,16 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.copy;
+
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotype;
+import org.opt4j.operators.Apply;
+import org.opt4j.operators.copy.Copy;
+
+@Apply(TreeGenotype.class)
+public class TreeCopyOperator implements Copy<TreeGenotype> {
+
+  @Override
+  public TreeGenotype copy(TreeGenotype genotype) {
+
+    return genotype.deepCopy();
+  }
+
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/copy/TreeCopyOperatorModule.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/copy/TreeCopyOperatorModule.java
new file mode 100644
index 0000000..0b3f026
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/copy/TreeCopyOperatorModule.java
@@ -0,0 +1,12 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.copy;
+
+import org.opt4j.operators.copy.CopyModule;
+
+public class TreeCopyOperatorModule extends CopyModule {
+
+  @Override
+  protected void config() {
+    addOperator(TreeCopyOperator.class);
+  }
+
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/crossover/TreeCrossoverOperator.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/crossover/TreeCrossoverOperator.java
new file mode 100644
index 0000000..936133c
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/crossover/TreeCrossoverOperator.java
@@ -0,0 +1,94 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.crossover;
+
+import de.tudresden.inf.st.mquat.jastadd.model.Implementation;
+import de.tudresden.inf.st.mquat.jastadd.model.Instance;
+import de.tudresden.inf.st.mquat.jastadd.model.Request;
+import de.tudresden.inf.st.mquat.jastadd.model.Resource;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotype;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotypeNode;
+import org.opt4j.operators.Apply;
+import org.opt4j.operators.crossover.Crossover;
+import org.opt4j.operators.crossover.Pair;
+
+@Apply(TreeGenotype.class)
+public class TreeCrossoverOperator implements Crossover<TreeGenotype<Request, Instance, Resource, Implementation>> {
+
+  private static final double probability = 0.5;
+
+  @Override
+  public Pair<TreeGenotype<Request, Instance, Resource, Implementation>> crossover(
+      TreeGenotype<Request, Instance, Resource, Implementation> oldGenotype1,
+      TreeGenotype<Request, Instance, Resource, Implementation> oldGenotype2) {
+
+    TreeGenotype<Request, Instance, Resource, Implementation> newGenotype1 = oldGenotype1.newInstance();
+    TreeGenotype<Request, Instance, Resource, Implementation> newGenotype2 = oldGenotype2.newInstance();
+
+    for (Request request : oldGenotype1.getRequestMap().keySet()) {
+
+      TreeGenotypeNode childOfOldGenotype1 = oldGenotype1.getRequestMap().get(request);
+      TreeGenotypeNode childOfOldGenotype2 = oldGenotype2.getRequestMap().get(request);
+
+      Pair<TreeGenotypeNode<Request, Instance, Resource, Implementation>> crossedPair = nodeStructureCrossover(childOfOldGenotype1, newGenotype1, childOfOldGenotype2, newGenotype2);
+
+      newGenotype1.getRequestMap().put(request, crossedPair.getFirst());
+      newGenotype2.getRequestMap().put(request, crossedPair.getSecond());
+    }
+
+    return new Pair<>(newGenotype1, newGenotype2);
+  }
+
+  private Pair<TreeGenotypeNode<Request, Instance, Resource, Implementation>> nodeStructureCrossover(
+      TreeGenotypeNode<Request, Instance, Resource, Implementation> oldNode1,
+      TreeGenotype<Request, Instance, Resource, Implementation> newContainer1,
+      TreeGenotypeNode<Request, Instance, Resource, Implementation> oldNode2,
+      TreeGenotype<Request, Instance, Resource, Implementation> newContainer2) {
+
+    TreeGenotypeNode<Request, Instance, Resource, Implementation> newNode1 = new TreeGenotypeNode<>(newContainer1);
+    TreeGenotypeNode<Request, Instance, Resource, Implementation> newNode2 = new TreeGenotypeNode<>(newContainer2);
+
+    // if both nodes have the same impl and resource, recurse
+    if (oldNode1.getImpl() == oldNode2.getImpl() && oldNode1.getResource() == oldNode2.getResource()) {
+
+      // transfer impl and resource from old to new
+      newNode1.setImpl(oldNode1.getImpl());
+      newNode1.setResource(oldNode1.getResource());
+      newNode2.setImpl(oldNode2.getImpl());
+      newNode2.setResource(oldNode2.getResource());
+
+      // call the crossover on all child substructures
+      for (Instance instance : oldNode1.getSubStructure().keySet()) {
+        TreeGenotypeNode childOfOldNode1 = oldNode1.getSubStructure().get(instance);
+        TreeGenotypeNode childOfOldNode2 = oldNode2.getSubStructure().get(instance);
+        Pair<TreeGenotypeNode<Request, Instance, Resource, Implementation>> crossedPair = nodeStructureCrossover(childOfOldNode1, newContainer1, childOfOldNode2, newContainer2);
+
+        newNode1.getSubStructure().put(instance, crossedPair.getFirst());
+        newNode2.getSubStructure().put(instance, crossedPair.getSecond());
+      }
+    } else {
+      // TODO figure out what to swap
+      boolean swapImpl = true;
+      boolean swapResource = true;
+
+      if (swapImpl) {
+        newNode1.setImpl(oldNode2.getImpl());
+        newNode2.setImpl(oldNode1.getImpl());
+
+        // add swapped substructures
+        for (Instance instance : oldNode1.getSubStructure().keySet()) {
+          newNode2.getSubStructure().put(instance, oldNode1.getSubStructure().get(instance).deepCopy());
+        }
+        for (Instance instance : oldNode2.getSubStructure().keySet()) {
+          newNode1.getSubStructure().put(instance, oldNode2.getSubStructure().get(instance).deepCopy());
+        }
+      }
+
+      if (swapResource) {
+        newNode1.setResource(oldNode2.getResource());
+        newNode2.setResource(oldNode1.getResource());
+      }
+    }
+
+    return new Pair<>(newNode1, newNode2);
+  }
+
+}
\ No newline at end of file
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/crossover/TreeCrossoverOperatorModule.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/crossover/TreeCrossoverOperatorModule.java
new file mode 100644
index 0000000..89ce508
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/crossover/TreeCrossoverOperatorModule.java
@@ -0,0 +1,21 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.crossover;
+
+import org.opt4j.operators.crossover.CrossoverModule;
+
+public class TreeCrossoverOperatorModule extends CrossoverModule {
+
+  @Override
+  protected void config() {
+    // TODO Auto-generated method stub
+
+    addOperator(TreeCrossoverOperator.class);
+  }
+
+//	@Override
+//	protected void configure() {
+//		// TODO Auto-generated method stub
+//		bind(TreeOperator.class).to(TreeOperator.class);
+//	}
+//
+
+}
\ No newline at end of file
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/mutate/TreeMutateOperator.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/mutate/TreeMutateOperator.java
new file mode 100644
index 0000000..a28f1a8
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/mutate/TreeMutateOperator.java
@@ -0,0 +1,83 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.mutate;
+
+import com.google.inject.Inject;
+import de.tudresden.inf.st.mquat.jastadd.model.*;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.Opt4jCreator;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotype;
+import de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.genotypes.TreeGenotypeNode;
+import org.opt4j.core.common.random.Rand;
+import org.opt4j.operators.Apply;
+import org.opt4j.operators.mutate.Mutate;
+
+import java.util.List;
+import java.util.Random;
+
+@Apply(TreeGenotype.class)
+public class TreeMutateOperator implements Mutate<TreeGenotype<Request, Instance, Resource, Implementation>> {
+
+  protected final Random random;
+  public static double Min = 0.0d;
+  public static double Max = 1.0d;
+
+  @Inject
+  public TreeMutateOperator(Rand random) {
+    this.random = random;
+  }
+
+  @Override
+  public void mutate(TreeGenotype<Request, Instance, Resource, Implementation> genotype, double p) {
+
+    for (Request e : genotype.getRequestMap()
+        .keySet()) {
+
+      TreeGenotypeNode<Request, Instance, Resource, Implementation> node = genotype.getRequestMap().get(e);
+
+      double randomValue = Min + (Max - Min) * this.random.nextDouble();
+
+      if (true) {
+        genotype.getRequestMap().put(e, mutateNode(e, node, p));
+      }
+
+    }
+  }
+
+  private TreeGenotypeNode<Request, Instance, Resource, Implementation> mutateNode(Request request,
+      TreeGenotypeNode<Request, Instance, Resource, Implementation> node, double p) {
+
+    double Min = 0;
+    double Max = 1;
+    double randomValue = Min + (Max - Min) * this.random.nextDouble();
+
+    if (randomValue < .5) {
+
+      Component comp = node.getImpl().containingComponent();
+      Implementation newImp = comp.getImplementation(this.random.nextInt(comp.getNumImplementation()));
+
+      // clear substructure requirements
+      List<Resource> unassignedResources = node.getContainingGenotype().getResources();
+      // add newly freed resources
+      for (TreeGenotypeNode subNode : node.getSubStructure().values()) {
+        unassignedResources.addAll(subNode.containedResources());
+      }
+      node.getSubStructure().clear();
+
+      node.setImpl(newImp);
+
+      for (ComponentRequirement cr : newImp.getComponentRequirementList()) {
+        for (Instance ci : cr.getInstanceList()) {
+          // create node
+          Assignment newAssignment = newImp.root().createRandomAssignment(false, request, cr.getComponentRef().getRef(), unassignedResources, new Random());
+          node.getSubStructure().put(ci, Opt4jCreator.createNode(newAssignment, node.getContainingGenotype()));
+        }
+      }
+    } else {
+      // mutate the children
+      for (Instance instance : node.getSubStructure().keySet()) {
+        node.getSubStructure().put(instance, mutateNode(request, node.getSubStructure().get(instance), p));
+      }
+    }
+
+    return node;
+  }
+
+}
diff --git a/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/mutate/TreeMutateOperatorModule.java b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/mutate/TreeMutateOperatorModule.java
new file mode 100644
index 0000000..d23c427
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/main/java/de/tudresden/inf/st/mquat/solving/genetic/opt4j/custom/operator/mutate/TreeMutateOperatorModule.java
@@ -0,0 +1,12 @@
+package de.tudresden.inf.st.mquat.solving.genetic.opt4j.custom.operator.mutate;
+
+import org.opt4j.operators.mutate.MutateModule;
+
+public class TreeMutateOperatorModule extends MutateModule {
+
+	@Override
+	protected void config() {
+		addOperator(TreeMutateOperator.class);
+	}
+
+}
diff --git a/jastadd-mquat-solver-genetic/src/test/java/de/tudresden/inf/st/mquat/solving/SimpleHandwrittenTest.java b/jastadd-mquat-solver-genetic/src/test/java/de/tudresden/inf/st/mquat/solving/SimpleHandwrittenTest.java
new file mode 100644
index 0000000..380e0ad
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/test/java/de/tudresden/inf/st/mquat/solving/SimpleHandwrittenTest.java
@@ -0,0 +1,11 @@
+package de.tudresden.inf.st.mquat.solving;
+
+import de.tudresden.inf.st.mquat.solving.genetic.GeneticSolver;
+
+public class SimpleHandwrittenTest extends HandwrittenTestSuite {
+
+  @Override
+  protected Solver getSolver() {
+    return new GeneticSolver(10000);
+  }
+}
diff --git a/jastadd-mquat-solver-genetic/src/test/java/de/tudresden/inf/st/mquat/solving/SimpleSolverTest.java b/jastadd-mquat-solver-genetic/src/test/java/de/tudresden/inf/st/mquat/solving/SimpleSolverTest.java
new file mode 100644
index 0000000..1af1407
--- /dev/null
+++ b/jastadd-mquat-solver-genetic/src/test/java/de/tudresden/inf/st/mquat/solving/SimpleSolverTest.java
@@ -0,0 +1,54 @@
+package de.tudresden.inf.st.mquat.solving;
+
+import de.tudresden.inf.st.mquat.generator.ScenarioDescription;
+import de.tudresden.inf.st.mquat.generator.ScenarioGenerator;
+import de.tudresden.inf.st.mquat.jastadd.model.Root;
+import de.tudresden.inf.st.mquat.jastadd.model.Solution;
+import de.tudresden.inf.st.mquat.solving.genetic.GeneticSolver;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class SimpleSolverTest {
+
+  private static Logger logger;
+
+  @BeforeClass
+  public static void initLogger() {
+    logger = LogManager.getLogger(SimpleSolverTest.class);
+  }
+
+  /**
+   * tests the genetic solver with one very genetic use case
+   */
+  @Test
+  public void testSimpleSolver() throws SolvingException {
+    int tlc = 1;
+    int iac = 2;
+    int isd = 0;
+    int cac = 0;
+    int csd = 0;
+    int dep = 2;
+    int imp = 1;
+    int mod = 3;
+    double res = 1.5d;
+    int nfp = 0;
+    int req = 3;
+    int cpu = 1;
+    int seed = 0;
+
+    ScenarioGenerator generator = new ScenarioGenerator(new ScenarioDescription(tlc, iac, isd, cac, csd, dep, imp, res, req, cpu, seed));
+
+    Root model = generator.generate();
+    GeneticSolver solver = new GeneticSolver(20000);
+
+    Solution solution = solver.solve(model);
+
+    Assert.assertNotNull(solution);
+
+    logger.info("the best solution is {} and has an objective of {}.", (solution.isValid() ? "valid" : "invalid"), solution.computeObjective());
+
+  }
+}
diff --git a/settings.gradle b/settings.gradle
index 6eb5a59..68483c9 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -5,6 +5,7 @@ include ':jastadd-mquat-benchmark'
 include ':jastadd-mquat-solver'
 include ':jastadd-mquat-solver-ilp'
 include ':jastadd-mquat-solver-emfer'
+include ':jastadd-mquat-solver-genetic'
 include ':jastadd-mquat-solver-simple'
 include 'jastadd-mquat-solver-aco'
 
-- 
GitLab