From e11bce7ad3e803e80883499fec0ad6e4540ffe43 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Tue, 30 Jun 2020 18:03:48 +0200 Subject: Add modified VIATRA-DSE version --- .../org/eclipse/viatra/dse/api/DSEException.java | 47 ++ .../viatra/dse/api/DSETransformationRule.java | 51 ++ .../viatra/dse/api/DesignSpaceExplorer.java | 622 +++++++++++++++++++++ .../src/org/eclipse/viatra/dse/api/Objectives.java | 153 +++++ .../src/org/eclipse/viatra/dse/api/Solution.java | 60 ++ .../eclipse/viatra/dse/api/SolutionTrajectory.java | 338 +++++++++++ .../src/org/eclipse/viatra/dse/api/Strategies.java | 123 ++++ .../dse/api/strategy/impl/BestFirstStrategy.java | 228 ++++++++ .../api/strategy/impl/BreadthFirstStrategy.java | 220 ++++++++ .../dse/api/strategy/impl/DepthFirstStrategy.java | 188 +++++++ .../api/strategy/impl/FixedPriorityStrategy.java | 208 +++++++ .../api/strategy/impl/HillClimbingStrategy.java | 142 +++++ .../api/strategy/impl/RandomSearchStrategy.java | 163 ++++++ .../dse/api/strategy/interfaces/IStrategy.java | 44 ++ .../api/strategy/interfaces/IStrategyFactory.java | 13 + .../dse/base/ActivationCodesConflictSet.java | 213 +++++++ .../viatra/dse/base/DesignSpaceManager.java | 563 +++++++++++++++++++ .../viatra/dse/base/DseConflictResolver.java | 35 ++ .../eclipse/viatra/dse/base/DseConflictSet.java | 83 +++ .../eclipse/viatra/dse/base/DseEvmRuleBase.java | 21 + .../eclipse/viatra/dse/base/DseIdPoolHelper.java | 68 +++ .../eclipse/viatra/dse/base/ExplorerThread.java | 88 +++ .../org/eclipse/viatra/dse/base/GlobalContext.java | 374 +++++++++++++ .../viatra/dse/base/IDseStrategyContext.java | 117 ++++ .../dse/base/SingletonSetConflictResolver.java | 53 ++ .../org/eclipse/viatra/dse/base/ThreadContext.java | 542 ++++++++++++++++++ .../viatra/dse/designspace/api/DesignSpace.java | 106 ++++ .../dse/designspace/api/IBacktrackListener.java | 7 + .../viatra/dse/designspace/api/IDesignSpace.java | 27 + .../viatra/dse/designspace/api/TrajectoryInfo.java | 154 +++++ .../viatra/dse/multithreading/DSEThreadPool.java | 58 ++ .../dse/objectives/ActivationFitnessProcessor.java | 15 + .../eclipse/viatra/dse/objectives/Comparators.java | 31 + .../org/eclipse/viatra/dse/objectives/Fitness.java | 29 + .../viatra/dse/objectives/IGlobalConstraint.java | 58 ++ .../eclipse/viatra/dse/objectives/IObjective.java | 110 ++++ .../dse/objectives/LeveledObjectivesHelper.java | 114 ++++ .../dse/objectives/ObjectiveComparatorHelper.java | 217 +++++++ .../viatra/dse/objectives/TrajectoryFitness.java | 84 +++ .../impl/AlwaysSatisfiedDummyHardObjective.java | 52 ++ .../viatra/dse/objectives/impl/BaseObjective.java | 150 +++++ .../dse/objectives/impl/CompositeObjective.java | 137 +++++ .../dse/objectives/impl/ConstraintsObjective.java | 316 +++++++++++ .../dse/objectives/impl/DepthHardObjective.java | 89 +++ .../impl/ModelQueriesGlobalConstraint.java | 116 ++++ .../viatra/dse/objectives/impl/ModelQueryType.java | 14 + .../impl/NeverSatisfiedDummyHardObjective.java | 52 ++ .../impl/NoRuleActivationsHardObjective.java | 59 ++ .../impl/TrajectoryCostSoftObjective.java | 148 +++++ .../dse/solutionstore/ISolutionFoundHandler.java | 40 ++ .../dse/solutionstore/ISolutionNameProvider.java | 18 + .../solutionstore/IdBasedSolutionNameProvider.java | 37 ++ .../dse/solutionstore/LogSolutionHandler.java | 28 + .../ModelSaverSolutionFoundHandler.java | 55 ++ .../viatra/dse/solutionstore/SolutionStore.java | 311 +++++++++++ .../eclipse/viatra/dse/statecode/IStateCoder.java | 82 +++ .../viatra/dse/statecode/IStateCoderFactory.java | 29 + .../viatra/dse/statecoding/IObjectsProvider.java | 45 ++ .../dse/statecoding/IObjectsProviderFactory.java | 25 + .../dse/statecoding/IncrementalObjectProvider.java | 60 ++ .../IncrementalObjectProviderFactory.java | 18 + .../dse/statecoding/StatecodingDependency.java | 33 ++ .../statecoding/StatecodingDependencyGraph.java | 44 ++ .../dse/statecoding/StatecodingDependencyType.java | 15 + .../viatra/dse/statecoding/StatecodingNode.java | 100 ++++ .../dse/statecoding/StatecodingNodeType.java | 17 + .../viatra/dse/statecoding/TheStateCoder.java | 215 +++++++ .../dse/statecoding/TheStateCoderFactory.java | 40 ++ .../dse/statecoding/simple/SimpleStateCoder.java | 250 +++++++++ .../simple/SimpleStateCoderFactory.java | 38 ++ .../src/org/eclipse/viatra/dse/util/EMFHelper.java | 424 ++++++++++++++ .../src/org/eclipse/viatra/dse/util/Hasher.java | 86 +++ .../dse/util/ValueComparableEObjectStringMap.java | 63 +++ .../visualizer/DesignSpaceVisualizerOptions.java | 56 ++ .../dse/visualizer/IDesignSpaceVisualizer.java | 39 ++ .../dse/visualizer/IExploreEventHandler.java | 40 ++ 76 files changed, 9108 insertions(+) create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DSEException.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DSETransformationRule.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DesignSpaceExplorer.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Objectives.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Solution.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Strategies.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/BestFirstStrategy.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/BreadthFirstStrategy.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/DepthFirstStrategy.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/FixedPriorityStrategy.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/HillClimbingStrategy.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/RandomSearchStrategy.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/interfaces/IStrategy.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/interfaces/IStrategyFactory.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ActivationCodesConflictSet.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DesignSpaceManager.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseConflictResolver.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseConflictSet.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseEvmRuleBase.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseIdPoolHelper.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ExplorerThread.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/GlobalContext.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/IDseStrategyContext.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/SingletonSetConflictResolver.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ThreadContext.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/DesignSpace.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/IBacktrackListener.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/IDesignSpace.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/TrajectoryInfo.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/multithreading/DSEThreadPool.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/ActivationFitnessProcessor.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/Comparators.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/Fitness.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/IGlobalConstraint.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/IObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/LeveledObjectivesHelper.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/ObjectiveComparatorHelper.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/TrajectoryFitness.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/AlwaysSatisfiedDummyHardObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/BaseObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/CompositeObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ConstraintsObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/DepthHardObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ModelQueriesGlobalConstraint.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ModelQueryType.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/NeverSatisfiedDummyHardObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/NoRuleActivationsHardObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/TrajectoryCostSoftObjective.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ISolutionFoundHandler.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ISolutionNameProvider.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/IdBasedSolutionNameProvider.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/LogSolutionHandler.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ModelSaverSolutionFoundHandler.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/SolutionStore.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecode/IStateCoder.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecode/IStateCoderFactory.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IObjectsProvider.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IObjectsProviderFactory.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IncrementalObjectProvider.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IncrementalObjectProviderFactory.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependency.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependencyGraph.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependencyType.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingNode.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingNodeType.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/TheStateCoder.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/TheStateCoderFactory.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/simple/SimpleStateCoder.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/simple/SimpleStateCoderFactory.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/Hasher.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/ValueComparableEObjectStringMap.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/DesignSpaceVisualizerOptions.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/IDesignSpaceVisualizer.java create mode 100644 Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/IExploreEventHandler.java (limited to 'Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org') diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DSEException.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DSEException.java new file mode 100644 index 00000000..f0da19ed --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DSEException.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api; + +/** + * Represents a general runtime exception that happened during the execution of the design space exploration process. + * Problems that cause this exception are not recoverable within the scope of the design space exploration process. + */ +public class DSEException extends RuntimeException { + + private static final long serialVersionUID = -8312212010574763824L; + + /** + * @see RuntimeException#RuntimeException() + */ + public DSEException() { + super(); + } + + /** + * @see RuntimeException#RuntimeException(String) + */ + public DSEException(String message) { + super(message); + } + + /** + * @see RuntimeException#RuntimeException(String, Throwable) + */ + public DSEException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @see RuntimeException#RuntimeException(Throwable) + */ + public DSEException(Throwable cause) { + super(cause); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DSETransformationRule.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DSETransformationRule.java new file mode 100644 index 00000000..8c3511ae --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DSETransformationRule.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api; + +import java.util.Objects; +import java.util.function.Consumer; + +import org.eclipse.viatra.query.runtime.api.IPatternMatch; +import org.eclipse.viatra.query.runtime.api.IQuerySpecification; +import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +/** + * An instance of this class is a specification of a graph transformation rule on a given metamodel. Such a rule + * consists of a left hand side (LHS), which is specified by an {@link IQuerySpecification} and a right hand side (RHS), + * which is specified by an {@link Consumer}. + * + * @author Andras Szabolcs Nagy + * + * @param + * A VIATRA Query pattern match - left hand side of the rule + * @param + * A VIATRA Query pattern matcher - left hand side of the rule + * @deprecated + */ +@Deprecated +public class DSETransformationRule> extends + BatchTransformationRule { + + public DSETransformationRule(String name, IQuerySpecification querySpec, + Consumer action) { + super(name, querySpec, BatchTransformationRule.STATELESS_RULE_LIFECYCLE, action); + + Objects.requireNonNull(name); + Objects.requireNonNull(querySpec); + Objects.requireNonNull(action); + + } + + public DSETransformationRule(IQuerySpecification querySpec, + Consumer action) { + this(querySpec.getFullyQualifiedName(), querySpec, action); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DesignSpaceExplorer.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DesignSpaceExplorer.java new file mode 100644 index 00000000..9cd6e68a --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/DesignSpaceExplorer.java @@ -0,0 +1,622 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.base.DesignSpaceManager; +import org.eclipse.viatra.dse.base.GlobalContext; +import org.eclipse.viatra.dse.designspace.api.DesignSpace; +import org.eclipse.viatra.dse.designspace.api.IDesignSpace; +import org.eclipse.viatra.dse.objectives.IGlobalConstraint; +import org.eclipse.viatra.dse.objectives.IObjective; +import org.eclipse.viatra.dse.solutionstore.ISolutionNameProvider; +import org.eclipse.viatra.dse.solutionstore.IdBasedSolutionNameProvider; +import org.eclipse.viatra.dse.solutionstore.SolutionStore; +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.dse.statecode.IStateCoderFactory; +import org.eclipse.viatra.dse.statecoding.simple.SimpleStateCoderFactory; +import org.eclipse.viatra.dse.visualizer.IDesignSpaceVisualizer; +import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictResolver; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +/** + *

+ * The {@link DesignSpaceExplorer} is the main API of the Design Space Exploration engine. + *

+ * + *

+ * To parameterize the algorithm one must use the following methods after instantiating: + *

    + *
  • {@link #setInitialModel(EObject)} or it's overloads to set the starting model.
  • + *
  • {@link #addTransformationRule(BatchTransformationRule)} to define the transformations.
  • + *
  • {@link #startExploration(IStrategy)} or it's overloads to start an exploration with the given exploration + * strategy. Use the {@link Strategies} helper class for instantiating built-in, configurable exploration strategies. + *
  • + *
+ *

+ * + *

+ * Designs Space Exploration is the process of finding a sequence (or sequences) of predefined transformation + * rules ("transitions") that, if applied in order on the starting model, results in a new model state that fulfills the + * hard (or goal) constraints and is near optimal with respect to the objectives. + *

+ * + *

+ * An extension to this paradigm is the introduction of global constraints, which guarantees, that no sequence will be + * returned, which if executed, results in an intermediate model state that violates the specified global constraints, + * including the final state. You can add constraints by invoking {@link #addGlobalConstraint(IGlobalConstraint)}. + *

+ * + * @author Andras Szabolcs Nagy & Miklos Foldenyi + * + */ +public class DesignSpaceExplorer { + + private Notifier model; + + private GlobalContext globalContext = new GlobalContext(); + + private final Logger logger = Logger.getLogger(this.getClass()); + + private Set metaModelPackages = new HashSet(); + + private static final String MODEL_NOT_YET_GIVEN = "The starting model is not given yet. Please call the setInitialModel method first."; + + private boolean deepCopyModel; + + /** + *

+ * Creates a {@link DesignSpaceExplorer} object that is able to execute a design space exploration process. + *

+ * + *

+ * By default the state coder used is the generic (not meta-model specific) {@link GraphHash}. You can provide your + * custom state coder by implementing the {@link IStateCoderFactory} and {@link IStateCoder} interfaces, and passing + * the former to the {@link #setStateCoderFactory(IStateCoderFactory)} method. + * + */ + public DesignSpaceExplorer() { + setDesignspace(new DesignSpace()); + } + + /** + * Adds a metamodel in the form of {@link EPackage}, which is needed for certain guidance. + * + * @param metaModelPackage + */ + public void addMetaModelPackage(EPackage metaModelPackage) { + metaModelPackages.add(metaModelPackage); + } + + /** + * Defines the initial model of the exploration, and whether it is supposed to be used to execute the DSE process or + * it should be cloned. Please note, that in multithreaded mode any subsequent threads will be working on cloned + * models. + * + * @param model + * The root object of the EMF model. + * @param deepCopyModel + * If it is set to true, the exploration will run on a cloned model. + */ + public void setInitialModel(Notifier model, boolean deepCopyModel) { + this.model = model; + this.deepCopyModel = deepCopyModel; + } + + /** + * Defines the initial model of the exploration. The model will be cloned, which is desired in most cases as the + * given model won't be changed. + * + * @param model + * The root object of the EMF model. + */ + public void setInitialModel(Notifier model) { + setInitialModel(model, true); + } + + /** + * Defines the initial model of the exploration. The given model won't be cloned, thus the exploration will modify + * it. + * + * @param model + * The root object of the EMF model. It won't be cloned. + */ + public void setInitialModelUncloned(Notifier model) { + setInitialModel(model, false); + } + + /** + * Adds a {@link BatchTransformationRule}. + * + * @param rule + * The transformationRule. + */ + public void addTransformationRule(BatchTransformationRule rule) { + Preconditions.checkArgument(rule != null); + for (BatchTransformationRule rule2 : globalContext.getTransformations()) { + if (rule.getPrecondition().equals(rule2.getPrecondition())) { + throw new DSEException( + "Two transformation rule (" + + rule.getName() + + "; " + + rule2.getName() + + ") uses the same LHS VIATRA Query pattern (" + + rule.getPrecondition().getFullyQualifiedName() + + "), which may lead to hash collision." + + " Please wrap the pattern with an other pattern with the 'find' keyword (or duplicate the code), and use that for one of the rules LHS."); + } + } + + globalContext.getTransformations().add(rule); + } + + /** + * Adds a global constraint to the exploration process. Please see the {@link IGlobalConstraint} interface and its + * implementations for details. + * + * @param constraint + * The global constraint. + * @see IGlobalConstraint + */ + public void addGlobalConstraint(IGlobalConstraint constraint) { + globalContext.getGlobalConstraints().add(constraint); + } + + /** + * Adds an objective the the exploration process. Please see the {@link IObjective} interface and its + * implementations for details. + * + * @param objective + * The objective. + * @see IObjective + */ + public void addObjective(IObjective objective) { + for (IObjective o : globalContext.getObjectives()) { + if (o.getName().equals(objective.getName())) { + throw new DSEException("Two objectives with the same name cannot be registered:" + o.getName()); + } + } + globalContext.getObjectives().add(objective); + } + + /** + * Sets a {@link IStateCoderFactory} for which will be used for creating {@link IStateCoder}s. The default + * implementation is the {@link SimpleStateCoderFactory}, which works well in most of the cases. + * + * @param stateCoderFactory + * The factory. + */ + public final void setStateCoderFactory(IStateCoderFactory stateCoderFactory) { + globalContext.setStateCoderFactory(stateCoderFactory); + } + + /** + * Defines the maximum processing threads that the design space exploration can use. Note, that this is only + * limiting the threads doing the actual calculation. By default this value will be set to the number of logical + * processors (including HyperThreading) in the computer, reported by {@link Runtime#availableProcessors()}. + * + * @param maxNumberOfThreads + * The number of maximum processing threads available to the design space exploration process. + */ + public void setMaxNumberOfThreads(int maxNumberOfThreads) { + globalContext.getThreadPool().setMaximumPoolSize(maxNumberOfThreads); + } + + /** + * Sets the {@link IDesignSpace} implementation that is to be used during the design space exploration process. By + * default, the {@link DesignSpace} implementation is used. + * + * @param designspace + * The {@link IDesignSpace} implementation. + */ + public final void setDesignspace(IDesignSpace designspace) { + globalContext.setDesignSpace(designspace); + } + + /** + * Sets the solution store for strategies. Please see the {@link SolutionStore} for how to configure it. + * + * @param solutionStore + * The parameterized {@link SolutionStore} implementation. + */ + public void setSolutionStore(SolutionStore solutionStore) { + globalContext.setSolutionStore(solutionStore); + } + + /** + * Starts the design space exploration. It returns only when the strategy decides to stop the execution. + * + * @param strategy + * The strategy of the exploration. + */ + public void startExploration(IStrategy strategy) { + startExploration(strategy, true, -1); + } + + /** + * Starts the design space exploration asynchronously. Completion of the process can be verified by calling + * {@link DesignSpaceExplorer#isDone()}. + * + * @param strategy + * The strategy of the exploration. + */ + public void startExplorationAsync(IStrategy strategy) { + startExploration(strategy, false, -1); + } + + /** + * Starts the design space exploration with a timeout. It returns only when the strategy decides to stop the + * execution or the given timeout is elapsed. + * + * @param strategy + * The strategy of the exploration. + * @param timeout + * The number of milliseconds before the exploration is forced to stop. + * @return Returns true if the exploration stopped by the timeout. + */ + public boolean startExplorationWithTimeout(IStrategy strategy, long timeout) { + return startExploration(strategy, true, timeout); + } + + /** + * Starts the design space exploration asynchronously with a timeout. Completion of the process can be verified by + * calling {@link DesignSpaceExplorer#isDone()}. + * + * @param strategy + * The strategy of the exploration. + * @param timeout + * The number of milliseconds before the exploration is forced to stop. + * @return Returns true if the exploration stopped by the timeout. + */ + public boolean startExplorationAsyncWithTimeout(IStrategy strategy, long timeout) { + return startExploration(strategy, false, timeout); + } + + /** + * Starts the design space exploration. If {@code waitForTermination} is true, then it returns only when the + * strategy decides to stop the execution or there was a timeout, otherwise when the exploration process is started + * it returns immediately. In this case, process completion can be verified by calling + * {@link DesignSpaceExplorer#isDone()}. + * + * @param strategy + * The strategy of the exploration. + * @param waitForTermination + * True if the method must wait for the engine to stop, i.e. whether to start synchronously. + * @param timeout + * The number of milliseconds before the exploration is forced to stop. + * @return Returns true if the exploration stopped by the timeout. + */ + public boolean startExploration(IStrategy strategy, boolean waitForTermination, final long timeout) { + initExploration(strategy); + + Timer timer = new Timer(); + final AtomicBoolean wasTimeout = new AtomicBoolean(false); + + if (timeout > 0) { + TimerTask timerTask = new TimerTask() { + @Override + public void run() { + logger.info("Timeout, stopping threads..."); + globalContext.stopAllThreads(); + wasTimeout.set(true); + } + }; + timer.schedule(timerTask, timeout); + } + + if (waitForTermination) { + waitForTerminaition(); + timer.cancel(); + } else { + logger.info("Design space exploration started asynchronously."); + } + + return wasTimeout.get(); + + } + + private void initExploration(IStrategy strategy) { + Preconditions.checkArgument(model != null, MODEL_NOT_YET_GIVEN); + Preconditions.checkArgument(strategy != null, "A strategy must be given. Use the Strategies helper class."); + Preconditions.checkState(!globalContext.getTransformations().isEmpty(), + "At least one transformation rule must be added to start the exploration."); + + if (globalContext.getStateCoderFactory() == null) { + if (getMetaModelPackages() == null || getMetaModelPackages().isEmpty()) { + throw new DSEException("Cannot initialize state coder." + + " Please specifiy the EPackages your model uses with addMetaModelPackage(EPackage)"); + } + globalContext.setStateCoderFactory(new SimpleStateCoderFactory(getMetaModelPackages())); + } + + logger.info("DesignSpaceExplorer started exploration."); + + if (deepCopyModel) { + globalContext.startFirstThread(strategy, model); + } else { + globalContext.startFirstThreadWithoutModelClone(strategy, model); + } + } + + /** + * Returns all of the found {@link Solution}s, trajectories. Call it after + * {@link DesignSpaceExplorer#startExploration()}. Calling this while the process is running returns the solutions + * that have been found so far. The returned {@link Solution} objects may change internal state after they + * have been returned, if a shorter trajectory has been found to the referred state. + * + * @return The found solutions. + */ + public Collection getSolutions() { + return globalContext.getSolutionStore().getSolutions(); + } + + /** + * Returns an arbitrary solution trajectory or null if the exploration failed to find any. + * + * @return An arbitrary solution trajectory. + */ + public SolutionTrajectory getArbitrarySolution() { + Collection solutions = getSolutions(); + if (solutions.isEmpty()) { + return null; + } + return solutions.iterator().next().getArbitraryTrajectory(); + } + + /** + * Returns the number of distinct states the exploration process has visited so far. + * + * @return the number of distinct states. + */ + public long getNumberOfStates() { + return globalContext.getDesignSpace().getNumberOfStates(); + } + + /** + * Returns the number of distinct transitions the exploration process has discovered (but not necessarily traversed) + * so far. + * + * @return the number of distinct transitions. + */ + public long getNumberOfTransitions() { + return globalContext.getDesignSpace().getNumberOfTransitions(); + } + + /** + * Returns the {@link EPackage}s, which were registered with the + * {@link DesignSpaceExplorer#addMetaModelPackage(EPackage)} method. + * + * @return The set of meta model packages. + */ + public Set getMetaModelPackages() { + return metaModelPackages; + } + + /** + * Returns true if the {@link IExplorerThread strategy} decided to stop, and all the threads finished their work. + * + * @return true if the process has finished, false otherwise. + */ + public boolean isDone() { + return globalContext.isDone(); + } + + /** + * Returns the {@link GlobalContext} which holds the configurations such as rule, objectives, etc. + * + * @return The global context. + */ + public GlobalContext getGlobalContext() { + return globalContext; + } + + /** + * Registers a design space visualizer. Please see the corresponding interface {@link IDesignSpaceVisualizer}. + * + * @see IDesignSpaceVisualizer + * + * @param visualizer + */ + public void addDesignSpaceVisulaizer(IDesignSpaceVisualizer visualizer) { + globalContext.registerDesignSpaceVisualizer(visualizer); + } + + /** + * Creates a string containing the state codes of all the found solutions and the found trajectories to these + * solutions with fitness values. + * + * @return A pretty string with the solutions. + */ + public String toStringSolutions() { + StringBuilder sb = new StringBuilder(); + Collection solutions = getSolutions(); + sb.append("Number of solutions: "); + sb.append(solutions.size()); + sb.append("\n"); + for (Solution solution : solutions) { + sb.append("Solution: "); + sb.append(solution.getStateCode()); + sb.append("\n"); + for (SolutionTrajectory trajectory : solution.getTrajectories()) { + sb.append(" "); + sb.append(trajectory.toPrettyString()); + sb.append("\n"); + } + } + return sb.toString(); + } + + /** + * A conflict resolver can filter rule activations the DSE engine will see. The primary use of this is symmetry + * reduction. This function is subject to change for better API. + * + * @param conflictResolver + */ + public void setConflictResolver(ConflictResolver conflictResolver) { + globalContext.setConflictResolver(conflictResolver); + } + + /** + * Enumeration for different use cases of logging, including: + *

    + *
  • OFF - no error messages.
  • + *
  • WARN - only error and warn messages.
  • + *
  • BASIC - logs basic information on how the exploration is going.
  • + *
  • VERBOSE_STRATEGY - logs everything the exploration strategy is prepared for.
  • + *
  • VERBOSE_FULL - logs every transformation.
  • + *
+ * + * @author Andras Szabolcs Nagy + * + */ + public enum DseLoggingLevel { + OFF, WARN, BASIC, VERBOSE_STRATEGY, VERBOSE_FULL + } + + /** + * Changes the level of logging. See {@link DseLoggingLevel} for details. + * + * @param dseLoggingLevel + */ + public static void turnOnLogging(DseLoggingLevel dseLoggingLevel) { + switch (dseLoggingLevel) { + case OFF: + Logger.getLogger(DesignSpaceExplorer.class).setLevel(Level.OFF); + Logger.getLogger(IStrategy.class).setLevel(Level.OFF); + Logger.getLogger(DesignSpaceManager.class).setLevel(Level.OFF); + break; + case WARN: + Logger.getLogger(DesignSpaceExplorer.class).setLevel(Level.WARN); + Logger.getLogger(IStrategy.class).setLevel(Level.WARN); + Logger.getLogger(DesignSpaceManager.class).setLevel(Level.WARN); + break; + case BASIC: + Logger.getLogger(DesignSpaceExplorer.class).setLevel(Level.INFO); + Logger.getLogger(IStrategy.class).setLevel(Level.INFO); + Logger.getLogger(DesignSpaceManager.class).setLevel(Level.WARN); + break; + case VERBOSE_STRATEGY: + Logger.getLogger(DesignSpaceExplorer.class).setLevel(Level.DEBUG); + Logger.getLogger(IStrategy.class).setLevel(Level.DEBUG); + Logger.getLogger(DesignSpaceManager.class).setLevel(Level.WARN); + break; + case VERBOSE_FULL: + Logger.getLogger(DesignSpaceExplorer.class).setLevel(Level.DEBUG); + Logger.getLogger(IStrategy.class).setLevel(Level.DEBUG); + Logger.getLogger(DesignSpaceManager.class).setLevel(Level.DEBUG); + break; + default: + throw new DSEException("Not supported logging level."); + } + } + + /** + * Changes the level of logging. See {@link DseLoggingLevel} for details. + * + * Also configures a basic console appender for log4j. + * + * @param dseLoggingLevel + */ + public static void turnOnLoggingWithBasicConfig(DseLoggingLevel dseLoggingLevel) { + BasicConfigurator.configure(); + Logger.getRootLogger().setLevel(Level.WARN); + turnOnLogging(dseLoggingLevel); + } + + /** + * Stops the exploration and waits for termination. It has no effect if the exploration is already terminated or not + * even started. + */ + public void stopExploration() { + if (globalContext.isDone()) { + logger.info("Cannot stop exploration - design space exploration has already finished."); + } else if (globalContext.isNotStarted()) { + logger.info("Cannot stop exploration - design space exploration has not been started."); + } else { + globalContext.stopAllThreads(); + waitForTerminaition(); + } + } + + /** + * Stops the exploration asynchronously. It has no effect if the exploration is already terminated or not even + * started. + */ + public void stopExplorationAsync() { + if (globalContext.isDone()) { + logger.info("Cannot stop exploration - design space exploration has already finished."); + } else if (globalContext.isNotStarted()) { + logger.info("Cannot stop exploration - design space exploration has not been started."); + } else { + globalContext.stopAllThreads(); + } + } + + /** + * Waits for termination. + */ + public void waitForTerminaition() { + globalContext.waitForTermination(); + } + + /** + * Serializes all the found solutions by transforming the given initial model. + *

Files will be named solution[id].xmi. + * @param model The initial model. + */ + public void saveModels(Notifier model) { + this.saveModels(model, "solution", "xmi"); + } + + /** + * Serializes all the found solutions by transforming the given initial model. + *

Files will be named solution[id].[extension]. + * @param model The initial model. + * @param extension The extension of the omitted file. + */ + public void saveModels(Notifier model, String extension) { + this.saveModels(model, "solution", extension); + } + + /** + * Serializes all the found solutions by transforming the given initial model. + *

Files will be named [fileNamePrefix][id].[extension]. + * @param model The initial model. + * @param fileNamePrefix The prefix (optionally including a file path) of the omitted file. + * @param extension The extension of the omitted file. + */ + public void saveModels(Notifier model, String fileNamePrefix, String extension) { + globalContext.getSolutionStore().saveModels(model, new IdBasedSolutionNameProvider(fileNamePrefix, extension)); + } + + /** + * Serializes all the found solutions by transforming the given initial model. + *

Files will be named using the {@link ISolutionNameProvider}. + * @param model The initial model. + */ + public void saveModels(Notifier model, ISolutionNameProvider solutionNameProvider) { + globalContext.getSolutionStore().saveModels(model, solutionNameProvider); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Objectives.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Objectives.java new file mode 100644 index 00000000..3b375fac --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Objectives.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api; + +import org.eclipse.viatra.dse.objectives.impl.CompositeObjective; +import org.eclipse.viatra.dse.objectives.impl.ConstraintsObjective; +import org.eclipse.viatra.dse.objectives.impl.AlwaysSatisfiedDummyHardObjective; +import org.eclipse.viatra.dse.objectives.impl.DepthHardObjective; +import org.eclipse.viatra.dse.objectives.impl.NeverSatisfiedDummyHardObjective; +import org.eclipse.viatra.dse.objectives.impl.NoRuleActivationsHardObjective; +import org.eclipse.viatra.dse.objectives.impl.TrajectoryCostSoftObjective; + +/** + * + * Helper class for creating built-in objectives. + * + * @author Andras Szabolcs Nagy + * + */ +public class Objectives { + + private Objectives() { + } + + /** + * This objective uses VIATRA Queries to calculate fitness and/or goal constraints. Use methods on the returned + * objective to configure it. + * + * @param name + * @return The objective. + * @see ConstraintsObjective + */ + public static ConstraintsObjective createConstraintsObjective(String name) { + return new ConstraintsObjective(name); + } + + /** + * This objective calculates fitness on the trajectory by adding either fix costs to the rules, or by calculating + * custom fitness on activation of rules. + * + * @param name + * @return The objective. + * @see TrajectoryCostSoftObjective + */ + public static TrajectoryCostSoftObjective createTrajcetoryCostObjective(String name) { + return new TrajectoryCostSoftObjective(name); + } + + /** + * This objective adds a goal constraint that a solution state should not have any activations. + * + * @return The objective. + * @see NoRuleActivationsHardObjective + */ + public static NoRuleActivationsHardObjective createNoRuleActivationsHardConstraint() { + return new NoRuleActivationsHardObjective(); + } + + /** + * This objective adds a goal constraint that a solution state should not have any activations. + * + * @param name + * @return The objective. + * @see NoRuleActivationsHardObjective + */ + public static NoRuleActivationsHardObjective createNoRuleActivationsHardConstraint(String name) { + return new NoRuleActivationsHardObjective(name); + } + + /** + * This objective can combine the calculated fitness value of other objectives. Weights are supported. + * + * @param name + * @return The objective. + * @see NoRuleActivationsHardObjective + */ + public static CompositeObjective createCompositeObjective(String name) { + return new CompositeObjective(name); + } + + /** + * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid + * solution. + * + * @return The objective. + * @see AlwaysSatisfiedDummyHardObjective + */ + public static AlwaysSatisfiedDummyHardObjective createAlwaysSatisfiedDummyHardObjective() { + return new AlwaysSatisfiedDummyHardObjective(); + } + + /** + * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid + * solution. + * + * @param name + * @return The objective. + * @see AlwaysSatisfiedDummyHardObjective + */ + public static AlwaysSatisfiedDummyHardObjective createDummyHardObjective(String name) { + return new AlwaysSatisfiedDummyHardObjective(name); + } + + /** + * This hard objective is never fulfilled. Use it if all states should be regarded as an invalid solution. + * + * @return The objective. + * @see AlwaysSatisfiedDummyHardObjective + */ + public static NeverSatisfiedDummyHardObjective createNeverSatisfiedDummyHardObjective() { + return new NeverSatisfiedDummyHardObjective(); + } + + /** + * This hard objective is never fulfilled. Use it if all states should be regarded as an invalid solution. + * + * @return The objective. + * @see AlwaysSatisfiedDummyHardObjective + */ + public static NeverSatisfiedDummyHardObjective createNeverSatisfiedDummyHardObjective(String name) { + return new NeverSatisfiedDummyHardObjective(name); + } + + /** + * This hard objective is fulfilled if the length of the trajectory is in the specified interval (inclusive). Use + * {@link DepthHardObjective#withMinDepth(int)} and {@link DepthHardObjective#withMaxDepth(int)} to configure. + * + * @return The objective. + * @see DepthHardObjective + */ + public static DepthHardObjective createDepthHardObjective() { + return new DepthHardObjective(); + } + + /** + * This hard objective is fulfilled if the length of the trajectory is in the specified interval (inclusive). Use + * {@link DepthHardObjective#withMinDepth(int)} and {@link DepthHardObjective#withMaxDepth(int)} to configure. + * + * @param name + * @return The objective. + * @see DepthHardObjective + */ + public static DepthHardObjective createDepthHardObjective(String name) { + return new DepthHardObjective(name); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Solution.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Solution.java new file mode 100644 index 00000000..b776db7a --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Solution.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +public class Solution { + + private Set trajectories; + private final Object stateId; + + public Solution(Object stateId, SolutionTrajectory trajectory) { + this.stateId = stateId; + trajectories = new HashSet<>(); + trajectories.add(trajectory); + } + + public void addTrajectory(SolutionTrajectory trajectory) { + trajectories.add(trajectory); + } + + public SolutionTrajectory getArbitraryTrajectory() { + return trajectories.iterator().next(); + } + + public SolutionTrajectory getShortestTrajectory() { + Iterator iterator = trajectories.iterator(); + SolutionTrajectory shortestTrajecotry = iterator.next(); + int minSize = shortestTrajecotry.getTrajectoryLength(); + + while (iterator.hasNext()) { + SolutionTrajectory traj = iterator.next(); + int size = traj.getTrajectoryLength(); + if (size < minSize) { + shortestTrajecotry = traj; + minSize = size; + } + } + + return shortestTrajecotry; + } + + public Collection getTrajectories() { + return trajectories; + } + + public Object getStateCode() { + return stateId; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java new file mode 100644 index 00000000..d1a41065 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/SolutionTrajectory.java @@ -0,0 +1,338 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; + +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.edit.command.ChangeCommand; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.viatra.dse.base.DseIdPoolHelper; +import org.eclipse.viatra.dse.designspace.api.IBacktrackListener; +import org.eclipse.viatra.dse.objectives.Fitness; +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.dse.statecode.IStateCoderFactory; +import org.eclipse.viatra.dse.util.EMFHelper; +import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; +import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher; +import org.eclipse.viatra.query.runtime.emf.EMFScope; +import org.eclipse.viatra.query.runtime.matchers.ViatraQueryRuntimeException; +import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +import com.google.common.util.concurrent.UncheckedExecutionException; + +/** + * A SolutionTrajectory represents a trajectory (i.e. sequence of transformation + * rule applications), which can transform the initial model to a desired state. + * An instance of this class holds the the actual rule sequence and the + * corresponding activation codes. Furthermore it can be used to perform the + * transformation on a given model (if possible). + *

+ * It is also possible to undo the transformation if initialized with an editing + * domain. + *

+ * The instance of this class can be reused for different models. + * + * @author Andras Szabolcs Nagy + * + */ +public class SolutionTrajectory { + + private final List activationCodes; + private final List> transformationRules; + private final IStateCoderFactory stateCoderFactory; + private Fitness fitness; + private Solution solution; + + private ViatraQueryEngine engine; + private Notifier model; + private EditingDomain editingDomain; + private IStateCoder stateCoder; + private IBacktrackListener listener; + + private int currentIndex; + + public SolutionTrajectory(final List activationCodes, + final List> transformationRules, final IStateCoderFactory stateCoderFactory, + final IBacktrackListener backtrackListener) { + Objects.requireNonNull(transformationRules, "Parameter transformationRules cannot be null!"); + Objects.requireNonNull(stateCoderFactory, "Parameter stateCoderFactory cannot be null!"); + Objects.requireNonNull(activationCodes, "Parameter activations cannot be null!"); + Preconditions.checkState(transformationRules.size() == activationCodes.size(), + "The two List parameters must be the same in size."); + + this.activationCodes = activationCodes; + this.transformationRules = transformationRules; + this.stateCoderFactory = stateCoderFactory; + this.listener = backtrackListener; + currentIndex = 0; + } + + /** + * Initialize this SolutionTrajectory for transforming the model along the + * trajectory. + * + * @param model The model. + * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. + */ + public void setModel(Notifier model) { + editingDomain = null; + EMFScope scope = new EMFScope(model); + this.engine = ViatraQueryEngine.on(scope); + this.model = model; + stateCoder = stateCoderFactory.createStateCoder(); + stateCoder.init(model); + currentIndex = 0; + DseIdPoolHelper.INSTANCE.disposeByThread(); + DseIdPoolHelper.INSTANCE.registerRules(rule -> { + int id = 0; + for (BatchTransformationRule r : transformationRules.subList(0, currentIndex)) { + if (r.equals(rule)) { + id++; + } + } + return id; + }, new HashSet>(transformationRules)); + } + + /** + * Initialize this SolutionTrajectory for transforming the given model along the + * trajectory. + *

+ * The transformation will be reversible by creating an {@link EditingDomain} on + * the model. + * + * @param modelRoot The root of the model. + * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. + */ + public void setModelWithEditingDomain(Notifier modelRoot) { + setModel(modelRoot); + editingDomain = EMFHelper.createEditingDomain(model); + } + + /** + * Transforms the given model along the trajectory. + * + * @param modelRoot The root of the model. + * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. + */ + public void doTransformation(Notifier modelRoot) { + setModel(modelRoot); + doTransformation(); + } + + /** + * Transforms the given model along the trajectory. + *

+ * The transformation will be reversible by creating an {@link EditingDomain} on + * the model. + * + * @param modelRoot The root of the model. + * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. + */ + public void doTransformationUndoable(Notifier modelRoot) { + setModelWithEditingDomain(modelRoot); + doTransformation(); + } + + /** + * Transforms the given model along the trajectory. To initialize the model call + * the {@link SolutionTrajectory#setModel(Notifier)} method. + * + * @throws Exception If the activation to fire is not found. + * Possible problems: wrong model, bad state + * serializer. + * @throws ViatraQueryRuntimeException If the VIATRA Query fails to initialize. + */ + public void doTransformation() { + while (doNextTransformation()) + ; + } + + /** + * Transforms the given model by one step to the solution (makes one step in the + * trajectory). To initialize the model call the + * {@link SolutionTrajectory#setModel(Notifier)} method. + * + * @throws ViatraQueryRuntimeException + */ + public boolean doNextTransformation() { + if (currentIndex >= activationCodes.size()) { + return false; + } else { + doNextTransformation(currentIndex); + currentIndex++; + return true; + } + } + + @SuppressWarnings("unchecked") + private void doNextTransformation(int index) { + Objects.requireNonNull(model, "The model cannot be null! Use the setModel method."); + + // cast for the ".process(match)" method. + BatchTransformationRule tr = transformationRules.get(index); + Object activationCode = activationCodes.get(index); + + ViatraQueryMatcher matcher = tr.getPrecondition().getMatcher(engine); + + boolean isActivationFound = false; + for (final IPatternMatch match : matcher.getAllMatches()) { + Object matchHash = stateCoder.createActivationCode(match); + if (matchHash.equals(activationCode)) { + @SuppressWarnings("rawtypes") + final Consumer action = tr.getAction(); + + if (editingDomain == null) { + action.accept(match); + } else { + ChangeCommand cc = new ChangeCommand(model) { + @Override + protected void doExecute() { + action.accept(match); + } + }; + long start = System.nanoTime(); + editingDomain.getCommandStack().execute(cc); + listener.forwardWorked(System.nanoTime() - start); + } + + isActivationFound = true; + break; + } + } + if (!isActivationFound) { + throw new UncheckedExecutionException( + "Activation was not found for transformation! Possible cause: wrong model, bad state coder. index: " + + index + " Activation code: " + activationCode, + null); + } + } + + /** + * Call this method to undo the last transformation. + * + * @return True, if it was successful. + */ + public boolean undoLastTransformation() { + Objects.requireNonNull(editingDomain, "To be able to undo the transformation initialize with editing domain."); + long start = System.nanoTime(); + boolean result; + + if (currentIndex > 0) { + try { + ((AdvancedViatraQueryEngine) engine).delayUpdatePropagation(() -> { + editingDomain.getCommandStack().undo(); + return null; + }); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + currentIndex--; + result = true; + } + result = false; + listener.backtrackWorked(System.nanoTime() - start); + return result; + } + + /** + * Call this method to undo the transformation. + */ + public void undoTransformation() { + while (undoLastTransformation()) + ; + } + + public List getActivationCodes() { + return activationCodes; + } + + public List> getTransformationRules() { + return transformationRules; + } + + public IStateCoderFactory getStateCoderFactory() { + return stateCoderFactory; + } + + public ViatraQueryEngine getEngine() { + return engine; + } + + public Notifier getModel() { + return model; + } + + public IStateCoder getStateCoder() { + return stateCoder; + } + + public int getCurrentIndex() { + return currentIndex; + } + + public int getTrajectoryLength() { + return activationCodes.size(); + } + + public Fitness getFitness() { + return fitness; + } + + public void setFitness(Fitness fitness) { + this.fitness = fitness; + } + + public String toPrettyString() { + StringBuilder sb = new StringBuilder(); + sb.append("Fitness: "); + sb.append(fitness.toString()); + sb.append(" | Trajectory ("); + sb.append(activationCodes.size()); + sb.append("): "); + for (Object object : activationCodes) { + sb.append(object.toString()); + sb.append(" | "); + } + return sb.toString(); + } + + @Override + public int hashCode() { + return activationCodes.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof SolutionTrajectory) { + SolutionTrajectory that = (SolutionTrajectory) obj; + return activationCodes.equals(that.activationCodes); + } + return false; + } + + public Solution getSolution() { + return solution; + } + + public void setSolution(Solution solution) { + this.solution = solution; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Strategies.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Strategies.java new file mode 100644 index 00000000..ed7a90da --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/Strategies.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api; + +import org.eclipse.viatra.dse.api.strategy.impl.BestFirstStrategy; +import org.eclipse.viatra.dse.api.strategy.impl.BreadthFirstStrategy; +import org.eclipse.viatra.dse.api.strategy.impl.DepthFirstStrategy; +import org.eclipse.viatra.dse.api.strategy.impl.FixedPriorityStrategy; +import org.eclipse.viatra.dse.api.strategy.impl.HillClimbingStrategy; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; + +/** + * Helper class for instantiating strategies. To implement a new strategy use the {@link IStrategy} interface. + * + * @author Andras Szabolcs Nagy + * + */ +public final class Strategies { + + private Strategies() { + } + + /** + * Creates a depth-first search exploration strategy without a depth limit. + * + * @return The strategy. + * @see DepthFirstStrategy + */ + public static DepthFirstStrategy createDfsStrategy() { + return new DepthFirstStrategy(); + } + + /** + * Creates a depth-first search exploration strategy with a depth limit. A negative depth limit means no + * depth limit, zero means that it will check the initial state. + * + * @param depthLimit + * @return The strategy. + * @see DepthFirstStrategy + */ + public static DepthFirstStrategy createDfsStrategy(int depthLimit) { + return new DepthFirstStrategy(depthLimit); + } + + /** + * Creates a fixed priority exploration strategy without a depth limit. It is a depth-first search exploration + * strategy but from a current state it only explores the activations with the highest priority. Priorities can be + * defined on the strategy itself. + * + * @return The strategy. + * @see FixedPriorityStrategy + */ + public static FixedPriorityStrategy createFixedPriorityStrategy() { + return createFixedPriorityStrategy(-1); + } + + /** + * Creates a fixed priority exploration strategy with a depth limit, where a zero or negative depth limit means no + * depth limit. It is a depth-first search exploration strategy but from a current state it only explores the + * activations with the highest priority. Priorities can be defined on the strategy itself. + * + * @param depthLimit + * @return The strategy. + * @see FixedPriorityStrategy + */ + public static FixedPriorityStrategy createFixedPriorityStrategy(int depthLimit) { + return new FixedPriorityStrategy().withDepthLimit(depthLimit); + } + + /** + * Creates a breadth-first search exploration strategy without a depth limit. + * + * @return The strategy. + * @see BreadthFirstStrategy + */ + public static BreadthFirstStrategy createBfsStrategy() { + return new BreadthFirstStrategy(); + } + + /** + * Creates a breadth-first search exploration strategy with a depth limit. A zero or negative depth limit means no + * depth limit. + * + * @param depthLimit + * @return The strategy. + * @see BreadthFirstStrategy + */ + public static BreadthFirstStrategy createBfsStrategy(int depthLimit) { + return new BreadthFirstStrategy(depthLimit); + } + + /** + * Creates a hill climbing exploration strategy. By default, it explores all neighborhood states and chooses the + * best one to continue with until all neighborhood states are dominated by the current state. Other options are + * available on the strategy. + * + * @return The strategy. + * @see HillClimbingStrategy + */ + public static HillClimbingStrategy creatHillClimbingStrategy() { + return new HillClimbingStrategy(); + } + + /** + * See {@link BestFirstStrategy}. + */ + public static BestFirstStrategy createBestFirstStrategy() { + return new BestFirstStrategy(); + } + + /** + * See {@link BestFirstStrategy}. + */ + public static BestFirstStrategy createBestFirstStrategy(int depthLimit) { + return new BestFirstStrategy(depthLimit); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/BestFirstStrategy.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/BestFirstStrategy.java new file mode 100644 index 00000000..fe5604a1 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/BestFirstStrategy.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api.strategy.impl; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.PriorityQueue; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.Fitness; +import org.eclipse.viatra.dse.objectives.ObjectiveComparatorHelper; +import org.eclipse.viatra.dse.solutionstore.SolutionStore; + +/** + * This exploration strategy eventually explorers the whole design space but goes in the most promising directions + * first, based on the {@link Fitness}. + * + * There are a few parameter to tune such as + *
    + *
  • maximum depth
  • + *
  • continue the exploration from a state that satisfies the hard objectives (the default that it will + * backtrack),
  • + *
  • whether to continue the exploration from the newly explored state if it is at least equally good than the + * previous one or only if it is better (default is "at least equally good").
  • + *
+ * + * @author Andras Szabolcs Nagy + * + */ +public class BestFirstStrategy implements IStrategy { + + private ThreadContext context; + private SolutionStore solutionStore; + + private int maxDepth; + private boolean isInterrupted = false; + private boolean backTrackIfSolution = true; + private boolean onlyBetterFirst = false; + + private PriorityQueue trajectoiresToExplore; + private Logger logger = Logger.getLogger(IStrategy.class); + + private static class TrajectoryWithFitness { + + public Object[] trajectory; + public Fitness fitness; + + public TrajectoryWithFitness(Object[] trajectory, Fitness fitness) { + super(); + this.trajectory = trajectory; + this.fitness = fitness; + } + + @Override + public String toString() { + return Arrays.toString(trajectory) + fitness.toString(); + } + + } + + /** + * Creates a new best-first search algorithm without depth limit. + */ + public BestFirstStrategy() { + this(-1); + } + + /** + * Creates a new best-first search algorithm with depth limit. + * + * @param maxDepth + * A negative maxDepth means no depth limit, zero means the checking of the initial state. + */ + public BestFirstStrategy(int maxDepth) { + if (maxDepth < 0) { + this.maxDepth = Integer.MAX_VALUE; + } else { + this.maxDepth = maxDepth; + } + } + + public BestFirstStrategy continueIfHardObjectivesFulfilled() { + backTrackIfSolution = false; + return this; + } + + public BestFirstStrategy goOnOnlyIfFitnessIsBetter() { + onlyBetterFirst = true; + return this; + } + + @Override + public void initStrategy(ThreadContext context) { + this.context = context; + this.solutionStore = context.getGlobalContext().getSolutionStore(); + final ObjectiveComparatorHelper objectiveComparatorHelper = context.getObjectiveComparatorHelper(); + + trajectoiresToExplore = new PriorityQueue(11, + (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness)); + } + + @Override + public void explore() { + final ObjectiveComparatorHelper objectiveComparatorHelper = context.getObjectiveComparatorHelper(); + + boolean globalConstraintsAreSatisfied = context.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + logger.info("Global contraint is not satisifed in the first state. Terminate."); + return; + } + + final Fitness firstFittness = context.calculateFitness(); + if (firstFittness.isSatisifiesHardObjectives()) { + context.newSolution(); + logger.info("First state is a solution. Terminate."); + return; + } + + if (maxDepth == 0) { + return; + } + + final Object[] firstTrajectory = context.getTrajectory().toArray(new Object[0]); + TrajectoryWithFitness currentTrajectoryWithFittness = new TrajectoryWithFitness(firstTrajectory, firstFittness); + trajectoiresToExplore.add(currentTrajectoryWithFittness); + + mainLoop: while (!isInterrupted) { + + if (currentTrajectoryWithFittness == null) { + if (trajectoiresToExplore.isEmpty()) { + logger.debug("State space is fully traversed."); + return; + } else { + currentTrajectoryWithFittness = trajectoiresToExplore.element(); + if (logger.isDebugEnabled()) { + logger.debug("New trajectory is chosen: " + currentTrajectoryWithFittness); + } + context.getDesignSpaceManager().executeTrajectoryWithMinimalBacktrackWithoutStateCoding(currentTrajectoryWithFittness.trajectory); + } + } + + Collection activationIds = context.getUntraversedActivationIds(); + Iterator iterator = activationIds.iterator(); + + while (!isInterrupted && iterator.hasNext()) { + final Object nextActivation = iterator.next(); + if (!iterator.hasNext()) { + logger.debug("Last untraversed activation of the state."); + trajectoiresToExplore.remove(currentTrajectoryWithFittness); + } + + if (logger.isDebugEnabled()) { + logger.debug("Executing new activation: " + nextActivation); + } + context.executeAcitvationId(nextActivation); + if (context.isCurrentStateAlreadyTraversed()) { + logger.info("The new state is already visited."); + context.backtrack(); + } else if (!context.checkGlobalConstraints()) { + logger.debug("Global contraint is not satisifed."); + context.backtrack(); + } else { + final Fitness nextFitness = context.calculateFitness(); + if (nextFitness.isSatisifiesHardObjectives()) { + solutionStore.newSolution(context); + logger.debug("Found a solution."); + if (backTrackIfSolution) { + context.backtrack(); + continue; + } + } + if (context.getDepth() >= maxDepth) { + logger.debug("Reached max depth."); + context.backtrack(); + continue; + } + + TrajectoryWithFitness nextTrajectoryWithFittness = new TrajectoryWithFitness( + context.getTrajectory().toArray(), nextFitness); + trajectoiresToExplore.add(nextTrajectoryWithFittness); + + int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFittness.fitness, + nextTrajectoryWithFittness.fitness); + if (compare < 0) { + logger.debug("Better fitness, moving on: " + nextFitness); + currentTrajectoryWithFittness = nextTrajectoryWithFittness; + continue mainLoop; + } else if (compare == 0) { + if (onlyBetterFirst) { + logger.debug("Equally good fitness, backtrack: " + nextFitness); + context.backtrack(); + continue; + } else { + logger.debug("Equally good fitness, moving on: " + nextFitness); + currentTrajectoryWithFittness = nextTrajectoryWithFittness; + continue mainLoop; + } + } else { + logger.debug("Worse fitness."); + currentTrajectoryWithFittness = null; + continue mainLoop; + } + } + } + + logger.debug("State is fully traversed."); + trajectoiresToExplore.remove(currentTrajectoryWithFittness); + currentTrajectoryWithFittness = null; + + } + logger.info("Interrupted."); + } + + @Override + public void interruptStrategy() { + isInterrupted = true; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/BreadthFirstStrategy.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/BreadthFirstStrategy.java new file mode 100644 index 00000000..6b7d9817 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/BreadthFirstStrategy.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api.strategy.impl; + +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.base.GlobalContext; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.Fitness; +import org.eclipse.viatra.dse.solutionstore.SolutionStore; + +/** + * A breadth-first search algorithm implementation, that + *
    + *
  • can work with multiple threads,
  • + *
  • indeterministic,
  • + *
  • saves all states (trajectories) as solutions that fulfill all the hard objectives,
  • + *
  • can have a depth limit,
  • + *
  • will backtrack when a model satisfies the hard objectives (after saving it as a solution) and will not explore + * beyond that state.
  • + *
+ * + * @author Andras Szabolcs Nagy + * + */ +public class BreadthFirstStrategy implements IStrategy { + + private static final class BfsSharedObject { + private final ConcurrentLinkedQueue trajectoryQueue1 = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue trajectoryQueue2 = new ConcurrentLinkedQueue<>(); + + private final AtomicBoolean pushToQueue1 = new AtomicBoolean(false); + private final AtomicBoolean designSpaceTraversed = new AtomicBoolean(false); + + public final CyclicBarrier barrier; + + public BfsSharedObject(int numberOfThreads) { + barrier = new CyclicBarrier(numberOfThreads, () -> { + boolean oldValue = pushToQueue1.get(); + pushToQueue1.set(!oldValue); + if (trajectoryQueue1.isEmpty() && trajectoryQueue2.isEmpty()) { + designSpaceTraversed.set(true); + } + }); + } + + public Object[] poll() { + if (pushToQueue1.get()) { + return trajectoryQueue2.poll(); + } else { + return trajectoryQueue1.poll(); + } + } + + public void push(Object[] trajectory) { + if (pushToQueue1.get()) { + trajectoryQueue1.add(trajectory); + } else { + trajectoryQueue2.add(trajectory); + } + } + + public boolean isDesignSpaceTraversed() { + return designSpaceTraversed.get(); + } + } + + private int maxDepth = 0; + private BfsSharedObject shared; + private boolean isInterrupted = false; + private ThreadContext context; + private Logger logger = Logger.getLogger(IStrategy.class); + private SolutionStore solutionStore; + private boolean isFirstThread = false; + + /** + * Creates a new breadth-first search algorithm without depth limit. + */ + public BreadthFirstStrategy() { + this.maxDepth = Integer.MAX_VALUE; + } + + /** + * Creates a new breadth-first search algorithm with depth limit. + * + * @param maxDepth + * A negative maxDepth means no depth limit, zero means the checking of the initial state. + */ + public BreadthFirstStrategy(int maxDepth) { + if (maxDepth < 0) { + this.maxDepth = Integer.MAX_VALUE; + } else { + this.maxDepth = maxDepth; + } + } + + @Override + public void initStrategy(ThreadContext context) { + this.context = context; + this.solutionStore = context.getGlobalContext().getSolutionStore(); + + GlobalContext globalContext = context.getGlobalContext(); + if (globalContext.getSharedObject() == null) { + isFirstThread = true; + shared = new BfsSharedObject(globalContext.getThreadPool().getMaximumPoolSize()); + globalContext.setSharedObject(shared); + logger.info("Breadth-first exploration strategy is inited."); + } else { + shared = (BfsSharedObject) globalContext.getSharedObject(); + } + } + + @Override + public void explore() { + + if (isFirstThread) { + + boolean globalConstraintsAreSatisfied = context.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + logger.info("Global contraint is not satisifed in the first state. Terminate."); + return; + } + + Fitness fitness = context.calculateFitness(); + if (fitness.isSatisifiesHardObjectives()) { + context.newSolution(); + logger.info("First state is a solution. Terminate."); + return; + } + + Object[] currentTrajectory = context.getTrajectory().toArray(new Object[0]); + + shared.push(currentTrajectory); + + startThreads(); + } else { + try { + shared.barrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + } + } + + mainLoop: while (!isInterrupted && !shared.isDesignSpaceTraversed()) { + + Object[] next = shared.poll(); + while (next == null) { + try { + logger.debug("Reached barrier."); + shared.barrier.await(); + } catch (InterruptedException | BrokenBarrierException e1) { + } + if (isInterrupted || shared.isDesignSpaceTraversed()) { + break mainLoop; + } + next = shared.poll(); + } + + context.backtrackUntilRoot(); + + context.executeTrajectory(next); + + Collection activationIds = context.getCurrentActivationIds(); + int i = activationIds.size() - 1; + + while (!isInterrupted && i >= 0) { + + Iterator iterator = activationIds.iterator(); + int index = i--; + while (iterator.hasNext() && index > 0) { + index--; + iterator.next(); + } + Object activationIdToTry = iterator.next(); + + context.executeAcitvationId(activationIdToTry); + + if (context.isCurrentStateAlreadyTraversed()) { + logger.info("The new state is already visited."); + } else if (!context.checkGlobalConstraints()) { + logger.debug("Global contraint is not satisifed."); + } else if (context.calculateFitness().isSatisifiesHardObjectives()) { + solutionStore.newSolution(context); + logger.debug("Found a solution."); + } else if (context.getDepth() >= maxDepth) { + logger.debug("Reached max depth."); + } else { + Object[] currentTrajectory = context.getTrajectory().toArray(new Object[0]); + shared.push(currentTrajectory); + } + + context.backtrack(); + } + + } + } + + private void startThreads() { + context.startAllThreads(() -> new BreadthFirstStrategy(maxDepth)); + } + + @Override + public void interruptStrategy() { + isInterrupted = true; + shared.barrier.reset(); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/DepthFirstStrategy.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/DepthFirstStrategy.java new file mode 100644 index 00000000..22a4a683 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/DepthFirstStrategy.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api.strategy.impl; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.Fitness; + +/** + * A depth-first search algorithm implementation, that + *
    + *
  • can work with multiple threads,
  • + *
  • randomly traverses the search space,
  • + *
  • saves all states (trajectories) as solutions that fulfill all the hard objectives,
  • + *
  • can have a depth limit,
  • + *
  • will backtrack when a model satisfies the hard objectives (after saving it as a solution), which can be modified + * by calling {@link #continueIfHardObjectivesFulfilled()}
  • + *
+ * + * @author Andras Szabolcs Nagy + * + */ +public class DepthFirstStrategy implements IStrategy { + + private int maxDepth; + private AtomicBoolean isInterrupted = new AtomicBoolean(false); + private ThreadContext context; + + private Logger logger = Logger.getLogger(IStrategy.class); + + private Random random = new Random(); + private boolean backTrackIfSolution = true; + + /** + * Creates a new depth-first search algorithm without depth limit. + */ + public DepthFirstStrategy() { + this.maxDepth = Integer.MAX_VALUE; + } + + /** + * Creates a new depth-first search algorithm with depth limit. + * + * @param maxDepth + * A negative maxDepth means no depth limit, zero means the checking of the initial state. + */ + public DepthFirstStrategy(int maxDepth) { + if (maxDepth < 0) { + this.maxDepth = Integer.MAX_VALUE; + } else { + this.maxDepth = maxDepth; + } + } + + /** + * If called, the algorithm will not backtrack after the hard objectives are fulfilled, instead it goes deeper in + * the search space. + */ + public DepthFirstStrategy continueIfHardObjectivesFulfilled() { + backTrackIfSolution = false; + return this; + } + + @Override + public void initStrategy(ThreadContext context) { + this.context = context; + + if (context.getSharedObject() == null) { + context.setSharedObject(new Object()); + logger.info("Depth-first exploration strategy is initied."); + startThreads(); + } + + } + + private void startThreads() { + context.startAllThreads(() -> new DepthFirstStrategy(maxDepth)); + } + + @Override + public void explore() { + + mainloop: do { + + boolean globalConstraintsAreSatisfied = context.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Global contraint is not satisifed and cannot backtrack."); + break; + } else { + logger.debug("Global contraint is not satisifed, backtrack."); + continue; + } + } + + Fitness fitness = context.calculateFitness(); + if (fitness.isSatisifiesHardObjectives()) { + context.newSolution(); + if (backTrackIfSolution) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Found a solution but cannot backtrack."); + break; + } else { + logger.debug("Found a solution, backtrack."); + continue; + } + } + } + + if (context.getDepth() >= maxDepth) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Reached max depth but cannot bactrack."); + break; + } else { + logger.debug("Reached max depth, bactrack."); + continue; + } + } + + if (isInterrupted.get()) { + logger.info("Interrupted, stop exploration."); + break; + } + + Object activationId = null; + Collection activationIds; + + do { + activationIds = context.getUntraversedActivationIds(); + if (activationIds.isEmpty()) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("No more transitions from current state and cannot backtrack."); + break mainloop; + } else { + logger.debug("No more transitions from current state, backtrack."); + continue; + } + } + } while (activationIds.isEmpty()); + + int index = random.nextInt(activationIds.size()); + + Iterator iterator = activationIds.iterator(); + while (index-- > 0) { + iterator.next(); + } + activationId = iterator.next(); + + context.executeAcitvationId(activationId); + + boolean loopInTrajectory = context.isCurrentStateInTrajectory(); + if (loopInTrajectory) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + throw new DSEException("The new state is present in the trajectoy but cannot bactkrack. Should never happen!"); + } else { + logger.info("The new state is already visited in the trajectory, backtrack."); + } + } + + } while (true); + + logger.info("Terminated."); + } + + @Override + public void interruptStrategy() { + isInterrupted.set(true); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/FixedPriorityStrategy.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/FixedPriorityStrategy.java new file mode 100644 index 00000000..4ccda4ce --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/FixedPriorityStrategy.java @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api.strategy.impl; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.Fitness; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +import com.google.common.collect.Lists; + +/** + * Works as {@link DepthFirstStrategy} but: + *
    + *
  • works only with single thread,
  • + *
  • in a given state, it only traverses the activations with locally the highest priority.
  • + *
+ * + * @author Andras Szabolcs Nagy + * + */ +public class FixedPriorityStrategy implements IStrategy { + + private int maxDepth = Integer.MAX_VALUE; + private AtomicBoolean isInterrupted = new AtomicBoolean(false); + private ThreadContext context; + + private Logger logger = Logger.getLogger(IStrategy.class); + private Map, Integer> priorities = new HashMap, Integer>(); + + private Random random = new Random(); + private Map> bestPriorityInState = new HashMap<>(); + + /** + * Adds a depth limit to the strategy. + * + * @param depthLimit + * The depth limit. + * @return The actual instance to enable a builder pattern like usage. + */ + public FixedPriorityStrategy withDepthLimit(int maxDepth) { + if (maxDepth < 0) { + this.maxDepth = Integer.MAX_VALUE; + } else { + this.maxDepth = maxDepth; + } + return this; + } + + /** + * Assigns a priority to a rule. Unassigned rule will have a priority of 0. + * + * @param rule + * The transformation rule. + * @param priority + * The priority of the rule. Higher is better. + * @return The actual instance to enable a builder pattern like usage. + */ + public FixedPriorityStrategy withRulePriority(BatchTransformationRule rule, int priority) { + priorities.put(rule, priority); + return this; + } + + public Map, Integer> getPriorities() { + return priorities; + } + + @Override + public void initStrategy(ThreadContext context) { + this.context = context; + + logger.info("Fixed priority exploration strategy is initied."); + } + + @Override + public void explore() { + + mainloop: do { + + boolean globalConstraintsAreSatisfied = context.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Global contraint is not satisifed and cannot backtrack."); + break; + } else { + logger.debug("Global contraint is not satisifed, backtrack."); + continue; + } + } + + Fitness fitness = context.calculateFitness(); + if (fitness.isSatisifiesHardObjectives()) { + context.newSolution(); + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Found a solution but cannot backtrack."); + break; + } else { + logger.debug("Found a solution, backtrack."); + continue; + } + } + + if (context.getDepth() >= maxDepth) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Reached max depth but cannot bactrack."); + break; + } else { + logger.debug("Reached max depth, bactrack."); + continue; + } + } + + if (isInterrupted.get()) { + logger.info("Interrupted, stop exploration."); + break; + } + + List transitions; + + do { + + transitions = bestPriorityInState.get(context.getCurrentStateId()); + + if (transitions == null) { + Integer bestPriority = getBestPriority(context.getCurrentActivationIds()); + transitions = Lists.newArrayList(); + for (Object iTransition : context.getCurrentActivationIds()) { + Integer integer = priorities.get(context.getRuleByActivationId(iTransition)); + if (integer == null) { + integer = Integer.valueOf(0); + } + if (integer.equals(bestPriority)) { + transitions.add(iTransition); + } + } + bestPriorityInState.put(context.getCurrentStateId(), transitions); + } + + if (transitions.isEmpty()) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("No more transitions from current state and cannot backtrack."); + break mainloop; + } else { + logger.debug("No more transitions from current state, backtrack."); + continue; + } + } + } while (transitions.isEmpty()); + + int index = random.nextInt(transitions.size()); + Object transition = transitions.remove(index); + + context.executeAcitvationId(transition); + + boolean loopInTrajectory = context.isCurrentStateInTrajectory(); + if (loopInTrajectory) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + throw new DSEException( + "The new state is present in the trajectoy but cannot bactkrack. Should never happen!"); + } else { + logger.info("The new state is already visited in the trajectory, backtrack."); + } + } + + } while (true); + + logger.info("Terminated."); + } + + @Override + public void interruptStrategy() { + isInterrupted.set(true); + } + + private Integer getBestPriority(Collection transitions) { + Integer bestPriority = Integer.MIN_VALUE; + for (Object iTransition : transitions) { + Integer priority = priorities.get(context.getRuleByActivationId(iTransition)); + if (priority == null) { + priority = Integer.valueOf(0); + } + if (priority > bestPriority) { + bestPriority = priority; + } + } + return bestPriority; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/HillClimbingStrategy.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/HillClimbingStrategy.java new file mode 100644 index 00000000..0ccb0668 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/HillClimbingStrategy.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api.strategy.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.Fitness; +import org.eclipse.viatra.dse.objectives.ObjectiveComparatorHelper; +import org.eclipse.viatra.dse.objectives.TrajectoryFitness; + +public class HillClimbingStrategy implements IStrategy { + + private AtomicBoolean isInterrupted = new AtomicBoolean(false); + private ThreadContext context; + + private Logger logger = Logger.getLogger(IStrategy.class); + + private Random random = new Random(); + private double percentOfOpenedStates; + private ObjectiveComparatorHelper objectiveComparatorHelper; + + public HillClimbingStrategy() { + this(2); + } + + public HillClimbingStrategy(double percentOfOpenedStates) { + this.percentOfOpenedStates = percentOfOpenedStates; + } + + @Override + public void initStrategy(ThreadContext context) { + this.context = context; + objectiveComparatorHelper = context.getObjectiveComparatorHelper(); + logger.info("Hill climbing exploration strategy is initied."); + } + + @Override + public void explore() { + + boolean globalConstraintsAreSatisfied = context.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Global contraint is not satisifed and cannot backtrack."); + return; + } + } + + mainloop: do { + + Fitness previousFitness = context.calculateFitness(); + + logger.debug("Current depth: " + context.getDepth() + " Fitness: " + previousFitness); + + Collection transitionsFromCurrentState = context.getCurrentActivationIds(); + + while (transitionsFromCurrentState.isEmpty()) { + logger.debug("No transitions from current state: considered as a solution."); + saveSolutionAndBacktrack(); + continue mainloop; + } + + ArrayList transitionsToTry = new ArrayList<>(transitionsFromCurrentState.size()); + for (Object transition : transitionsFromCurrentState) { + transitionsToTry.add(transition); + } + double numberOfTransitionsToTry = transitionsToTry.size() * percentOfOpenedStates; + + for (; numberOfTransitionsToTry > 0 && !transitionsToTry.isEmpty(); numberOfTransitionsToTry--) { + int index = random.nextInt(transitionsToTry.size()); + Object transition = transitionsToTry.remove(index); + + context.executeAcitvationId(transition); + + if (!context.checkGlobalConstraints()) { + logger.debug("Global contraint is not satisifed, backtrack."); + context.backtrack(); + continue; + } + if (context.isCurrentStateInTrajectory()) { + logger.debug("Current state is in trajectory, backtrack."); + context.backtrack(); + continue; + } + + Fitness fitness = context.calculateFitness(); + objectiveComparatorHelper.addTrajectoryFitness( + new TrajectoryFitness(context.getTrajectoryInfo().getLastActivationId(), fitness)); + context.backtrack(); + } + + if (objectiveComparatorHelper.getTrajectoryFitnesses().isEmpty()) { + logger.debug("No viable transitions from current state: considered as a solution."); + saveSolutionAndBacktrack(); + continue; + } + + TrajectoryFitness randomBestFitness = objectiveComparatorHelper.getRandomBest(); + objectiveComparatorHelper.clearTrajectoryFitnesses(); + + int compare = objectiveComparatorHelper.compare(previousFitness, randomBestFitness.fitness); + + if (compare > 0) { + saveSolutionAndBacktrack(); + continue; + } else { + previousFitness = randomBestFitness.fitness; + Object transition = randomBestFitness.trajectory[randomBestFitness.trajectory.length - 1]; + context.executeAcitvationId(transition); + } + + } while (!isInterrupted.get()); + + logger.info("Terminated."); + } + + private void saveSolutionAndBacktrack() { + context.calculateFitness(); + context.newSolution(); + logger.debug("Found solution: " + context.getTrajectoryInfo().toString()); + logger.debug("Backtrack for more solutions, if needed."); + context.backtrackUntilRoot(); + } + + @Override + public void interruptStrategy() { + isInterrupted.set(true); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/RandomSearchStrategy.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/RandomSearchStrategy.java new file mode 100644 index 00000000..af8fb8cc --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/impl/RandomSearchStrategy.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api.strategy.impl; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.base.GlobalContext; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.designspace.api.TrajectoryInfo; +import org.eclipse.viatra.dse.objectives.Fitness; + +public class RandomSearchStrategy implements IStrategy { + + private static class SharedData { + public final AtomicInteger triesLeft; + public final int minDepth; + public final int maxDepth; + + public SharedData(int minDepth, int maxDepth, int numberOfTries) { + this.minDepth = minDepth; + this.maxDepth = maxDepth; + this.triesLeft = new AtomicInteger(numberOfTries); + } + } + + private int maxDepth = -1; + private Random rnd = new Random(); + private SharedData shared; + private TrajectoryInfo trajectoryInfo; + int nth; + private ThreadContext context; + private AtomicBoolean isInterrupted = new AtomicBoolean(false); + private Logger logger = Logger.getLogger(IStrategy.class); + + public RandomSearchStrategy(int minDepth, int maxDepth, int numberOfTries) { + shared = new SharedData(minDepth, maxDepth, numberOfTries); + } + + private RandomSearchStrategy() { + } + + @Override + public void initStrategy(ThreadContext context) { + this.context = context; + trajectoryInfo = context.getTrajectoryInfo(); + GlobalContext gc = context.getGlobalContext(); + + Object sharedObject = gc.getSharedObject(); + if (sharedObject == null) { + gc.setSharedObject(shared); + logger.info("Random exploration strategy is initied."); + startThreads(); + } else { + shared = (SharedData) sharedObject; + } + + maxDepth = rnd.nextInt(shared.maxDepth - shared.minDepth) + shared.minDepth; + + } + + @Override + public void explore() { + + do { + + boolean globalConstraintsAreSatisfied = context.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Global contraint is not satisifed and cannot backtrack."); + break; + } else { + logger.debug("Global contraint is not satisifed, backtrack."); + continue; + } + } + + Fitness fitness = context.calculateFitness(); + if (fitness.isSatisifiesHardObjectives()) { + context.newSolution(); + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + logger.info("Found a solution but cannot backtrack."); + break; + } else { + logger.debug("Found a solution, backtrack."); + continue; + } + } + + if (trajectoryInfo.getDepth() < maxDepth) { + + Collection transitions = context.getCurrentActivationIds(); + int index = rnd.nextInt(transitions.size()); + Object transition = getByIndex(transitions, index); + context.executeAcitvationId(transition); + + } else { + + nth = shared.triesLeft.getAndDecrement(); + logger.debug(nth + " tries left"); + if (nth > 0) { + + context.backtrackUntilRoot(); + maxDepth = rnd.nextInt(shared.maxDepth - shared.minDepth) + shared.minDepth; + + } else { + break; + } + } + + boolean loopInTrajectory = context.isCurrentStateInTrajectory(); + if (loopInTrajectory) { + boolean isSuccessfulUndo = context.backtrack(); + if (!isSuccessfulUndo) { + throw new DSEException( + "The new state is present in the trajectoy but cannot bactkrack. Should never happen!"); + } else { + logger.info("The new state is already visited in the trajectory, backtrack."); + } + } + + } while (isInterrupted.get()); + + logger.info("Terminated."); + } + + @Override + public void interruptStrategy() { + isInterrupted.set(true); + } + + private void startThreads() { + context.startAllThreads(RandomSearchStrategy::new); + } + + private static Object getByIndex(Collection availableTransitions, int index) { + int i = 0; + Iterator iterator = availableTransitions.iterator(); + while (iterator.hasNext()) { + Object transition = iterator.next(); + if (i == index) { + return transition; + } else { + ++i; + } + } + throw new IndexOutOfBoundsException("size: " + availableTransitions.size() + ", index: " + index); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/interfaces/IStrategy.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/interfaces/IStrategy.java new file mode 100644 index 00000000..8c164396 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/interfaces/IStrategy.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api.strategy.interfaces; + +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.solutionstore.SolutionStore; + +/** + * This high level interface is responsible for defining basic operations of an exploration strategy. + * + * @author Andras Szabolcs Nagy + * + */ +public interface IStrategy { + + /** + * Initializes the strategy with a specific {@link ThreadContext}. + * + * @param context + * The context. + */ + void initStrategy(ThreadContext context); + + /** + * This method explores the design space as the implementation specifies. It will be called only once, hence the + * exploration loop is run by the implementation. The termination condition is also specified by the implementation + * and when it returns the exploration thread will be disposed. + */ + void explore(); + + /** + * The implementation of this interface should be ready to be interrupted. If this method is called, the + * {@link IStrategy#explore()} method should return ASAP. + * + * This method is also called by the {@link SolutionStore} class if enough solutions are found. + */ + void interruptStrategy(); +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/interfaces/IStrategyFactory.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/interfaces/IStrategyFactory.java new file mode 100644 index 00000000..b3352d13 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/api/strategy/interfaces/IStrategyFactory.java @@ -0,0 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.api.strategy.interfaces; + +public interface IStrategyFactory { + IStrategy createStrategy(); +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ActivationCodesConflictSet.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ActivationCodesConflictSet.java new file mode 100644 index 00000000..d3990c23 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ActivationCodesConflictSet.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; +import org.eclipse.viatra.transformation.evm.api.Activation; +import org.eclipse.viatra.transformation.evm.api.resolver.ChangeableConflictSet; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictResolver; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictSet; + +public class ActivationCodesConflictSet implements ChangeableConflictSet { + + private static class ActivationCodesMultiBiMap { + public Map, Object> activationsToCodes = new HashMap<>(); + public Map>> codesToActivations = new HashMap<>(); + + public void addActivation(Activation activation, Object activationCode) { + activationsToCodes.put(activation, activationCode); + codesToActivations.computeIfAbsent(activationCode, k -> new HashSet<>()).add(activation); + } + + public void removeActivaion(Activation activation) { + Object activationCode = activationsToCodes.remove(activation); + Set> activations = codesToActivations.get(activationCode); + if (activations != null) { + activations.remove(activation); + } + } + + public void clear() { + activationsToCodes.clear(); + codesToActivations.clear(); + } + } + + protected ActivationCodesMultiBiMap activationCodes; + protected IStateCoder stateCoder; + + protected Set> newActivations = new HashSet<>(); + protected Set> removedActivations = new HashSet<>(); +// private Logger logger = Logger.getLogger(getClass()); + + private boolean isIncremental = false; + private ConflictSet nextActivationsConflictSet; + + public void setIncremental(boolean isIncremental) { + this.isIncremental = isIncremental; + } + + public ActivationCodesConflictSet(ConflictSet nextActivationsConflictSet, IStateCoder stateCoder) { + Objects.requireNonNull(nextActivationsConflictSet); + this.nextActivationsConflictSet = nextActivationsConflictSet; + this.stateCoder = stateCoder; + activationCodes = new ActivationCodesMultiBiMap(); + } + + private Object createActivationCode(Activation activation) { + return stateCoder.createActivationCode((IPatternMatch) activation.getAtom()); + } + + @Override + public boolean removeActivation(Activation activation) { + if (isIncremental) { +//* + removedActivations.add(activation); + newActivations.remove(activation); +/*/ + if(!removedActivations.add(activation)) { + logger.debug("Abnormal: already marked to remove: " + activation); + } else { + logger.debug("marked to remove: " + activation); + } + if(newActivations.remove(activation)) { + logger.debug("Abnormal: removed from new activations: " + activation); + } +//*/ + } + return false; + } + + @Override + public boolean addActivation(Activation activation) { + if (isIncremental) { +//* + newActivations.add(activation); + removedActivations.remove(activation); + /*/ + if (activation.isEnabled()) { + if (!newActivations.add(activation)) { + logger.debug("Abnormal: already added as new: " + activation); + } else { + logger.debug("activation added: " + activation); + } + } + if(removedActivations.remove(activation)) { + logger.debug("Abnormal: was already marked to remove: " + activation); + } +//*/ + } + return false; + } + + public Object getActivationId(Activation activation) { + return activationCodes.activationsToCodes.get(activation); + } + + public Activation getActivation(Object activationId) { + Set> activationsSet = activationCodes.codesToActivations.get(activationId); + if (activationsSet == null || activationsSet.isEmpty()) { + return null; + } else { + return activationsSet.iterator().next(); + } + } + + public void updateActivationCodes() { +// logger.debug("Updating activation codes."); + + if (isIncremental) { + for (Activation activation : removedActivations) { + activationCodes.removeActivaion(activation); +// logger.debug("removed activation: " + activationId); + } + + for (Activation activation : newActivations) { + if (activation.getState().isInactive()) { + continue; + } + Object activationCode = createActivationCode(activation); + activationCodes.addActivation(activation, activationCode); +// logger.debug("new activation: " + activationId); +// Activation similarActivation = activationIds.inverse().get(activationId); +// if (similarActivation != null) { +// logger.debug("Activation " + toStringAct(activation) + " is already present with id: " + activationId); +// if (similarActivation.isEnabled()) { +// logger.warn("Duplicate activation code: " + activationId); +// } else { +// logger.debug("Force put: " + activationId); +// } +// continue; +// } +// activationIds.put(activation, activationId); + } + removedActivations.clear(); + newActivations.clear(); + } else { + activationCodes.clear(); + for (Activation activation : nextActivationsConflictSet.getNextActivations()) { + Object activationCode = createActivationCode(activation); + activationCodes.addActivation(activation, activationCode); + } + } + + + } + + protected void reinitWithActivations(ConflictSet nextActivationsConflictSet) { + this.nextActivationsConflictSet = nextActivationsConflictSet; + activationCodes.clear(); + for (Activation activation : nextActivationsConflictSet.getNextActivations()) { + Object activationCode = createActivationCode(activation); + activationCodes.addActivation(activation, activationCode); + } + } + + @Override + public Activation getNextActivation() { + throw new UnsupportedOperationException(); + } + + @Override + public Set> getNextActivations() { + throw new UnsupportedOperationException(); + } + + @Override + public Set> getConflictingActivations() { + throw new UnsupportedOperationException(); + } + + @Override + public ConflictResolver getConflictResolver() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Object activationCode : activationCodes.activationsToCodes.values()) { + sb.append(activationCode); + sb.append(" | "); + } + return sb.toString(); + } + + public Collection getCurrentActivationCodes() { + return activationCodes.activationsToCodes.values(); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DesignSpaceManager.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DesignSpaceManager.java new file mode 100644 index 00000000..4c6b4097 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DesignSpaceManager.java @@ -0,0 +1,563 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.edit.command.ChangeCommand; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.api.SolutionTrajectory; +import org.eclipse.viatra.dse.designspace.api.IBacktrackListener; +import org.eclipse.viatra.dse.designspace.api.IDesignSpace; +import org.eclipse.viatra.dse.designspace.api.TrajectoryInfo; +import org.eclipse.viatra.dse.objectives.ActivationFitnessProcessor; +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.dse.visualizer.IExploreEventHandler; +import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; +import org.eclipse.viatra.transformation.evm.api.Activation; +import org.eclipse.viatra.transformation.evm.api.Context; +import org.eclipse.viatra.transformation.evm.api.resolver.ChangeableConflictSet; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +public class DesignSpaceManager implements IBacktrackListener { + + private final IStateCoder stateCoder; + private final EditingDomain domain; + private Notifier model; + + private IDesignSpace designSpace; + + private final TrajectoryInfo trajectory; + + // the occurence vector callback + private List handlers; + + // Dummy context for evm + private final Context evmContext = Context.create(); + + private Logger logger = Logger.getLogger(this.getClass()); + + private boolean isNewState = false; + private Map, ActivationFitnessProcessor> activationFitnessProcessors; + private Map, String> activationFitnessProcessorNames; + private ThreadContext context; + + private ActivationCodesConflictSet activationCodes; + private ChangeableConflictSet conflictSet; + + private AdvancedViatraQueryEngine engine; + + private Random random = new Random(); + + private long forwardTime = 0; + private long backtrackingTime = 0; + + public DesignSpaceManager(ThreadContext context) { + + this.context = context; + model = context.getModel(); + designSpace = context.getGlobalContext().getDesignSpace(); + domain = context.getEditingDomain(); + + conflictSet = context.getConflictResolver().getLastCreatedConflictSet(); + + stateCoder = context.getStateCoder(); + Object initialStateId = stateCoder.createStateCode(); + designSpace.addState(null, null, initialStateId); + + activationCodes = context.getActivationCodesConflictSet(); + + engine = (AdvancedViatraQueryEngine) context.getQueryEngine(); + + this.trajectory = new TrajectoryInfo(initialStateId); + + logger.debug("DesignSpaceManager initialized with initial model: " + initialStateId); + } + + public void fireActivation(final Object transition) { + if (fireActivationSilent(transition)) { + return; + } + + StringBuilder sb = new StringBuilder(); + sb.append( + "A retrieved Transition SHOULD have a matching Activation. Possible causes: the state serializer is faulty; the algorithm choosed a wrong Transition."); + sb.append("\nSought transition: "); + sb.append(transition); + Object currentStateId = getCurrentState(); + sb.append("\nCurrent known state: "); + sb.append(currentStateId); + Object actualStateId = stateCoder.createStateCode(); + sb.append("\nActual state: "); + sb.append((actualStateId.equals(currentStateId) ? "same as current" : actualStateId)); + sb.append("\n"); + sb.append(trajectory); + sb.append("\nAvailable transitions:"); + for (Activation act : conflictSet.getNextActivations()) { + IPatternMatch match = (IPatternMatch) act.getAtom(); + Object code = generateMatchCode(match); + sb.append("\n\t"); + sb.append(code); + } + + throw new DSEException(sb.toString()); + } + + public boolean tryFireActivation(final Object transition) { + return fireActivationSilent(transition); + } + + private boolean fireActivationSilent(final Object transition) { + final Activation activation = getActivationById(transition); + + if (activation == null) { + return false; + } + + BatchTransformationRule rule = getRuleByActivation(activation); + + Map measureCosts = new HashMap(); + if (activationFitnessProcessors != null && activationFitnessProcessors.containsKey(rule)) { + IPatternMatch match = (IPatternMatch) activation.getAtom(); + ActivationFitnessProcessor processor = activationFitnessProcessors.get(rule); + double fitness = processor.process(match); + measureCosts.put(activationFitnessProcessorNames.get(rule), fitness); + } + + ChangeCommand rc = new ChangeCommand(model) { + @Override + protected void doExecute() { + activation.fire(evmContext); + } + }; + + Object previousState = trajectory.getCurrentStateId(); + + long start = System.nanoTime(); + domain.getCommandStack().execute(rc); + forwardTime += System.nanoTime() - start; + + Object newStateId = stateCoder.createStateCode(); + updateActivationCodes(); + + if (designSpace != null) { + isNewState = !designSpace.isTraversed(newStateId); + designSpace.addState(previousState, transition, newStateId); + } + + trajectory.addStep(transition, rule, newStateId, measureCosts); + + if (handlers != null) { + for (IExploreEventHandler iExploreEventHandler : handlers) { + iExploreEventHandler.transitionFired(transition); + } + } + + logger.debug("Executed activation: " + transition);/* + * + " from state: " + previousState + " to " + newStateId); + */ + + return true; + } + + public boolean executeRandomActivationId() { + Collection transitions = getTransitionsFromCurrentState(); + int size = transitions.size(); + if (size == 0) { + return false; + } + + int index = random.nextInt(size); + Iterator iterator = transitions.iterator(); + for (int i = 0; i < index; ++i) { + iterator.next(); + } + Object transition = iterator.next(); + + fireActivation(transition); + return true; + } + + public int executeTrajectory(Object[] trajectoryToExecute) { + return executeTrajectory(trajectoryToExecute, 0, trajectoryToExecute.length, false, true); + } + + public int executeTrajectory(Object[] trajectoryToExecute, int fromIncludedIndex, int toExcludedIndex) { + return executeTrajectory(trajectoryToExecute, fromIncludedIndex, toExcludedIndex, false, true); + } + + public int executeTrajectoryByTrying(Object[] trajectoryToExecute) { + return executeTrajectory(trajectoryToExecute, 0, trajectoryToExecute.length, true, true); + } + + public int executeTrajectoryByTrying(Object[] trajectoryToExecute, int fromIncludedIndex, int toExcludedIndex) { + return executeTrajectory(trajectoryToExecute, fromIncludedIndex, toExcludedIndex, true, true); + } + + public int executeTrajectoryWithoutStateCoding(Object[] trajectoryToExecute) { + return executeTrajectory(trajectoryToExecute, 0, trajectoryToExecute.length, false, false); + } + + public int executeTrajectoryWithoutStateCoding(Object[] trajectoryToExecute, int fromIncludedIndex, + int toExcludedIndex) { + return executeTrajectory(trajectoryToExecute, fromIncludedIndex, toExcludedIndex, false, false); + } + + public int executeTrajectoryByTryingWithoutStateCoding(Object[] trajectoryToExecute) { + return executeTrajectory(trajectoryToExecute, 0, trajectoryToExecute.length, true, false); + } + + public int executeTrajectoryByTryingWithoutStateCoding(Object[] trajectoryToExecute, int fromIncludedIndex, + int toExcludedIndex) { + return executeTrajectory(trajectoryToExecute, fromIncludedIndex, toExcludedIndex, true, false); + } + + private int executeTrajectory(Object[] trajectoryToExecute, int fromIncludedIndex, int toExcludedIndex, + boolean tryAllActivations, boolean createStateCode) { + logger.debug("Executing trajectory."); + int unsuccesfulIndex = -1; + if (tryAllActivations) { + unsuccesfulIndex = 0; + } + for (int i = fromIncludedIndex; i < toExcludedIndex; i++) { + Object activationId = trajectoryToExecute[i]; + final Activation activation = getActivationById(activationId); + + if (activation == null) { + logger.debug("Couldn't execute activation: " + activationId); + if (tryAllActivations) { + unsuccesfulIndex++; + continue; + } else { + unsuccesfulIndex = i; + logger.debug("Trajectory execution stopped at index " + i + "/" + trajectoryToExecute.length); + break; + } + } + + BatchTransformationRule rule = getRuleByActivation(activation); + + Map measureCosts = new HashMap(); + if (activationFitnessProcessors != null && activationFitnessProcessors.containsKey(rule)) { + IPatternMatch match = (IPatternMatch) activation.getAtom(); + ActivationFitnessProcessor processor = activationFitnessProcessors.get(rule); + double fitness = processor.process(match); + measureCosts.put(activationFitnessProcessorNames.get(rule), fitness); + } + + ChangeCommand rc = new ChangeCommand(model) { + @Override + protected void doExecute() { + activation.fire(evmContext); + } + }; + + long start = System.nanoTime(); + domain.getCommandStack().execute(rc); + forwardTime += System.nanoTime() - start; + + Object newStateId = null; + if (createStateCode) { + newStateId = stateCoder.createStateCode(); + } + updateActivationCodes(); + + trajectory.addStep(activationId, rule, newStateId, measureCosts); + + logger.debug("Activation executed: " + activationId); + } + if (!createStateCode) { + trajectory.modifyLastStateCode(stateCoder.createStateCode()); + } + logger.debug("Trajectory execution finished."); + return unsuccesfulIndex; + + } + + public Object getTransitionByActivation(Activation activation) { + return activationCodes.getActivationId(activation); + } + + public Activation getActivationById(Object activationId) { + return activationCodes.getActivation(activationId); + } + + public BatchTransformationRule getRuleByActivation(Activation activation) { + return context.getGlobalContext().getSpecificationRuleMap().get(activation.getInstance().getSpecification()); + } + + public BatchTransformationRule getRuleByActivationId(Object activationId) { + return getRuleByActivation(getActivationById(activationId)); + } + + /** + * Returns true if the given state is not owned by this crawler. + * + **/ + public boolean isNewModelStateAlreadyTraversed() { + return !isNewState; + } + + public List getTrajectoryFromRoot() { + return trajectory.getTrajectory(); + } + + public Collection getTransitionsFromCurrentState() { + return activationCodes.getCurrentActivationCodes(); + } + + public Collection getUntraversedTransitionsFromCurrentState() { + if (designSpace == null) { + throw new DSEException("Unsupported without a design space"); + } + Object currentState = trajectory.getCurrentStateId(); + Collection traversedIds = designSpace.getActivationIds(currentState); + + List untraversedTransitions = new ArrayList<>(); + for (Object activationId : activationCodes.getCurrentActivationCodes()) { + if (!traversedIds.contains(activationId)) { + untraversedTransitions.add(activationId); + } + } + + return untraversedTransitions; + } + + public boolean undoLastTransformation() { + + if (!trajectory.canStepBack()) { + return false; + } + + long start = System.nanoTime(); + try { + engine.delayUpdatePropagation(() -> { + domain.getCommandStack().undo(); + return null; + }); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + updateActivationCodes(); + + Object lastActivationId = trajectory.getLastActivationId(); + + trajectory.backtrack(); + + if (handlers != null) { + for (IExploreEventHandler iExploreEventHandler : handlers) { + iExploreEventHandler.undo(lastActivationId); + } + } + + logger.debug("Backtrack."); + backtrackingTime += System.nanoTime() - start; + + return true; + } + + public void backtrackXTimes(int steps) { + long start = System.nanoTime(); + try { + engine.delayUpdatePropagation(() -> { + for (int i = steps; i > 0; i--) { + domain.getCommandStack().undo(); + trajectory.backtrack(); + } + return null; + }); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + backtrackingTime += System.nanoTime() - start; + updateActivationCodes(); + logger.debug("Backtracked " + steps + " times."); + } + + public int backtrackUntilLastCommonActivation(Object[] newTrajectory) { + long start = System.nanoTime(); + Iterator currentTrajectoryIterator = trajectory.getTrajectory().iterator(); + if (!currentTrajectoryIterator.hasNext()) { + return 0; + } + int indexOfLastCommonActivation = 0; + for (Object activationCode : newTrajectory) { + if (currentTrajectoryIterator.hasNext()) { + Object activationCodeFromCurrent = currentTrajectoryIterator.next(); + if (activationCodeFromCurrent.equals(activationCode)) { + indexOfLastCommonActivation++; + } else { + break; + } + } else { + // current trajectory is smaller + break; + } + } + int numberOfBacktracks = trajectory.getDepth() - indexOfLastCommonActivation; + if (numberOfBacktracks > 0) { + try { + engine.delayUpdatePropagation(() -> { + for (int i = numberOfBacktracks; i > 0; i--) { + domain.getCommandStack().undo(); + trajectory.backtrack(); + } + return null; + }); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + backtrackingTime += System.nanoTime() - start; + updateActivationCodes(); + logger.debug("Backtracked " + numberOfBacktracks + " times."); + return indexOfLastCommonActivation; + } + + public void executeTrajectoryWithMinimalBacktrack(Object[] trajectory) { + executeTrajectoryWithMinimalBacktrack(trajectory, trajectory.length); + } + + public void executeTrajectoryWithMinimalBacktrack(Object[] trajectory, int toExcludedIndex) { + int fromIndex = backtrackUntilLastCommonActivation(trajectory); + executeTrajectory(trajectory, fromIndex, toExcludedIndex, false, true); + } + + public void executeTrajectoryWithMinimalBacktrackWithoutStateCoding(Object[] trajectory) { + executeTrajectoryWithMinimalBacktrackWithoutStateCoding(trajectory, trajectory.length); + } + + public void executeTrajectoryWithMinimalBacktrackWithoutStateCoding(Object[] trajectory, int toExcludedIndex) { + int fromIndex = backtrackUntilLastCommonActivation(trajectory); + executeTrajectory(trajectory, fromIndex, toExcludedIndex, false, false); + Object stateCode = stateCoder.createStateCode(); + this.trajectory.modifyLastStateCode(stateCode); + } + + public void undoUntilRoot() { + long start = System.nanoTime(); + try { + engine.delayUpdatePropagation(() -> { + while (trajectory.canStepBack()) { + domain.getCommandStack().undo(); + trajectory.backtrack(); + } + return null; + }); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + backtrackingTime += System.nanoTime() - start; + updateActivationCodes(); + logger.debug("Backtracked to root."); + } + + private Object generateMatchCode(IPatternMatch match) { + return stateCoder.createActivationCode(match); + } + + public Object getCurrentState() { + return trajectory.getCurrentStateId(); + } + + public SolutionTrajectory createSolutionTrajectroy() { + return trajectory.createSolutionTrajectory(context.getGlobalContext().getStateCoderFactory(), this); + } + + public TrajectoryInfo getTrajectoryInfo() { + return trajectory; + } + + public void setDesignSpace(IDesignSpace designSpace) { + this.designSpace = designSpace; + } + + public IDesignSpace getDesignSpace() { + return designSpace; + } + + public void registerExploreEventHandler(IExploreEventHandler handler) { + if (handler == null) { + return; + } + if (handlers == null) { + handlers = new ArrayList(); + } + handlers.add(handler); + } + + public void deregisterExploreEventHandler(IExploreEventHandler handler) { + if (handler == null) { + return; + } + if (handlers != null) { + handlers.remove(handler); + } + } + + public void registerActivationCostProcessor(String name, BatchTransformationRule rule, + ActivationFitnessProcessor activationFitnessProcessor) { + if (activationFitnessProcessors == null || activationFitnessProcessorNames == null) { + activationFitnessProcessors = new HashMap, ActivationFitnessProcessor>(); + activationFitnessProcessorNames = new HashMap, String>(); + } + activationFitnessProcessors.put(rule, activationFitnessProcessor); + activationFitnessProcessorNames.put(rule, name); + } + + public boolean isCurentStateInTrajectory() { + Object currentStateId = trajectory.getCurrentStateId(); + List stateTrajectory = trajectory.getStateTrajectory(); + int size = stateTrajectory.size(); + for (int i = 0; i < size - 1; i++) { + Object stateId = stateTrajectory.get(i); + if (currentStateId.equals(stateId)) { + return true; + } + } + return false; + } + + public IStateCoder getStateCoder() { + return stateCoder; + } + + private void updateActivationCodes() { + activationCodes.updateActivationCodes(); + } + + public long getForwardTime() { + return forwardTime; + } + + public long getBacktrackingTime() { + return backtrackingTime; + } + + @Override + public void forwardWorked(long nanos) { + forwardTime += nanos; + } + + @Override + public void backtrackWorked(long nanos) { + backtrackingTime += nanos; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseConflictResolver.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseConflictResolver.java new file mode 100644 index 00000000..35504b56 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseConflictResolver.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.transformation.evm.api.resolver.ChangeableConflictSet; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictResolver; + +public class DseConflictResolver implements ConflictResolver { + + private ConflictResolver activationOrderingconflictResolver; + private IStateCoder stateCoder; + private DseConflictSet lastCreatedConflictSet; + + public DseConflictResolver(ConflictResolver activationOrderingConflictResolver, IStateCoder stateCoder) { + this.activationOrderingconflictResolver = activationOrderingConflictResolver; + this.stateCoder = stateCoder; + } + + @Override + public ChangeableConflictSet createConflictSet() { + lastCreatedConflictSet = new DseConflictSet(this, activationOrderingconflictResolver, stateCoder); + return lastCreatedConflictSet; + } + + public DseConflictSet getLastCreatedConflictSet() { + return lastCreatedConflictSet; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseConflictSet.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseConflictSet.java new file mode 100644 index 00000000..cba595f4 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseConflictSet.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import java.util.Set; + +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.transformation.evm.api.Activation; +import org.eclipse.viatra.transformation.evm.api.resolver.ChangeableConflictSet; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictResolver; + +public class DseConflictSet implements ChangeableConflictSet { + + private ActivationCodesConflictSet activationCodesConflictSet; + private ChangeableConflictSet activationOrderingConflictSet; + private ChangeableConflictSet prevActivationOrderingConflictSet; + private ConflictResolver resolver; + + public DseConflictSet(ConflictResolver resolver, ConflictResolver activationOrderingConflictResolver, + IStateCoder stateCoder) { + this.resolver = resolver; + activationOrderingConflictSet = activationOrderingConflictResolver.createConflictSet(); + activationCodesConflictSet = new ActivationCodesConflictSet(activationOrderingConflictSet, stateCoder); + } + + @Override + public Activation getNextActivation() { + return activationOrderingConflictSet.getNextActivation(); + } + + @Override + public Set> getNextActivations() { + return activationOrderingConflictSet.getNextActivations(); + } + + @Override + public Set> getConflictingActivations() { + return activationOrderingConflictSet.getConflictingActivations(); + } + + @Override + public ConflictResolver getConflictResolver() { + return resolver; + } + + @Override + public boolean addActivation(Activation activation) { + activationCodesConflictSet.addActivation(activation); + return activationOrderingConflictSet.addActivation(activation); + } + + @Override + public boolean removeActivation(Activation activation) { + activationCodesConflictSet.removeActivation(activation); + return activationOrderingConflictSet.removeActivation(activation); + } + + public ActivationCodesConflictSet getActivationCodesConflictSet() { + return activationCodesConflictSet; + } + + public void changeActivationOrderingConflictSet(ChangeableConflictSet newActivationOrderingConflictSet) { + for (Activation activation : activationOrderingConflictSet.getConflictingActivations()) { + newActivationOrderingConflictSet.addActivation(activation); + } + activationCodesConflictSet.reinitWithActivations(newActivationOrderingConflictSet); + ChangeableConflictSet tmp = activationOrderingConflictSet; + activationOrderingConflictSet = newActivationOrderingConflictSet; + prevActivationOrderingConflictSet = tmp; + } + + public void changeActivationOrderingConflictSetBack() { + ChangeableConflictSet newActivationOrderingConflictSet = + prevActivationOrderingConflictSet.getConflictResolver().createConflictSet(); + changeActivationOrderingConflictSet(newActivationOrderingConflictSet); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseEvmRuleBase.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseEvmRuleBase.java new file mode 100644 index 00000000..838c1d1b --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseEvmRuleBase.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import org.eclipse.viatra.transformation.evm.api.Agenda; +import org.eclipse.viatra.transformation.evm.api.RuleBase; +import org.eclipse.viatra.transformation.evm.api.event.EventRealm; + +public class DseEvmRuleBase extends RuleBase { + + public DseEvmRuleBase(EventRealm eventRealm, Agenda agenda) { + super(eventRealm, agenda); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseIdPoolHelper.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseIdPoolHelper.java new file mode 100644 index 00000000..f6fee7be --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/DseIdPoolHelper.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import java.util.Collection; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +public enum DseIdPoolHelper { + + INSTANCE; + + public static interface IGetRuleExecutions { + int getRuleExecutions(BatchTransformationRule rule); + } + + public static class IdProvider { + + private final BatchTransformationRule rule; + private IGetRuleExecutions getRuleExecutions; + + public IdProvider(IGetRuleExecutions getRuleExecutions, BatchTransformationRule rule) { + this.getRuleExecutions = getRuleExecutions; + this.rule = rule; + } + + public int getId() { + return getRuleExecutions.getRuleExecutions(rule); + } + + } + + private ConcurrentHashMap, IdProvider>> idProviders = new ConcurrentHashMap<>(); + + public int getId(BatchTransformationRule rule) { + Thread currentThread = Thread.currentThread(); + HashMap, IdProvider> ruleMap = idProviders.get(currentThread); + if (ruleMap == null) { + throw new DSEException("There is no registered id provider"); + } + IdProvider idProvider = ruleMap.get(rule); + return idProvider.getId(); + } + + public void registerRules(IGetRuleExecutions getRuleExecutions, Collection> rules) { + Thread currentThread = Thread.currentThread(); + HashMap, IdProvider> ruleMap = new HashMap<>(); + for (BatchTransformationRule rule : rules) { + IdProvider idProvider = new IdProvider(getRuleExecutions, rule); + ruleMap.put(rule, idProvider); + } + idProviders.put(currentThread, ruleMap); + } + + public void disposeByThread() { + Thread currentThread = Thread.currentThread(); + idProviders.remove(currentThread); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ExplorerThread.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ExplorerThread.java new file mode 100644 index 00000000..f2231e5c --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ExplorerThread.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; +import org.eclipse.viatra.transformation.evm.api.RuleEngine; + +/** + * This class implements the {@link Runnable} interface, to able to run an exploration strategy in a separate thread. It + * is also responsible to initialize the exploration, start the exploration (call the {@link IStrategy#explore()} + * method), catch any exception during exploration and to shutdown the thread correctly. + * + * @author Földenyi Miklos & Nagy Andras Szabolcs + * + */ +public class ExplorerThread implements Runnable { + + private final ThreadContext threadContext; + + private IStrategy strategy; + + public ExplorerThread(final ThreadContext context) { + this.threadContext = context; + strategy = threadContext.getStrategy(); + } + + /** + * Signals the {@link IStrategy} instance that execution should be stopped. By contract, the strategy is to + * stop execution at the next stage of execution where stopping and exiting is appropriate. + */ + public void stopRunning() { + strategy.interruptStrategy(); + } + + /** + * Starts the design space exploration. Returns only when the {@link IStrategy#explore()} method returns. + */ + public void run() { + GlobalContext globalContext = threadContext.getGlobalContext(); + try { + + threadContext.init(); + + strategy.initStrategy(threadContext); + + strategy.explore(); + + threadContext.backtrackUntilRoot(); + + } catch (Throwable e) { + Logger.getLogger(IStrategy.class).error("Thread stopped unexpectedly!", e); + globalContext.registerException(e); + } finally { + globalContext.strategyFinished(this); + dispose(); + } + } + + /** + * Disposes of this strategy. Recursively calls dispose on the underlying {@link RuleEngine} and + * {@link ViatraQueryEngine}. Calling this is only required if the design space exploration was launched in thread, as + * the underlying engines get collected on the stop of the running {@link Thread}. + */ + public void dispose() { + threadContext.getRuleEngine().dispose(); + DseIdPoolHelper.INSTANCE.disposeByThread(); + } + + /** + * Returns the associated {@link ThreadContext} that houses all the thread specific data about the exploration + * process, and is also the gateway to the {@link GlobalContext} which stores data relevant to the design space + * exploration process as a whole. + * + * @return the relevant {@link ThreadContext}. + */ + public ThreadContext getThreadContext() { + return threadContext; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/GlobalContext.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/GlobalContext.java new file mode 100644 index 00000000..7325ead3 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/GlobalContext.java @@ -0,0 +1,374 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategyFactory; +import org.eclipse.viatra.dse.designspace.api.IDesignSpace; +import org.eclipse.viatra.dse.multithreading.DSEThreadPool; +import org.eclipse.viatra.dse.objectives.IGlobalConstraint; +import org.eclipse.viatra.dse.objectives.IObjective; +import org.eclipse.viatra.dse.objectives.LeveledObjectivesHelper; +import org.eclipse.viatra.dse.solutionstore.SolutionStore; +import org.eclipse.viatra.dse.statecode.IStateCoderFactory; +import org.eclipse.viatra.dse.util.EMFHelper; +import org.eclipse.viatra.dse.visualizer.IDesignSpaceVisualizer; +import org.eclipse.viatra.transformation.evm.api.RuleSpecification; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictResolver; +import org.eclipse.viatra.transformation.evm.specific.ConflictResolvers; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +/** + * Creates new contexts for strategies. It is needed because of the multithreading. + * + * @author Andras Szabolcs Nagy + * + */ +public class GlobalContext { + + // **** fields and methods for multi threading *****// + // *************************************************// + + public enum ExplorationProcessState { + NOT_STARTED, + RUNNING, + STOPPING, + COMPLETED + } + + private ConcurrentLinkedQueue exceptions = new ConcurrentLinkedQueue(); + + private volatile ExplorationProcessState state = ExplorationProcessState.NOT_STARTED; + private final Set runningThreads = new HashSet(); + private DSEThreadPool threadPool = new DSEThreadPool(); + private int numberOfStartedThreads = 0; + private IDesignSpace designSpace; + + private AtomicBoolean firstThreadContextInited = new AtomicBoolean(false); + private AtomicBoolean firstThreadContextIniting = new AtomicBoolean(false); + + private Map, BatchTransformationRule> specificationRuleMap; + + private Object terminationSnycObject = new Object(); + + private Logger logger = Logger.getLogger(IStrategy.class); + + private boolean isAlreadyInited; + + public void waitForTermination() { + synchronized (terminationSnycObject) { + while (!isDone()) { + try { + terminationSnycObject.wait(); + } catch (InterruptedException e) { + } + } + } + } + + /** + * Starts a new thread to explore the design space. + * + * @param originalThreadContext The context of the thread which starts the new thread. + * @param model The model to start from. + * @param cloneModel It should be true in most cases. + * @param strategy The strategy, the thread will use. + * @return The {@link ExplorerThread} + */ + private synchronized ExplorerThread tryStartNewThread(ThreadContext originalThreadContext, Notifier model, + IStrategy strategy) { + + if(!isAlreadyInited) { + isAlreadyInited = true; + init(); + } + + if (state != ExplorationProcessState.COMPLETED && state != ExplorationProcessState.STOPPING + && threadPool.canStartNewThread()) { + + ThreadContext newThreadContext = new ThreadContext(this, strategy, model); + + // TODO : clone undo list? slave strategy can't go further back... + ExplorerThread explorerThread = new ExplorerThread(newThreadContext); + newThreadContext.setExplorerThread(explorerThread); + + boolean isSuccessful = threadPool.tryStartNewStrategy(explorerThread); + + if (isSuccessful) { + runningThreads.add(explorerThread); + + state = ExplorationProcessState.RUNNING; + ++numberOfStartedThreads; + + logger.info("New thread started, active threads: " + runningThreads.size()); + + return explorerThread; + } + } + return null; + } + + public synchronized ExplorerThread tryStartNewThread(ThreadContext originalThreadContext, IStrategy strategy) { + return tryStartNewThread(originalThreadContext, EMFHelper.clone(originalThreadContext.getModel()), strategy); + } + + public synchronized ExplorerThread tryStartNewThreadWithoutModelClone(ThreadContext originalThreadContext, + IStrategy strategy) { + return tryStartNewThread(originalThreadContext, originalThreadContext.getModel(), strategy); + } + + public synchronized ExplorerThread startFirstThread(IStrategy strategy, Notifier model) { + Preconditions.checkState(!isAlreadyInited, "First thread is already started."); + return tryStartNewThread(null, EMFHelper.clone(model), strategy); + } + + public synchronized ExplorerThread startFirstThreadWithoutModelClone(IStrategy strategy, Notifier model) { + Preconditions.checkState(!isAlreadyInited, "First thread is already started."); + return tryStartNewThread(null, model, strategy); + } + + public synchronized void startAllThreads(ThreadContext originalThreadContext, IStrategyFactory strategyFactory) { + while (canStartNewThread()) { + tryStartNewThread(originalThreadContext, strategyFactory.createStrategy()); + } + } + + /** + * Starts a new thread to explore the design space. + * + * @param strategyBase + * The {@link Strategy}. + * @param tedToClone + * The model to clone. Hint: context.getTed() + */ + + public synchronized void strategyFinished(ExplorerThread strategy) { + runningThreads.remove(strategy); + + logger.info("Thread finished, active threads: " + runningThreads.size()); + + // is the first part necessary? + if (runningThreads.isEmpty()) { + state = ExplorationProcessState.COMPLETED; + threadPool.shutdown(); + + // if the main thread (which started the exploration) + // is waiting for the solution, than wake it up + synchronized (terminationSnycObject) { + terminationSnycObject.notify(); + logger.info("Exploration terminated."); + } + + } + } + + public synchronized boolean isDone() { + return state == ExplorationProcessState.COMPLETED && runningThreads.isEmpty(); + } + + public synchronized boolean isNotStarted() { + return state == ExplorationProcessState.NOT_STARTED; + } + + public boolean canStartNewThread() { + return (state == ExplorationProcessState.NOT_STARTED || state == ExplorationProcessState.RUNNING) + && threadPool.canStartNewThread(); + } + + public synchronized void stopAllThreads() { + if (state == ExplorationProcessState.RUNNING) { + state = ExplorationProcessState.STOPPING; + logger.info("Stopping all threads."); + for (ExplorerThread strategy : runningThreads) { + strategy.stopRunning(); + } + } + } + + public void registerException(Throwable e) { + exceptions.add(e); + } + + // ******* fields and methods for exploration *******// + // **************************************************// + + private List objectives = new ArrayList(); + private IObjective[][] leveledObjectives; + private List globalConstraints = new ArrayList(); + private Set> transformations = new HashSet>(); + private IStateCoderFactory stateCoderFactory; + private SolutionStore solutionStore = new SolutionStore(1); + private Object sharedObject; + private List visualizers; + + private ConflictResolver conflictResolver = ConflictResolvers.createArbitraryResolver(); + + private void init() { + leveledObjectives = new LeveledObjectivesHelper(objectives).initLeveledObjectives(); + + specificationRuleMap = new HashMap<>(); + for (BatchTransformationRule rule : transformations) { + specificationRuleMap.put(rule.getRuleSpecification(), rule); + } + } + + public List getVisualizers() { + return ImmutableList.copyOf(visualizers); + } + + public void registerDesignSpaceVisualizer(IDesignSpaceVisualizer visualizer) { + if (visualizer == null) { + return; + } + if (visualizers == null) { + visualizers = new ArrayList(); + } + visualizers.add(visualizer); + } + + public void deregisterDesignSpaceVisualizer(IDesignSpaceVisualizer visualizer) { + if (visualizer == null) { + return; + } + if (visualizers != null) { + visualizers.remove(visualizer); + } + } + + public boolean isDesignSpaceVisualizerRegistered(IDesignSpaceVisualizer visualizer) { + if (visualizers != null) { + return visualizers.contains(visualizer); + } + return false; + } + + public void initVisualizersForThread(ThreadContext threadContext) { + if (visualizers != null && !visualizers.isEmpty()) { + for (IDesignSpaceVisualizer visualizer : visualizers) { + visualizer.init(threadContext); + threadContext.getDesignSpaceManager().registerExploreEventHandler(visualizer); + } + } + } + + public boolean isExceptionHappendInOtherThread() { + return !exceptions.isEmpty(); + } + + public Collection getExceptions() { + return exceptions; + } + + public IStateCoderFactory getStateCoderFactory() { + return stateCoderFactory; + } + + public void setStateCoderFactory(IStateCoderFactory stateCoderFactory) { + this.stateCoderFactory = stateCoderFactory; + } + + public Set> getTransformations() { + return transformations; + } + + public void setTransformations(Set> transformations) { + this.transformations = transformations; + } + + public DSEThreadPool getThreadPool() { + return threadPool; + } + + public IDesignSpace getDesignSpace() { + return designSpace; + } + + public void setDesignSpace(IDesignSpace designSpace) { + this.designSpace = designSpace; + } + + public int getNumberOfStartedThreads() { + return numberOfStartedThreads; + } + + public Object getSharedObject() { + return sharedObject; + } + + public void setSharedObject(Object sharedObject) { + this.sharedObject = sharedObject; + } + + public ExplorationProcessState getState() { + return state; + } + + public List getObjectives() { + return objectives; + } + + public void setObjectives(List objectives) { + this.objectives = objectives; + } + + public List getGlobalConstraints() { + return globalConstraints; + } + + public void setGlobalConstraints(List globalConstraints) { + this.globalConstraints = globalConstraints; + } + + AtomicBoolean getFirstThreadContextInited() { + return firstThreadContextInited; + } + + AtomicBoolean getFirstThreadContextIniting() { + return firstThreadContextIniting; + } + + public IObjective[][] getLeveledObjectives() { + return leveledObjectives; + } + + public void setSolutionStore(SolutionStore solutionStore) { + this.solutionStore = solutionStore; + } + + public SolutionStore getSolutionStore() { + return solutionStore; + } + + public Map, BatchTransformationRule> getSpecificationRuleMap() { + return specificationRuleMap; + } + + public void setConflictResolver(ConflictResolver conflictResolver) { + this.conflictResolver = conflictResolver; + } + + public ConflictResolver getConflictResolver() { + return conflictResolver; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/IDseStrategyContext.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/IDseStrategyContext.java new file mode 100644 index 00000000..d630964f --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/IDseStrategyContext.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategyFactory; +import org.eclipse.viatra.dse.designspace.api.IDesignSpace; +import org.eclipse.viatra.dse.designspace.api.TrajectoryInfo; +import org.eclipse.viatra.dse.objectives.Fitness; +import org.eclipse.viatra.dse.objectives.IGlobalConstraint; +import org.eclipse.viatra.dse.objectives.IObjective; +import org.eclipse.viatra.dse.objectives.ObjectiveComparatorHelper; +import org.eclipse.viatra.dse.solutionstore.SolutionStore; +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; +import org.eclipse.viatra.transformation.evm.api.Activation; +import org.eclipse.viatra.transformation.evm.api.RuleEngine; +import org.eclipse.viatra.transformation.evm.api.RuleSpecification; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +/** + * This interface is only to overview the required methods for exploration strategies. It is not used explicitly. + * + * @author Andras Szabolcs Nagy + * + */ +public interface IDseStrategyContext { + + void init(); + + Notifier getModel(); + EditingDomain getEditingDomain(); + ViatraQueryEngine getQueryEngine(); + RuleEngine getRuleEngine(); + IStrategy getStrategy(); + ExplorerThread getExplorerThread(); + List getObjectives(); + IObjective[][] getLeveledObjectives(); + List getGlobalConstraints(); + + SolutionStore getSolutionStore(); + void newSolution(); +// TODO void newSolution(TrajectoryFitness trajectoryFitness); + + + ObjectiveComparatorHelper getObjectiveComparatorHelper(); + + GlobalContext getGlobalContext(); + Set> getRules(); + BatchTransformationRule getRuleByRuleSpecification(RuleSpecification ruleSpecification); + ExplorerThread tryStartNewThread(IStrategy strategy); /*IDseStrategyContext originalContext*/ + ExplorerThread tryStartNewThreadWithoutModelClone(IStrategy strategy); + void startAllThreads(IStrategyFactory strategyFactory); + Object getSharedObject(); + void setSharedObject(Object sharedObject); + + + DesignSpaceManager getDesignSpaceManager(); + IStateCoder getStateCoder(); + IDesignSpace getDesignSpace(); + TrajectoryInfo getTrajectoryInfo(); + List getTrajectory(); + List getTrajectoryCopied(); + int getDepth(); + Object getCurrentStateId(); + + Object getTransitionByActivation(Activation activation); + Activation getActivationById(Object activationId); + BatchTransformationRule getRuleByActivation(Activation activation); + BatchTransformationRule getRuleByActivationId(Object activationId); + + Collection getCurrentActivationIds(); + Collection getUntraversedActivationIds(); +// TODO Object getArbitraryActivationId(); +// TODO Object getArbitraryUntraversedActivationId(); + + void executeAcitvationId(Object activationId); + boolean tryExecuteAcitvationId(Object activationId); + boolean executeRandomActivationId(); + void executeTrajectory(Object[] activationIds); + void executeTrajectory(Object[] activationIds, int fromIncludedIndex, int toExcludedIndex); + int executeTrajectoryByTrying(Object[] activationIds); + int executeTrajectoryByTrying(Object[] activationIds, int fromIncludedIndex, int toExcludedIndex); + int executeTrajectoryWithoutStateCoding(Object[] activationIds); + int executeTrajectoryWithoutStateCoding(Object[] activationIds, int fromIncludedIndex, int toExcludedIndex); + int executeTrajectoryByTryingWithoutStateCoding(Object[] activationIds); + int executeTrajectoryByTryingWithoutStateCoding(Object[] activationIds, int fromIncludedIndex, int toExcludedIndex); + void executeTrajectoryWithMinimalBacktrack(Object[] trajectory); + void executeTrajectoryWithMinimalBacktrack(Object[] trajectory, int toExcludedIndex); + void executeTrajectoryWithMinimalBacktrackWithoutStateCoding(Object[] trajectory); + void executeTrajectoryWithMinimalBacktrackWithoutStateCoding(Object[] trajectory, int toExcludedIndex); + + boolean backtrack(); + // TODO int backtrack(int times); + void backtrackUntilLastCommonActivation(Object[] trajectory); + void backtrackUntilRoot(); + + Fitness calculateFitness(); + Fitness getLastFitness(); + boolean checkGlobalConstraints(); + boolean isCurrentStateAlreadyTraversed(); + // this needs states stored: + boolean isCurrentStateInTrajectory(); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/SingletonSetConflictResolver.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/SingletonSetConflictResolver.java new file mode 100644 index 00000000..8c0a715a --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/SingletonSetConflictResolver.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import org.eclipse.viatra.transformation.evm.api.RuleEngine; +import org.eclipse.viatra.transformation.evm.api.resolver.ChangeableConflictSet; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictResolver; + +/** + * + * @author Andras Szabolcs Nagy + * + */ +public class SingletonSetConflictResolver implements ConflictResolver { + + protected ChangeableConflictSet conflictSet; + protected ConflictResolver conflictResolver; + protected ConflictResolver prevConflictResolver; + protected RuleEngine ruleEngine; + + public SingletonSetConflictResolver(ConflictResolver conflictResolver) { + this.conflictResolver = conflictResolver; + conflictSet = conflictResolver.createConflictSet(); + } + + @Override + public ChangeableConflictSet createConflictSet() { + return conflictSet; + } + + public void changeConflictResolver(ConflictResolver conflictResolver) { + ConflictResolver tmp = this.conflictResolver; + this.conflictResolver = conflictResolver; + prevConflictResolver = tmp; + conflictSet = conflictResolver.createConflictSet(); + ruleEngine.setConflictResolver(this); + } + + public void changeConflictResolverBack() { + changeConflictResolver(prevConflictResolver); + } + + public void setRuleEngine(RuleEngine ruleEngine) { + this.ruleEngine = ruleEngine; + ruleEngine.setConflictResolver(this); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ThreadContext.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ThreadContext.java new file mode 100644 index 00000000..b62442ce --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/base/ThreadContext.java @@ -0,0 +1,542 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.base; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategyFactory; +import org.eclipse.viatra.dse.designspace.api.IDesignSpace; +import org.eclipse.viatra.dse.designspace.api.TrajectoryInfo; +import org.eclipse.viatra.dse.objectives.Fitness; +import org.eclipse.viatra.dse.objectives.IGlobalConstraint; +import org.eclipse.viatra.dse.objectives.IObjective; +import org.eclipse.viatra.dse.objectives.ObjectiveComparatorHelper; +import org.eclipse.viatra.dse.solutionstore.SolutionStore; +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.dse.util.EMFHelper; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; +import org.eclipse.viatra.query.runtime.emf.EMFScope; +import org.eclipse.viatra.query.runtime.exception.ViatraQueryException; +import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; +import org.eclipse.viatra.transformation.evm.api.Activation; +import org.eclipse.viatra.transformation.evm.api.RuleEngine; +import org.eclipse.viatra.transformation.evm.api.RuleSpecification; +import org.eclipse.viatra.transformation.evm.api.event.EventFilter; +import org.eclipse.viatra.transformation.evm.api.resolver.ChangeableConflictSet; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictResolver; +import org.eclipse.viatra.transformation.evm.api.resolver.ConflictSet; +import org.eclipse.viatra.transformation.evm.specific.RuleEngines; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +/** + * This class holds all the information that is related to a single processing thread of the DesignSpaceExploration + * process. For any attributes related to the Design Space Exploration process as a whole, see {@link GlobalContext}. + * + * @author Miklos Foldenyi + * + */ +public class ThreadContext implements IDseStrategyContext{ + + private final GlobalContext globalContext; + private final IStrategy strategy; + private ExplorerThread explorerThread; + private RuleEngine ruleEngine; + private ViatraQueryEngine queryEngine; + private EditingDomain domain; + private Notifier model; + private DesignSpaceManager designSpaceManager; + private List objectives; + private List globalConstraints; + private Fitness lastFitness; + private ObjectiveComparatorHelper objectiveComparatorHelper; + private IStateCoder stateCoder; + private DseConflictResolver dseConflictResolver; + private DseConflictSet dseConflictSet; + private ActivationCodesConflictSet activationCodesConflictSet; + + /** + * This value is true after the {@link ThreadContext} has been initialized in it's own thread. + */ + private AtomicBoolean inited = new AtomicBoolean(false); + + private boolean isFirstThread = false; + private IObjective[][] leveledObjectives; + + private static class GetRuleExecutionsImpl implements DseIdPoolHelper.IGetRuleExecutions { + + private List> executedRules; + + public GetRuleExecutionsImpl(List> executedRulesView) { + this.executedRules = executedRulesView; + } + + @Override + public int getRuleExecutions(BatchTransformationRule rule) { + int nextId = 0; + for (BatchTransformationRule r : executedRules) { + if (r.equals(rule)) { + nextId ++; + } + } + return nextId; + } + + } + + public DseConflictResolver getConflictResolver() { + return dseConflictResolver; + } + + public ConflictSet getConflictSet() { + return dseConflictSet; + } + + /** + * Creates a {@link ThreadContext} and sets it up to be initialized on the given {@link TransactionalEditingDomain} + * + * @param globalContext + * @param strategyBase + * @param domain + * @param trajectoryInfoToClone + * @param parentGuidance + */ + public ThreadContext(final GlobalContext globalContext, IStrategy strategy, Notifier model) { + Preconditions.checkArgument(model != null, "Cannot initialize ThreadContext on a null model."); + this.globalContext = globalContext; + this.strategy = strategy; + this.model = model; + } + + /** + * Initializes the {@link ThreadContext} by initializing the underlying {@link ViatraQueryEngine} and + * {@link RuleEngine}. {@link Guidance} initialization is also happening within this method. + * + * @throws DSEException + */ + public void init() { + + AtomicBoolean isFirst = globalContext.getFirstThreadContextIniting(); + AtomicBoolean isFirstReady = globalContext.getFirstThreadContextInited(); + if (!isFirstReady.get()) { + if (!isFirst.compareAndSet(false, true)) { + try { + do { + Thread.sleep(5); + } while (!isFirstReady.get()); + } catch (InterruptedException e) { + } + } else { + isFirstThread = true; + } + } + // prohibit re-initialization + Preconditions.checkArgument(!inited.getAndSet(true), "This Thread context has been initialized already!"); + + try { + // initialize query engine + final EMFScope scope = new EMFScope(model); + queryEngine = ViatraQueryEngine.on(scope); + + + stateCoder = getGlobalContext().getStateCoderFactory().createStateCoder(); + stateCoder.init(model); + stateCoder.createStateCode(); + + ConflictResolver activationOrderingCconflictResolver = globalContext.getConflictResolver(); + dseConflictResolver = new DseConflictResolver(activationOrderingCconflictResolver, stateCoder); + + ruleEngine = RuleEngines.createViatraQueryRuleEngine(queryEngine); + ruleEngine.setConflictResolver(dseConflictResolver); + for (BatchTransformationRule tr : globalContext.getTransformations()) { + ruleEngine.addRule(tr.getRuleSpecification(), (EventFilter) tr.getFilter()); + } + dseConflictSet = dseConflictResolver.getLastCreatedConflictSet(); + activationCodesConflictSet = dseConflictSet.getActivationCodesConflictSet(); + activationCodesConflictSet.updateActivationCodes(); + + + } catch (ViatraQueryException e) { + throw new DSEException("Failed to create unmanaged ViatraQueryEngine on the model.", e); + } + + if (isFirstThread) { + + objectives = globalContext.getObjectives(); + leveledObjectives = globalContext.getLeveledObjectives(); + globalConstraints = globalContext.getGlobalConstraints(); + + } else { + objectives = new ArrayList(); + + IObjective[][] leveledObjectivesToCopy = globalContext.getLeveledObjectives(); + leveledObjectives = new IObjective[leveledObjectivesToCopy.length][]; + for (int i = 0; i < leveledObjectivesToCopy.length; i++) { + leveledObjectives[i] = new IObjective[leveledObjectivesToCopy[i].length]; + for (int j = 0; j < leveledObjectivesToCopy[i].length; j++) { + leveledObjectives[i][j] = leveledObjectivesToCopy[i][j].createNew(); + objectives.add(leveledObjectives[i][j]); + } + } + + globalConstraints = new ArrayList(); + for (IGlobalConstraint globalConstraint : globalContext.getGlobalConstraints()) { + globalConstraints.add(globalConstraint.createNew()); + } + + } + // create the thread specific DesignSpaceManager + this.domain = EMFHelper.createEditingDomain(model); + designSpaceManager = new DesignSpaceManager(this); + + boolean isThereHardObjective = false; + for (IObjective objective : objectives) { + objective.init(this); + if (objective.isHardObjective()) { + isThereHardObjective = true; + } + } + if (!isThereHardObjective) { + Logger.getLogger(IStrategy.class).warn( + "No hard objective is specified: all reachable state is a solution. Use a dummy hard objective to be explicit."); + } + + for (IGlobalConstraint globalConstraint : globalConstraints) { + globalConstraint.init(this); + } + + DseIdPoolHelper.INSTANCE.registerRules(new GetRuleExecutionsImpl(getDesignSpaceManager().getTrajectoryInfo().getRules()), getRules()); + + globalContext.initVisualizersForThread(this); + + if (isFirstThread) { + isFirstReady.set(true); + } + + } + + public Fitness calculateFitness() { + Fitness result = new Fitness(); + + boolean satisifiesHardObjectives = true; + + for (IObjective objective : objectives) { + Double fitness = objective.getFitness(this); + result.put(objective.getName(), fitness); + if (objective.isHardObjective() && !objective.satisifiesHardObjective(fitness)) { + satisifiesHardObjectives = false; + } + } + + result.setSatisifiesHardObjectives(satisifiesHardObjectives); + + lastFitness = result; + + return result; + } + + public boolean checkGlobalConstraints() { + for (IGlobalConstraint globalConstraint : globalContext.getGlobalConstraints()) { + if (!globalConstraint.checkGlobalConstraint(this)) { + return false; + } + } + return true; + } + + public RuleEngine getRuleEngine() { + return ruleEngine; + } + + public GlobalContext getGlobalContext() { + return globalContext; + } + + public DesignSpaceManager getDesignSpaceManager() { + return designSpaceManager; + } + + public EditingDomain getEditingDomain() { + return domain; + } + + public Notifier getModel() { + return model; + } + + public ViatraQueryEngine getQueryEngine() { + return queryEngine; + } + + public IStrategy getStrategy() { + return strategy; + } + + public ExplorerThread getExplorerThread() { + return explorerThread; + } + + public void setExplorerThread(ExplorerThread explorerThread) { + this.explorerThread = explorerThread; + } + + public Fitness getLastFitness() { + return lastFitness; + } + + public ObjectiveComparatorHelper getObjectiveComparatorHelper() { + if (objectiveComparatorHelper == null) { + objectiveComparatorHelper = new ObjectiveComparatorHelper(leveledObjectives); + } + return objectiveComparatorHelper; + } + + public IObjective[][] getLeveledObjectives() { + return leveledObjectives; + } + + public List getObjectives() { + return objectives; + } + + public List getGlobalConstraints() { + return globalConstraints; + } + + @Override + public SolutionStore getSolutionStore() { + return globalContext.getSolutionStore(); + } + + @Override + public void newSolution() { + globalContext.getSolutionStore().newSolution(this); + } + + @Override + public Object getSharedObject() { + return globalContext.getSharedObject(); + } + + @Override + public void setSharedObject(Object sharedObject) { + globalContext.setSharedObject(sharedObject); + } + + @Override + public Set> getRules() { + return globalContext.getTransformations(); + } + + @Override + public BatchTransformationRule getRuleByRuleSpecification(RuleSpecification ruleSpecification) { + return globalContext.getSpecificationRuleMap().get(ruleSpecification); + } + + @Override + public ExplorerThread tryStartNewThread(IStrategy strategy) { + return globalContext.tryStartNewThread(this, strategy); + } + + @Override + public ExplorerThread tryStartNewThreadWithoutModelClone(IStrategy strategy) { + return globalContext.tryStartNewThreadWithoutModelClone(this, strategy); + } + + @Override + public void startAllThreads(IStrategyFactory strategyFactory) { + globalContext.startAllThreads(this, strategyFactory); + } + + @Override + public IStateCoder getStateCoder() { + return stateCoder; + } + + @Override + public IDesignSpace getDesignSpace() { + return globalContext.getDesignSpace(); + } + + @Override + public TrajectoryInfo getTrajectoryInfo() { + return designSpaceManager.getTrajectoryInfo(); + } + + @Override + public List getTrajectory() { + return designSpaceManager.getTrajectoryInfo().getTrajectory(); + } + + @Override + public List getTrajectoryCopied() { + return new ArrayList(getTrajectory()); + } + + @Override + public int getDepth() { + return designSpaceManager.getTrajectoryInfo().getDepth(); + } + + @Override + public Object getCurrentStateId() { + return designSpaceManager.getTrajectoryInfo().getCurrentStateId(); + } + + @Override + public Object getTransitionByActivation(Activation activation) { + return designSpaceManager.getTransitionByActivation(activation); + } + + @Override + public Activation getActivationById(Object activationId) { + return designSpaceManager.getActivationById(activationId); + } + + @Override + public BatchTransformationRule getRuleByActivation(Activation activation) { + return designSpaceManager.getRuleByActivation(activation); + } + + @Override + public BatchTransformationRule getRuleByActivationId(Object activationId) { + return designSpaceManager.getRuleByActivationId(activationId); + } + + @Override + public Collection getCurrentActivationIds() { + return designSpaceManager.getTransitionsFromCurrentState(); + } + + @Override + public Collection getUntraversedActivationIds() { + return designSpaceManager.getUntraversedTransitionsFromCurrentState(); + } + + @Override + public void executeAcitvationId(Object activationId) { + designSpaceManager.fireActivation(activationId); + } + + @Override + public boolean tryExecuteAcitvationId(Object activationId) { + return designSpaceManager.tryFireActivation(activationId); + } + + @Override + public boolean executeRandomActivationId() { + return designSpaceManager.executeRandomActivationId(); + } + + @Override + public void executeTrajectory(Object[] activationIds) { + designSpaceManager.executeTrajectory(activationIds); + } + + @Override + public void executeTrajectory(Object[] activationIds, int fromIncludedIndex, int toExcludedIndex) { + designSpaceManager.executeTrajectory(activationIds, fromIncludedIndex, toExcludedIndex); + } + + @Override + public int executeTrajectoryByTrying(Object[] activationIds) { + return designSpaceManager.executeTrajectoryByTrying(activationIds); + } + + @Override + public int executeTrajectoryByTrying(Object[] activationIds, int fromIncludedIndex, int toExcludedIndex) { + return designSpaceManager.executeTrajectoryByTrying(activationIds, fromIncludedIndex, toExcludedIndex); + } + + @Override + public int executeTrajectoryWithoutStateCoding(Object[] activationIds) { + return designSpaceManager.executeTrajectoryWithoutStateCoding(activationIds); + } + + @Override + public int executeTrajectoryWithoutStateCoding(Object[] activationIds, int fromIncludedIndex, int toExcludedIndex) { + return designSpaceManager.executeTrajectoryWithoutStateCoding(activationIds, fromIncludedIndex, toExcludedIndex); + } + + @Override + public int executeTrajectoryByTryingWithoutStateCoding(Object[] activationIds) { + return designSpaceManager.executeTrajectoryByTryingWithoutStateCoding(activationIds); + } + + @Override + public int executeTrajectoryByTryingWithoutStateCoding(Object[] activationIds, int fromIncludedIndex, int toExcludedIndex) { + return designSpaceManager.executeTrajectoryByTryingWithoutStateCoding(activationIds, fromIncludedIndex, toExcludedIndex); + } + + @Override + public void executeTrajectoryWithMinimalBacktrack(Object[] trajectory) { + designSpaceManager.executeTrajectoryWithMinimalBacktrack(trajectory); + } + + @Override + public void executeTrajectoryWithMinimalBacktrack(Object[] trajectory, int toExcludedIndex) { + designSpaceManager.executeTrajectoryWithMinimalBacktrack(trajectory, toExcludedIndex); + } + + @Override + public void executeTrajectoryWithMinimalBacktrackWithoutStateCoding(Object[] trajectory) { + designSpaceManager.executeTrajectoryWithMinimalBacktrackWithoutStateCoding(trajectory); + } + + @Override + public void executeTrajectoryWithMinimalBacktrackWithoutStateCoding(Object[] trajectory, int toExcludedIndex) { + designSpaceManager.executeTrajectoryWithMinimalBacktrackWithoutStateCoding(trajectory, toExcludedIndex); + } + + @Override + public boolean backtrack() { + return designSpaceManager.undoLastTransformation(); + } + + @Override + public void backtrackUntilLastCommonActivation(Object[] trajectory) { + designSpaceManager.backtrackUntilLastCommonActivation(trajectory); + } + + @Override + public void backtrackUntilRoot() { + designSpaceManager.undoUntilRoot(); + } + + @Override + public boolean isCurrentStateAlreadyTraversed() { + return designSpaceManager.isNewModelStateAlreadyTraversed(); + } + + @Override + public boolean isCurrentStateInTrajectory() { + return designSpaceManager.isCurentStateInTrajectory(); + } + + public ActivationCodesConflictSet getActivationCodesConflictSet() { + return activationCodesConflictSet; + } + + public void changeActivationOrdering(ChangeableConflictSet activationOrderingConflictSet) { + this.dseConflictSet.changeActivationOrderingConflictSet(activationOrderingConflictSet); + } + + public void changeActivationOrderingBack() { + this.dseConflictSet.changeActivationOrderingConflictSetBack(); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/DesignSpace.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/DesignSpace.java new file mode 100644 index 00000000..bc0f08d4 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/DesignSpace.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.designspace.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +public class DesignSpace implements IDesignSpace { + + Set statesView; + + Set rootStates; + Set rootStatesView; + + Map> statesAndActivations; + + Random random = new Random(); + + public DesignSpace() { + rootStates = new HashSet<>(); + rootStatesView = Collections.unmodifiableSet(rootStates); + + statesAndActivations = new HashMap<>(); + statesView = statesAndActivations.keySet(); + } + + @Override + public synchronized Collection getStates() { + return statesView; + } + + @Override + public synchronized Collection getRoots() { + return rootStatesView; + } + + @Override + public synchronized void addState(Object sourceStateId, Object firedActivationId, Object newStateId) { + + List activationIds = statesAndActivations.get(newStateId); + + if (activationIds == null) { + activationIds = new ArrayList(); + statesAndActivations.put(newStateId, activationIds); + + if (sourceStateId == null) { + rootStates.add(newStateId); + return; + } + } + + activationIds = statesAndActivations.get(sourceStateId); + + if (activationIds == null) { + activationIds = new ArrayList(); + activationIds.add(firedActivationId); + statesAndActivations.put(sourceStateId, activationIds); + } else { + activationIds.add(firedActivationId); + } + } + + public synchronized boolean isTraversed(Object stateId) { + return statesAndActivations.containsKey(stateId); + } + + @Override + public synchronized Collection getActivationIds(Object stateId) { + return statesAndActivations.get(stateId); + } + + @Override + public synchronized Object getRandomActivationId(Object stateId) { + List activations = statesAndActivations.get(stateId); + int index = random.nextInt(activations.size()); + return activations.get(index); + } + + @Override + public synchronized long getNumberOfStates() { + return statesAndActivations.size(); + } + + @Override + public synchronized long getNumberOfTransitions() { + int numberOfTransitions = 0; + for (List activations : statesAndActivations.values()) { + numberOfTransitions += activations.size(); + } + return numberOfTransitions; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/IBacktrackListener.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/IBacktrackListener.java new file mode 100644 index 00000000..5c688276 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/IBacktrackListener.java @@ -0,0 +1,7 @@ +package org.eclipse.viatra.dse.designspace.api; + +public interface IBacktrackListener { + void forwardWorked(long nanos); + + void backtrackWorked(long nanos); +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/IDesignSpace.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/IDesignSpace.java new file mode 100644 index 00000000..a1d64bbf --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/IDesignSpace.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.designspace.api; + +import java.util.Collection; + +public interface IDesignSpace { + + Collection getStates(); + Collection getRoots(); + void addState(Object sourceStateId, Object firedActivationId, Object newStateId); + + boolean isTraversed(Object stateId); + + Collection getActivationIds(Object stateId); + Object getRandomActivationId(Object stateId); + + long getNumberOfStates(); + long getNumberOfTransitions(); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/TrajectoryInfo.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/TrajectoryInfo.java new file mode 100644 index 00000000..acd88416 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/designspace/api/TrajectoryInfo.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.designspace.api; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.api.SolutionTrajectory; +import org.eclipse.viatra.dse.base.DesignSpaceManager; +import org.eclipse.viatra.dse.statecode.IStateCoderFactory; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +public class TrajectoryInfo { + + private final List trajectory; + private final List trajectoryView; + private final List> rules; + private final List> rulesView; + private final List stateIds; + private final List stateIdsView; + private final List> measuredCosts; + + public TrajectoryInfo(Object initialStateId) { + Objects.requireNonNull(initialStateId); + + stateIds = new ArrayList<>(); + stateIds.add(initialStateId); + + trajectory = new ArrayList<>(); + rules = new ArrayList<>(); + measuredCosts = new ArrayList<>(); + + trajectoryView = Collections.unmodifiableList(trajectory); + stateIdsView = Collections.unmodifiableList(stateIds); + rulesView = Collections.unmodifiableList(rules); + } + + /** + * Copy constructor + * + * @since 0.17 + */ + public TrajectoryInfo(TrajectoryInfo other) { + this(other.stateIds, other.trajectory, other.rules, other.measuredCosts); + } + + protected TrajectoryInfo(List stateIds, List trajectory, List> rules, List> measuredCosts) { + + this.stateIds = new ArrayList<>(stateIds); + this.trajectory = new ArrayList<>(trajectory); + this.rules = new ArrayList<>(rules); + trajectoryView = Collections.unmodifiableList(trajectory); + stateIdsView = Collections.unmodifiableList(stateIds); + rulesView = Collections.unmodifiableList(rules); + this.measuredCosts = new ArrayList<>(measuredCosts); + } + + public void addStep(Object activationId, BatchTransformationRule rule, Object newStateId, Map measuredCosts) { + stateIds.add(newStateId); + trajectory.add(activationId); + rules.add(rule); + this.measuredCosts.add(measuredCosts); + } + + public void backtrack() { + int size = trajectory.size(); + + if (size == 0) { + throw new DSEException("Cannot step back any further!"); + } + + trajectory.remove(size - 1); + rules.remove(size - 1); + stateIds.remove(size); + measuredCosts.remove(size - 1); + } + + public Object getInitialStateId() { + return stateIds.get(0); + } + + public Object getCurrentStateId() { + return stateIds.get(stateIds.size() - 1); + } + + public Object getLastActivationId() { + return trajectory.get(trajectory.size() - 1); + } + + public List getTrajectory() { + return trajectoryView; + } + + public List getStateTrajectory() { + return stateIdsView; + } + + public List> getRules() { + return rulesView; + } + + public int getDepth() { + return trajectory.size(); + } + + public List> getMeasuredCosts() { + return measuredCosts; + } + + public SolutionTrajectory createSolutionTrajectory(final IStateCoderFactory stateCoderFactory, final IBacktrackListener listener) { + + List activationIds = new ArrayList<>(trajectory); + List> copiedRules = new ArrayList<>(rules); + + return new SolutionTrajectory(activationIds, copiedRules, stateCoderFactory, listener); + } + + public boolean canStepBack() { + return !trajectory.isEmpty(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Trajectory:\n"); + for (Object activationId : trajectory) { + sb.append(activationId); + sb.append("\n"); + } + return sb.toString(); + } + + /** + * This method is only used by the {@link DesignSpaceManager}. + * @param stateCode + * @return false if the initial state code is the last one, otherwise true. + */ + public boolean modifyLastStateCode(Object stateCode) { + if (stateIds.size() == 1) { + return false; + } + stateIds.set(stateIds.size() - 1, stateCode); + return true; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/multithreading/DSEThreadPool.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/multithreading/DSEThreadPool.java new file mode 100644 index 00000000..17e96a75 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/multithreading/DSEThreadPool.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.multithreading; + +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.DesignSpaceExplorer; +import org.eclipse.viatra.dse.base.ExplorerThread; + +/** + * + * @author Andras Szabolcs Nagy + * + */ +public class DSEThreadPool extends ThreadPoolExecutor { + + private static final long THREAD_KEEP_ALIVE_IN_SECONDS = 60; + + public DSEThreadPool() { + // Based on the Executors.newCachedThreadPool() + super(0, getProcNumber(), THREAD_KEEP_ALIVE_IN_SECONDS, TimeUnit.SECONDS, new SynchronousQueue()); + } + + // helper for constructor + private static int getProcNumber() { + return Runtime.getRuntime().availableProcessors(); + } + + public boolean tryStartNewStrategy(ExplorerThread strategy) { + + if (!canStartNewThread()) { + return false; + } + + try { + submit(strategy); + } catch (RejectedExecutionException e) { + Logger.getLogger(DesignSpaceExplorer.class).info("Couldn't start new thread.", e); + return false; + } + + return true; + } + + public boolean canStartNewThread() { + return getMaximumPoolSize() > getActiveCount(); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/ActivationFitnessProcessor.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/ActivationFitnessProcessor.java new file mode 100644 index 00000000..c06d2916 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/ActivationFitnessProcessor.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives; + +import org.eclipse.viatra.query.runtime.api.IPatternMatch; + +public interface ActivationFitnessProcessor { + public double process(IPatternMatch match); +} \ No newline at end of file diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/Comparators.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/Comparators.java new file mode 100644 index 00000000..b1bd9349 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/Comparators.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives; + +import java.util.Comparator; + +/** + * This helper class holds comparators for objective implementations. + * + * @author Andras Szabolcs Nagy + * + */ +public class Comparators { + + private Comparators() { /*Utility class constructor*/ } + + public static final Comparator HIGHER_IS_BETTER = (o1, o2) -> o1.compareTo(o2); + + public static final Comparator LOWER_IS_BETTER = (o1, o2) -> o2.compareTo(o1); + + private static final Double ZERO = Double.valueOf(0); + + public static final Comparator DIFFERENCE_TO_ZERO_IS_BETTER = (o1, o2) -> ZERO.compareTo(Math.abs(o1)-Math.abs(o2)); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/Fitness.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/Fitness.java new file mode 100644 index 00000000..8beef3bd --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/Fitness.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives; + +import java.util.HashMap; + +public class Fitness extends HashMap{ + + private boolean satisifiesHardObjectives; + + public boolean isSatisifiesHardObjectives() { + return satisifiesHardObjectives; + } + + public void setSatisifiesHardObjectives(boolean satisifiesHardObjectives) { + this.satisifiesHardObjectives = satisifiesHardObjectives; + } + + @Override + public String toString() { + return super.toString() + " hardObjectives=" + satisifiesHardObjectives; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/IGlobalConstraint.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/IGlobalConstraint.java new file mode 100644 index 00000000..2f7f0347 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/IGlobalConstraint.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives; + +import org.eclipse.viatra.dse.base.ThreadContext; + +/** + * + * Implementation of this interface represents a global constraint of the DSE problem, which can halt an exploration + * continuing from a state which dissatisfies the global constraint. + *

+ * Certain global constraints can have inner state for the validation. In this case a new instance is necessary for + * every new thread, and the {@code createNew} method should not return the same instance more than once. + * + * @author Andras Szabolcs Nagy + * + */ +public interface IGlobalConstraint { + + /** + * Returns the name of the global constraint. + * + * @return The name of the global constraint. + */ + String getName(); + + /** + * Checks whether the current state satisfies the global constraint. + * + * @param context + * The {@link ThreadContext} which contains the necessary information. + * @return True if the state is valid and exploration can be continued from the actual state. + */ + boolean checkGlobalConstraint(ThreadContext context); + + /** + * Initializes the global constraint. It is called exactly once for every thread starts. + * + * @param context + * The {@link ThreadContext}. + */ + void init(ThreadContext context); + + /** + * Returns an instance of the {@link IGlobalConstraint}. If it returns the same instance, all the methods has to be + * thread save as they are called concurrently. + * + * @return An instance of the global constraint. + */ + IGlobalConstraint createNew(); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/IObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/IObjective.java new file mode 100644 index 00000000..849dd4e8 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/IObjective.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives; + +import java.util.Comparator; + +import org.eclipse.viatra.dse.base.ThreadContext; + +/** + * + * Implementation of this interface represents a single objective of the DSE problem, which can assess a solution + * (trajectory) in a single number. It has a name and a comparator which orders two solution based on the calculated + * value. + *

+ * Objectives can be either hard or soft objectives. Hard objectives can be satisfied or unsatisfied. If all of the hard + * objectives are satisfied on a single solution, then it is considered to be a valid (or goal) solution. + *

+ * Certain objectives can have inner state for calculating the fitness value. In this case a new instance is necessary + * for every new thread, and the {@code createNew} method should not return the same instance more than once. + * + * @author Andras Szabolcs Nagy + * + */ +public interface IObjective { + + /** + * Returns the name of the objective. + * + * @return The name of the objective. + */ + String getName(); + + /** + * Sets the {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is to + * minimize or maximize (or minimize or maximize a delta from a given number). + * + * @param comparator The comparator. + */ + void setComparator(Comparator comparator); + + /** + * Returns a {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is + * to minimize or maximize (or minimize or maximize a delta from a given number). + * + * @return The comparator. + */ + Comparator getComparator(); + + /** + * Calculates the value of the objective on a given solution (trajectory). + * + * @param context + * The {@link ThreadContext} + * @return The objective value in double. + */ + Double getFitness(ThreadContext context); + + /** + * Initializes the objective. It is called exactly once for every thread starts. + * + * @param context + * The {@link ThreadContext}. + */ + void init(ThreadContext context); + + /** + * Returns an instance of the {@link IObjective}. If it returns the same instance, all the methods has to be thread + * save as they are called concurrently. + * + * @return An instance of the objective. + */ + IObjective createNew(); + + /** + * Returns true if the objective is a hard objective. In such a case the method + * {@link IObjective#satisifiesHardObjective(Double)} is called. + * + * @return True if the objective is a hard objective. + * @see IObjective#satisifiesHardObjective(Double) + * @see IObjective + */ + boolean isHardObjective(); + + /** + * Determines if the given fitness value satisfies the hard objective. + * + * @param fitness + * The fitness value of a solution. + * @return True if it satisfies the hard objective or it is a soft constraint. + * @see IObjective + */ + boolean satisifiesHardObjective(Double fitness); + + /** + * Set the level of the objective. + */ + void setLevel(int level); + + /** + * Gets the level of the objective. + */ + int getLevel(); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/LeveledObjectivesHelper.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/LeveledObjectivesHelper.java new file mode 100644 index 00000000..2d81629b --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/LeveledObjectivesHelper.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class LeveledObjectivesHelper { + + private List objectives = new ArrayList(); + private IObjective[][] leveledObjectives; + + public LeveledObjectivesHelper(List objectives) { + this.objectives = objectives; + } + + public IObjective[][] initLeveledObjectives() { + if (objectives.isEmpty()) { + leveledObjectives = new IObjective[0][0]; + return leveledObjectives; + } + + int level = objectives.get(0).getLevel(); + boolean oneLevelOnly = true; + for (IObjective objective : objectives) { + if (objective.getLevel() != level) { + oneLevelOnly = false; + break; + } + } + + if (oneLevelOnly) { + leveledObjectives = new IObjective[1][objectives.size()]; + for (int i = 0; i < objectives.size(); i++) { + leveledObjectives[0][i] = objectives.get(i); + } + return leveledObjectives; + } + + IObjective[] objectivesArray = getSortedByLevelObjectives(objectives); + + int numberOfLevels = getNumberOfObjectiveLevels(objectivesArray); + + leveledObjectives = new IObjective[numberOfLevels][]; + + fillLeveledObjectives(objectivesArray); + + return leveledObjectives; + } + + private void fillLeveledObjectives(IObjective[] objectivesArray) { + int actLevel = objectivesArray[0].getLevel(); + int levelIndex = 0; + int lastIndex = 0; + int corrigationForLastLevel = 0; + boolean oneObjectiveAtLastLevel = false; + for (int i = 0; i < objectivesArray.length; i++) { + if (i == objectivesArray.length - 1) { + corrigationForLastLevel = 1; + if (objectivesArray[i - 1].getLevel() != objectivesArray[i].getLevel()) { + oneObjectiveAtLastLevel = true; + corrigationForLastLevel = 0; + } + } + if (objectivesArray[i].getLevel() != actLevel || corrigationForLastLevel == 1 || oneObjectiveAtLastLevel) { + leveledObjectives[levelIndex] = new IObjective[i - lastIndex + corrigationForLastLevel]; + for (int j = lastIndex; j < i + corrigationForLastLevel; j++) { + leveledObjectives[levelIndex][j - lastIndex] = objectivesArray[j]; + } + if (oneObjectiveAtLastLevel) { + leveledObjectives[levelIndex + 1] = new IObjective[1]; + leveledObjectives[levelIndex + 1][0] = objectivesArray[i]; + } + actLevel = objectivesArray[i].getLevel(); + levelIndex++; + lastIndex = i; + } + } + } + + private int getNumberOfObjectiveLevels(IObjective[] objectivesArray) { + + int actLevel = objectivesArray[0].getLevel(); + int numberOfLevels = 1; + + for (int i = 1; i < objectivesArray.length; i++) { + if (objectivesArray[i].getLevel() != actLevel) { + numberOfLevels++; + actLevel = objectivesArray[i].getLevel(); + } + } + + return numberOfLevels; + } + + private IObjective[] getSortedByLevelObjectives(List objectives) { + IObjective[] objectivesArray = objectives.toArray(new IObjective[objectives.size()]); + Arrays.sort(objectivesArray, Comparator.comparingInt(IObjective::getLevel)); + return objectivesArray; + } + + public IObjective[][] getLeveledObjectives() { + return leveledObjectives; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/ObjectiveComparatorHelper.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/ObjectiveComparatorHelper.java new file mode 100644 index 00000000..39139bb0 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/ObjectiveComparatorHelper.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; + +/** + * This class is responsible to compare and sort fitness values. {@link TrajectoryFitness} instances can be added to an + * instance of this class, that it can sort them. + * + * @author András Szabolcs Nagy + */ +public class ObjectiveComparatorHelper { + + private IObjective[][] leveledObjectives; + private List trajectoryFitnesses = new ArrayList(); + private Random random = new Random(); + private boolean computeCrowdingDistance = false; + + public ObjectiveComparatorHelper(IObjective[][] leveledObjectives) { + this.leveledObjectives = leveledObjectives; + } + + public void setComputeCrowdingDistance(boolean computeCrowdingDistance) { + this.computeCrowdingDistance = computeCrowdingDistance; + } + + /** + * Compares two fitnesses based on hierarchical dominance. Returns -1 if the second parameter {@code o2} is a better + * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates + * {@code o2}) and returns 0 if they are non-dominating each other. + */ + public int compare(Fitness o1, Fitness o2) { + + levelsLoop: for (int i = 0; i < leveledObjectives.length; i++) { + + boolean o1HasBetterFitness = false; + boolean o2HasBetterFitness = false; + + for (IObjective objective : leveledObjectives[i]) { + String objectiveName = objective.getName(); + int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName)); + + if (sgn < 0) { + o2HasBetterFitness = true; + } + if (sgn > 0) { + o1HasBetterFitness = true; + } + if (o1HasBetterFitness && o2HasBetterFitness) { + continue levelsLoop; + } + } + if (o2HasBetterFitness && !o1HasBetterFitness) { + return -1; + } else if (!o2HasBetterFitness && o1HasBetterFitness) { + return 1; + } + } + + return 0; + + } + + /** + * Adds a {@link TrajectoryFitness} to an inner list to compare later. + * + * @param trajectoryFitness + */ + public void addTrajectoryFitness(TrajectoryFitness trajectoryFitness) { + trajectoryFitnesses.add(trajectoryFitness); + } + + /** + * Clears the inner {@link TrajectoryFitness} list. + */ + public void clearTrajectoryFitnesses() { + trajectoryFitnesses.clear(); + } + + /** + * Returns the inner {@link TrajectoryFitness} list. + */ + public List getTrajectoryFitnesses() { + return trajectoryFitnesses; + } + + /** + * Returns a random {@link TrajectoryFitness} from the pareto front. + */ + public TrajectoryFitness getRandomBest() { + List paretoFront = getParetoFront(); + int randomIndex = random.nextInt(paretoFront.size()); + return paretoFront.get(randomIndex); + } + + /** + * Returns the pareto front of the previously added {@link TrajectoryFitness}. + */ + public List getParetoFront() { + return getFronts().get(0); + } + + /** + * Returns the previously added {@link TrajectoryFitness} instances in fronts. + */ + public List> getFronts() { + Preconditions.checkArgument(!trajectoryFitnesses.isEmpty(), "No trajectory fitnesses were added."); + List> fronts = new ArrayList>(); + + Map> dominatedInstances = new HashMap>(); + Map dominatingInstances = new HashMap(); + + // calculate dominations + for (TrajectoryFitness TrajectoryFitnessP : trajectoryFitnesses) { + dominatedInstances.put(TrajectoryFitnessP, new ArrayList()); + dominatingInstances.put(TrajectoryFitnessP, 0); + + for (TrajectoryFitness TrajectoryFitnessQ : trajectoryFitnesses) { + int dominates = compare(TrajectoryFitnessP.fitness, TrajectoryFitnessQ.fitness); + if (dominates > 0) { + dominatedInstances.get(TrajectoryFitnessP).add(TrajectoryFitnessQ); + } else if (dominates < 0) { + dominatingInstances.put(TrajectoryFitnessP, dominatingInstances.get(TrajectoryFitnessP) + 1); + } + } + + if (dominatingInstances.get(TrajectoryFitnessP) == 0) { + // p belongs to the first front + TrajectoryFitnessP.rank = 1; + if (fronts.isEmpty()) { + ArrayList firstDominationFront = new ArrayList(); + firstDominationFront.add(TrajectoryFitnessP); + fronts.add(firstDominationFront); + } else { + List firstDominationFront = fronts.get(0); + firstDominationFront.add(TrajectoryFitnessP); + } + } + } + + // create fronts + int i = 1; + while (fronts.size() == i) { + ArrayList nextDominationFront = new ArrayList(); + for (TrajectoryFitness TrajectoryFitnessP : fronts.get(i - 1)) { + for (TrajectoryFitness TrajectoryFitnessQ : dominatedInstances.get(TrajectoryFitnessP)) { + dominatingInstances.put(TrajectoryFitnessQ, dominatingInstances.get(TrajectoryFitnessQ) - 1); + if (dominatingInstances.get(TrajectoryFitnessQ) == 0) { + TrajectoryFitnessQ.rank = i + 1; + nextDominationFront.add(TrajectoryFitnessQ); + } + } + } + i++; + if (!nextDominationFront.isEmpty()) { + if (computeCrowdingDistance) { + crowdingDistanceAssignment(nextDominationFront, leveledObjectives); + } + fronts.add(nextDominationFront); + } + } + + return fronts; + } + + /** + * Executes the crowding distance assignment for the specified front. + * + * @param front + */ + public static void crowdingDistanceAssignment(List front, IObjective[][] leveledObjectives) { + + for (TrajectoryFitness InstanceData : front) { + // initialize crowding distance + InstanceData.crowdingDistance = 0; + } + + for (final IObjective[] objectives : leveledObjectives) { + for (final IObjective objective : objectives) { + + final String m = objective.getName(); + TrajectoryFitness[] sortedFront = front.toArray(new TrajectoryFitness[0]); + // sort using m-th objective value + Arrays.sort(sortedFront, (o1, o2) -> objective.getComparator().compare(o1.fitness.get(m), o2.fitness.get(m))); + // so that boundary points are always selected + sortedFront[0].crowdingDistance = Double.POSITIVE_INFINITY; + sortedFront[sortedFront.length - 1].crowdingDistance = Double.POSITIVE_INFINITY; + // If minimal and maximal fitness value for this objective are + // equal, then do not change crowding distance + if (sortedFront[0].fitness.get(m) != sortedFront[sortedFront.length - 1].fitness.get(m)) { + for (int i = 1; i < sortedFront.length - 1; i++) { + double newCrowdingDistance = sortedFront[i].crowdingDistance; + newCrowdingDistance += (sortedFront[i + 1].fitness.get(m) - sortedFront[i - 1].fitness.get(m)) + / (sortedFront[sortedFront.length - 1].fitness.get(m) - sortedFront[0].fitness.get(m)); + + sortedFront[i].crowdingDistance = newCrowdingDistance; + } + } + } + } + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/TrajectoryFitness.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/TrajectoryFitness.java new file mode 100644 index 00000000..f783afac --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/TrajectoryFitness.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.viatra.dse.designspace.api.TrajectoryInfo; + +/** + * This class represents a trajectory and its fitness. + * @author Andras Szabolcs Nagy + * + */ +public class TrajectoryFitness { + + public Object[] trajectory; + public Fitness fitness; + + public int rank; + public double crowdingDistance; + + private int hash; + + public int survive; + + /** + * Creates a {@link TrajectoryFitness} with the full trajectory. + * @param trajectory The trajectory. + * @param fitness The fitness. + */ + public TrajectoryFitness(Object[] trajectory, Fitness fitness) { + this.fitness = fitness; + this.trajectory = trajectory; + } + + /** + * Creates a {@link TrajectoryFitness} with the full trajectory. + * @param trajectoryInfo The trajectory. + * @param fitness The fitness. + */ + public TrajectoryFitness(TrajectoryInfo trajectoryInfo, Fitness fitness) { + this.fitness = fitness; + List fullTraj = trajectoryInfo.getTrajectory(); + trajectory = fullTraj.toArray(new Object[fullTraj.size()]); + } + + /** + * Creates a {@link TrajectoryFitness} with the given activation id} + * @param transition The transition. + * @param fitness The fitness. + */ + public TrajectoryFitness(Object transition, Fitness fitness) { + this.fitness = fitness; + trajectory = new Object[] {transition}; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TrajectoryFitness) { + return Arrays.equals(trajectory, ((TrajectoryFitness) obj).trajectory); + } + return false; + } + + @Override + public int hashCode() { + if (hash == 0 && trajectory.length > 0) { + hash = Arrays.hashCode(trajectory); + } + return hash; + } + + @Override + public String toString() { + return Arrays.toString(trajectory) + fitness.toString(); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/AlwaysSatisfiedDummyHardObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/AlwaysSatisfiedDummyHardObjective.java new file mode 100644 index 00000000..9898a3b5 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/AlwaysSatisfiedDummyHardObjective.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.IObjective; + +/** + * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. + * + * @author Andras Szabolcs Nagy + * + */ +public class AlwaysSatisfiedDummyHardObjective extends BaseObjective { + + private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; + + public AlwaysSatisfiedDummyHardObjective() { + super(DEFAULT_NAME); + } + + public AlwaysSatisfiedDummyHardObjective(String name) { + super(name); + } + + @Override + public Double getFitness(ThreadContext context) { + return 0d; + } + + @Override + public boolean isHardObjective() { + return true; + } + + @Override + public boolean satisifiesHardObjective(Double fitness) { + return true; + } + + @Override + public IObjective createNew() { + return this; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/BaseObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/BaseObjective.java new file mode 100644 index 00000000..0a1de875 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/BaseObjective.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import java.util.Comparator; +import java.util.Objects; + +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.Comparators; +import org.eclipse.viatra.dse.objectives.IObjective; + +/** + * This abstract class implements the basic functionality of an objective ({@link IObjective} namely its name, + * comparator, level and fitness hard constraint. + * + * @author Andras Szabolcs Nagy + * + */ +public abstract class BaseObjective implements IObjective { + + protected final String name; + protected Comparator comparator = Comparators.HIGHER_IS_BETTER; + protected int level = 0; + + protected double fitnessConstraint; + protected boolean isThereFitnessConstraint = false; + protected Comparator fitnessConstraintComparator; + + public BaseObjective(String name) { + Objects.requireNonNull(name, "Name of the objective cannot be null."); + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setComparator(Comparator comparator) { + this.comparator = comparator; + } + + @Override + public Comparator getComparator() { + return comparator; + } + + @Override + public void setLevel(int level) { + this.level = level; + } + + @Override + public int getLevel() { + return level; + } + + public BaseObjective withLevel(int level) { + setLevel(level); + return this; + } + + public BaseObjective withComparator(Comparator comparator) { + setComparator(comparator); + return this; + } + + /** + * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the + * current state as a solution. + * + * @param fitnessConstraint + * Solutions should be better than this value. + * @param fitnessConstraintComparator + * {@link Comparator} to determine if the current state is better than the given value. + * @return The actual instance to enable builder pattern like usage. + */ + public BaseObjective withHardConstraintOnFitness(double fitnessConstraint, + Comparator fitnessConstraintComparator) { + this.fitnessConstraint = fitnessConstraint; + this.fitnessConstraintComparator = fitnessConstraintComparator; + this.isThereFitnessConstraint = true; + return this; + } + + /** + * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the + * current state as a solution. The provided comparator will be used. + * + * @param fitnessConstraint + * Solutions should be better than this value. + * @return The actual instance to enable builder pattern like usage. + */ + public BaseObjective withHardConstraintOnFitness(double fitnessConstraint) { + return withHardConstraintOnFitness(fitnessConstraint, null); + } + + @Override + public void init(ThreadContext context) { + if (fitnessConstraintComparator == null) { + fitnessConstraintComparator = comparator; + } + } + + @Override + public boolean isHardObjective() { + return isThereFitnessConstraint; + } + + @Override + public boolean satisifiesHardObjective(Double fitness) { + if (isThereFitnessConstraint) { + int compare = fitnessConstraintComparator.compare(fitness, fitnessConstraint); + if (compare < 0) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BaseObjective) { + BaseObjective baseObjective = (BaseObjective) obj; + return name.equals(baseObjective.getName()); + } + return false; + } + + @Override + public String toString() { + return name; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/CompositeObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/CompositeObjective.java new file mode 100644 index 00000000..cc48d22e --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/CompositeObjective.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.IObjective; +import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; + +/** + * This objective collects a list of other objectives. It returns the weighted sum of the objectives. + * + * @author Andras Szabolcs Nagy + * + */ +public class CompositeObjective extends BaseObjective { + + public static final String DEFAULT_NAME = "CompositeObjective"; + protected List objectives; + protected List weights; + protected boolean hardObjective; + + public CompositeObjective(String name, List objectives, List weights) { + super(name); + Objects.requireNonNull(objectives, "The list of objectives cannot be null."); + Objects.requireNonNull(weights, "The list of weights cannot be null."); + Preconditions.checkState(objectives.size() == weights.size(), "The size of the objectives and weights must match."); + this.objectives = objectives; + this.weights = weights; + } + + public CompositeObjective(List objectives, List weights) { + this(DEFAULT_NAME, objectives, weights); + } + + public CompositeObjective(String name) { + this(name, new ArrayList(), new ArrayList()); + } + + public CompositeObjective() { + this(DEFAULT_NAME, new ArrayList(), new ArrayList()); + } + + /** + * Adds a new objective. + * + * @param objective + * @return The actual instance to enable builder pattern like usage. + */ + public CompositeObjective withObjective(IObjective objective) { + objectives.add(objective); + weights.add(1d); + return this; + } + + /** + * Adds a new objective. + * + * @param objective + * @return The actual instance to enable builder pattern like usage. + */ + public CompositeObjective withObjective(IObjective objective, double weight) { + objectives.add(objective); + weights.add(weight); + return this; + } + + @Override + public Double getFitness(ThreadContext context) { + + double result = 0; + + for (int i = 0; i < objectives.size(); i++) { + IObjective objective = objectives.get(i); + Double weight = weights.get(i); + result += objective.getFitness(context) * weight; + } + return result; + } + + @Override + public void init(ThreadContext context) { + super.init(context); + hardObjective = false; + for (IObjective objective : objectives) { + objective.init(context); + if (objective.isHardObjective()) { + hardObjective = true; + } + } + } + + @Override + public IObjective createNew() { + + List newObjectives = new ArrayList(); + + for (IObjective objective : objectives) { + newObjectives.add(objective.createNew()); + } + + CompositeObjective objective = new CompositeObjective(name, newObjectives, weights); + if (isThereFitnessConstraint) { + objective.withHardConstraintOnFitness(fitnessConstraint, fitnessConstraintComparator); + } + + return objective.withComparator(comparator).withLevel(level); + } + + @Override + public boolean isHardObjective() { + return hardObjective; + } + + @Override + public boolean satisifiesHardObjective(Double fitness) { + + boolean hardObjectiveSatisfied = true; + + for (IObjective objective : objectives) { + hardObjectiveSatisfied = objective.satisifiesHardObjective(fitness) ? hardObjectiveSatisfied : false; + } + + hardObjectiveSatisfied = super.satisifiesHardObjective(fitness) ? hardObjectiveSatisfied : false; + + return hardObjectiveSatisfied; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ConstraintsObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ConstraintsObjective.java new file mode 100644 index 00000000..77d416f5 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ConstraintsObjective.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.IObjective; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; +import org.eclipse.viatra.query.runtime.api.IQuerySpecification; +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; +import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher; +import org.eclipse.viatra.query.runtime.exception.ViatraQueryException; + +/** + * This objective serves as soft and as hard objective at the same time by defining two lists of VIATRA Query + * specifications. + * + * As a soft objective, it collects a list of VIATRA Query specifications, which have predefined weights. Then the + * fitness value of an arbitrary solution is calculated in the following way: + *

+ * fitness = sum( pattern[i].countMatches() * weight[i] ) + *

+ * As a hard objective it collects a separate list of VIATRA Query specifications. If every one of them has a match the + * hard constraint is considered to be fulfilled. + * + * @author Andras Szabolcs Nagy + * @see IObjective + * + */ +public class ConstraintsObjective extends BaseObjective { + + public static final String DEFAULT_NAME = "ConstraintsObjective"; + + public static class QueryConstraint { + public final String name; + public final IQuerySpecification> query; + public final Double weight; + public final ModelQueryType type; + + public QueryConstraint(String name, + IQuerySpecification> query, Double weight, + ModelQueryType type) { + this.name = name; + this.query = query; + this.weight = weight; + this.type = type; + } + + public QueryConstraint(String name, + IQuerySpecification> query, Double weight) { + this(name, query, weight, ModelQueryType.MUST_HAVE_MATCH); + } + + public QueryConstraint(String name, + IQuerySpecification> query, ModelQueryType type) { + this(name, query, 0d, type); + } + } + + protected List softConstraints; + protected List hardConstraints; + + protected List> softMatchers; + protected List> hardMatchers; + protected List softMatches; + protected List hardMatches; + + public ConstraintsObjective(String name, List softConstraints, + List hardConstraints) { + super(name); + Objects.requireNonNull(softConstraints, "The list of soft constraints cannot be null."); + Objects.requireNonNull(hardConstraints, "The list of hard constraints cannot be null."); + + this.softConstraints = softConstraints; + this.hardConstraints = hardConstraints; + } + + public ConstraintsObjective(String name, List hardConstraints) { + this(name, new ArrayList(), hardConstraints); + } + + public ConstraintsObjective(List hardConstraints) { + this(DEFAULT_NAME, new ArrayList(), hardConstraints); + } + + public ConstraintsObjective(String name) { + this(name, new ArrayList(), new ArrayList()); + } + + public ConstraintsObjective() { + this(DEFAULT_NAME, new ArrayList(), new ArrayList()); + } + + /** + * Adds a new soft constraint. + * + * @param name + * A name for the soft constraint. + * @param softConstraint + * A VIATRA Query pattern specification. + * @param weight + * The weight of the pattern. + * @return The actual instance to enable builder pattern like usage. + */ + public ConstraintsObjective withSoftConstraint(String name, + IQuerySpecification> softConstraint, double weight) { + softConstraints.add(new QueryConstraint(name, softConstraint, weight)); + return this; + } + + /** + * Adds a new soft constraint with the name of the query specification's fully qualified name. + * + * @param softConstraint + * A VIATRA Query pattern specification. + * @param weight + * The weight of the pattern. + * @return The actual instance to enable builder pattern like usage. + */ + public ConstraintsObjective withSoftConstraint( + IQuerySpecification> softConstraint, double weight) { + return withSoftConstraint(softConstraint.getFullyQualifiedName(), softConstraint, weight); + } + + /** + * Adds a new hard constraint. + * + * @param name + * A name for the hard constraint. + * @param softConstraint + * A VIATRA Query pattern specification. + * @param type + * {@link ModelQueryType}, which determines whether the constraint should have at least one match or none + * at all. + * @return The actual instance to enable builder pattern like usage. + */ + public ConstraintsObjective withHardConstraint(String name, + IQuerySpecification> hardConstraint, + ModelQueryType type) { + hardConstraints.add(new QueryConstraint(name, hardConstraint, type)); + return this; + } + + /** + * Adds a new hard constraint with the default {@link ModelQueryType#MUST_HAVE_MATCH}. + * + * @param name + * A name for the hard constraint. + * @param softConstraint + * A VIATRA Query pattern specification. + * @return The actual instance to enable builder pattern like usage. + */ + public ConstraintsObjective withHardConstraint(String name, + IQuerySpecification> hardConstraint) { + hardConstraints.add(new QueryConstraint(name, hardConstraint, ModelQueryType.MUST_HAVE_MATCH)); + return this; + } + + /** + * Adds a new hard constraint with the name of the query specification's fully qualified name and the default + * {@link ModelQueryType#MUST_HAVE_MATCH}. + * + * @param softConstraint + * A VIATRA Query pattern specification. + * @return The actual instance to enable builder pattern like usage. + */ + public ConstraintsObjective withHardConstraint( + IQuerySpecification> hardConstraint) { + return withHardConstraint(hardConstraint.getFullyQualifiedName(), hardConstraint, + ModelQueryType.MUST_HAVE_MATCH); + } + + /** + * Adds a new hard constraint with the name of the query specification's fully qualified name. + * + * @param softConstraint + * A VIATRA Query pattern specification. + * @param type + * {@link ModelQueryType}, which determines whether the constraint should have at least one match or none + * at all. + * @return The actual instance to enable builder pattern like usage. + */ + public ConstraintsObjective withHardConstraint( + IQuerySpecification> hardConstraint, + ModelQueryType type) { + return withHardConstraint(hardConstraint.getFullyQualifiedName(), hardConstraint, type); + } + + @Override + public Double getFitness(ThreadContext context) { + + if (softConstraints.isEmpty()) { + return 0d; + } + + double result = 0; + + for (int i = 0; i < softConstraints.size(); i++) { + int countMatches = softMatchers.get(i).countMatches(); + result += countMatches * softConstraints.get(i).weight; + softMatches.set(i, Integer.valueOf(countMatches)); + } + + return result; + } + + @Override + public void init(ThreadContext context) { + + super.init(context); + + softMatches = new ArrayList(softConstraints.size()); + softMatchers = new ArrayList>(softConstraints.size()); + hardMatches = new ArrayList(hardConstraints.size()); + hardMatchers = new ArrayList>(hardConstraints.size()); + for (int i = 0; i < softConstraints.size(); i++) { + softMatches.add(0); + } + for (int i = 0; i < hardConstraints.size(); i++) { + hardMatches.add(0); + } + + try { + ViatraQueryEngine queryEngine = context.getQueryEngine(); + + for (QueryConstraint qc : softConstraints) { + softMatchers.add(qc.query.getMatcher(queryEngine)); + } + + for (QueryConstraint qc : hardConstraints) { + hardMatchers.add(qc.query.getMatcher(queryEngine)); + } + + } catch (ViatraQueryException e) { + throw new DSEException("Couldn't initialize the VIATRA Query matcher, see inner exception", e); + } + } + + @Override + public IObjective createNew() { + new ArrayList(softConstraints.size()); + ConstraintsObjective result = new ConstraintsObjective(name, softConstraints, hardConstraints); + if (isThereFitnessConstraint) { + result.withHardConstraintOnFitness(fitnessConstraint, fitnessConstraintComparator); + } + return result.withComparator(comparator).withLevel(level); + } + + @Override + public boolean isHardObjective() { + return !hardConstraints.isEmpty() || super.isHardObjective(); + } + + @Override + public boolean satisifiesHardObjective(Double fitness) { + + boolean result = true; + + for (int i = 0; i < hardConstraints.size(); i++) { + ModelQueryType type = hardConstraints.get(i).type; + int countMatches = hardMatchers.get(i).countMatches(); + hardMatches.set(i, Integer.valueOf(countMatches)); + if ((type.equals(ModelQueryType.MUST_HAVE_MATCH) && countMatches <= 0) + || (type.equals(ModelQueryType.NO_MATCH) && countMatches > 0)) { + result = false; + } + } + + result = super.satisifiesHardObjective(fitness) ? result : false; + + return result; + } + + public List getSoftConstraints() { + return softConstraints; + } + + public List getHardConstraints() { + return hardConstraints; + } + + public String getSoftName(int index) { + return softConstraints.get(index).name; + } + + public String getHardName(int index) { + return hardConstraints.get(index).name; + } + + public List getSoftMatches() { + return softMatches; + } + + public List getHardMatches() { + return hardMatches; + } + + public List getSoftNames() { + List softNames = new ArrayList<>(softConstraints.size()); + for (QueryConstraint qc : softConstraints) { + softNames.add(qc.name); + } + return softNames; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/DepthHardObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/DepthHardObjective.java new file mode 100644 index 00000000..b21da397 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/DepthHardObjective.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.IObjective; + +/** + * This hard objective is fulfilled if the trajectory is in the specified interval (inclusive). + * + * @author Andras Szabolcs Nagy + * + */ +public class DepthHardObjective extends BaseObjective { + + private static final String DEFAULT_NAME = "DepthHardObjective"; + protected int minDepth; + protected int maxDepth; + private ThreadContext context; + + public DepthHardObjective() { + this(DEFAULT_NAME, 0, Integer.MAX_VALUE); + } + + public DepthHardObjective(String name) { + this(name, 0, Integer.MAX_VALUE); + } + + public DepthHardObjective(int minDepth) { + this(DEFAULT_NAME, minDepth, Integer.MAX_VALUE); + } + + public DepthHardObjective(String name, int minDepth) { + this(name, minDepth, Integer.MAX_VALUE); + } + + public DepthHardObjective(int minDepth, int maxDepth) { + this(DEFAULT_NAME, minDepth, maxDepth); + } + + public DepthHardObjective(String name, int minDepth, int maxDepth) { + super(name); + this.minDepth = minDepth; + this.maxDepth = maxDepth; + } + + public DepthHardObjective withMinDepth(int minDepth) { + this.minDepth = minDepth; + return this; + } + + public DepthHardObjective withMaxDepth(int maxDepth) { + this.maxDepth = maxDepth; + return this; + } + + @Override + public void init(ThreadContext context) { + super.init(context); + this.context = context; + } + + @Override + public Double getFitness(ThreadContext context) { + return 0d; + } + + @Override + public boolean isHardObjective() { + return true; + } + + @Override + public boolean satisifiesHardObjective(Double fitness) { + return minDepth <= context.getDepth() && context.getDepth() <= maxDepth; + } + + @Override + public IObjective createNew() { + return new DepthHardObjective(name, minDepth, maxDepth); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ModelQueriesGlobalConstraint.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ModelQueriesGlobalConstraint.java new file mode 100644 index 00000000..7616b4a2 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ModelQueriesGlobalConstraint.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.IGlobalConstraint; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; +import org.eclipse.viatra.query.runtime.api.IQuerySpecification; +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; +import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher; +import org.eclipse.viatra.query.runtime.exception.ViatraQueryException; + +/** + * This global constraint collects a list of VIATRA Query pattern and checks if any of them has a match on along a trajectory. + * If any of the patterns has a match then it is unsatisfied and the exploration should backtrack. + * + * @author Andras Szabolcs Nagy + * + */ +public class ModelQueriesGlobalConstraint implements IGlobalConstraint { + + public static final String GLOBAL_CONSTRAINT = "GlobalConstraint"; + protected String name; + protected List>> constraints; + protected List> matchers = new ArrayList>(); + protected ModelQueryType type = ModelQueryType.NO_MATCH; + + public ModelQueriesGlobalConstraint(String name, + List>> constraints) { + Objects.requireNonNull(name, "Name of the global constraint cannot be null."); + Objects.requireNonNull(constraints, "The list of constraints cannot be null."); + + this.name = name; + this.constraints = constraints; + } + + public ModelQueriesGlobalConstraint( + List>> constraints) { + this(GLOBAL_CONSTRAINT, constraints); + } + + public ModelQueriesGlobalConstraint(String name) { + this(name, new ArrayList>>()); + } + + public ModelQueriesGlobalConstraint() { + this(GLOBAL_CONSTRAINT, + new ArrayList>>()); + } + + /** + * Adds a new VIATRA Query pattern. + * + * @param constraint + * A VIATRA Query pattern. + * @return The actual instance to enable builder pattern like usage. + */ + public ModelQueriesGlobalConstraint withConstraint( + IQuerySpecification> constraint) { + constraints.add(constraint); + return this; + } + + public ModelQueriesGlobalConstraint withType(ModelQueryType type) { + this.type = type; + return this; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean checkGlobalConstraint(ThreadContext context) { + for (ViatraQueryMatcher matcher : matchers) { + if ((type.equals(ModelQueryType.NO_MATCH) && matcher.countMatches() > 0) + || (type.equals(ModelQueryType.MUST_HAVE_MATCH) && matcher.countMatches() == 0)) { + return false; + } + } + return true; + } + + @Override + public void init(ThreadContext context) { + try { + ViatraQueryEngine queryEngine = context.getQueryEngine(); + + for (IQuerySpecification> querySpecification : constraints) { + ViatraQueryMatcher matcher = querySpecification.getMatcher(queryEngine); + matchers.add(matcher); + } + + } catch (ViatraQueryException e) { + throw new DSEException("Couldn't get the VIATRA Query matcher, see inner exception", e); + } + } + + @Override + public IGlobalConstraint createNew() { + return new ModelQueriesGlobalConstraint(name, constraints); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ModelQueryType.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ModelQueryType.java new file mode 100644 index 00000000..76390352 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/ModelQueryType.java @@ -0,0 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +public enum ModelQueryType { + MUST_HAVE_MATCH, + NO_MATCH +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/NeverSatisfiedDummyHardObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/NeverSatisfiedDummyHardObjective.java new file mode 100644 index 00000000..27cf139c --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/NeverSatisfiedDummyHardObjective.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.IObjective; + +/** + * This hard objective is never fulfilled. Use it if all states should be regarded as an invalid solution. + * + * @author Andras Szabolcs Nagy + * + */ +public class NeverSatisfiedDummyHardObjective extends BaseObjective { + + private static final String DEFAULT_NAME = "NeverSatisfiedDummyHardObjective"; + + public NeverSatisfiedDummyHardObjective() { + super(DEFAULT_NAME); + } + + public NeverSatisfiedDummyHardObjective(String name) { + super(name); + } + + @Override + public Double getFitness(ThreadContext context) { + return 0d; + } + + @Override + public boolean isHardObjective() { + return true; + } + + @Override + public boolean satisifiesHardObjective(Double fitness) { + return false; + } + + @Override + public IObjective createNew() { + return this; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/NoRuleActivationsHardObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/NoRuleActivationsHardObjective.java new file mode 100644 index 00000000..756d94ec --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/NoRuleActivationsHardObjective.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.IObjective; + +/** + * This hard objective is satisfied if there are no rule activations from the current state (returning 1 in this case). + * + * @author Andras Szabolcs Nagy + * + */ +public class NoRuleActivationsHardObjective extends BaseObjective { + + protected static final String DEFAULT_NAME = "NoMoreActivationHardObjective"; + private ThreadContext context; + + public NoRuleActivationsHardObjective(String name) { + super(name); + } + + public NoRuleActivationsHardObjective() { + this(DEFAULT_NAME); + } + + @Override + public Double getFitness(ThreadContext context) { + return 0d; + } + + @Override + public void init(ThreadContext context) { + super.init(context); + this.context = context; + } + + @Override + public IObjective createNew() { + return new NoRuleActivationsHardObjective(name); + } + + @Override + public boolean isHardObjective() { + return true; + } + + @Override + public boolean satisifiesHardObjective(Double fitness) { + return context.getConflictSet().getNextActivations().isEmpty(); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/TrajectoryCostSoftObjective.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/TrajectoryCostSoftObjective.java new file mode 100644 index 00000000..25ff45ae --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/objectives/impl/TrajectoryCostSoftObjective.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.objectives.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + +import org.eclipse.viatra.dse.base.DesignSpaceManager; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.designspace.api.TrajectoryInfo; +import org.eclipse.viatra.dse.objectives.ActivationFitnessProcessor; +import org.eclipse.viatra.dse.objectives.Comparators; +import org.eclipse.viatra.dse.objectives.IObjective; +import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; +import org.eclipse.viatra.transformation.runtime.emf.rules.batch.BatchTransformationRule; + +/** + * This soft objective calculates a fitness value based on the length of the trajectory. Costs to the rules can be + * assigned. + * + * @author Andras Szabolcs Nagy + * + */ +public class TrajectoryCostSoftObjective extends BaseObjective { + + public static final String DEFAULT_NAME = "TrajectoryCostObjective"; + protected Map, Double> fixCosts; + protected Map, ActivationFitnessProcessor> activationCostProcessors; + protected double trajectoryLengthWeight = 0.0; + protected boolean calculateTrajectoryLengthWeight; + + public TrajectoryCostSoftObjective(String name) { + super(name); + comparator = Comparators.LOWER_IS_BETTER; + } + + public TrajectoryCostSoftObjective() { + this(DEFAULT_NAME); + } + + /** + * Sets the cost of a rule. + * + * @param rule + * @param cost + * @return The actual instance to enable builder pattern like usage. + */ + public TrajectoryCostSoftObjective withRuleCost(BatchTransformationRule rule, double cost) { + Objects.requireNonNull(rule); + if (fixCosts == null) { + fixCosts = new HashMap, Double>(); + } + Preconditions.checkArgument(!fixCosts.containsKey(rule)); + fixCosts.put(rule, cost); + return this; + } + + /** + * Sets an activation processor for a rule. + * + * @param rule + * @param activationCostProcessor + * @return The actual instance to enable builder pattern like usage. + */ + public TrajectoryCostSoftObjective withActivationCost(BatchTransformationRule rule, + ActivationFitnessProcessor activationCostProcessor) { + Objects.requireNonNull(rule); + Objects.requireNonNull(activationCostProcessor); + if (activationCostProcessors == null) { + activationCostProcessors = new HashMap, ActivationFitnessProcessor>(); + } + Preconditions.checkArgument(!activationCostProcessors.containsKey(rule)); + activationCostProcessors.put(rule, activationCostProcessor); + return this; + } + + /** + * The length of the trajectory multiplied with given parameter will be added to the fitness value. + * + * @param trajectoryLengthWeight + * The weight of a transformation rule application. + * @return The actual instance to enable builder pattern like usage. + */ + public TrajectoryCostSoftObjective withTrajectoryLengthWeight(double trajectoryLengthWeight) { + this.trajectoryLengthWeight = trajectoryLengthWeight; + this.calculateTrajectoryLengthWeight = true; + return this; + } + + @Override + public Double getFitness(ThreadContext context) { + + DesignSpaceManager dsm = context.getDesignSpaceManager(); + TrajectoryInfo trajectoryInfo = dsm.getTrajectoryInfo(); + List trajectory = trajectoryInfo.getTrajectory(); + List> rules = trajectoryInfo.getRules(); + + double result = 0; + + for (int i = 0; i < trajectory.size(); i++) { + BatchTransformationRule rule = rules.get(i); + + Double cost = fixCosts.get(rule); + if (cost != null) { + result += cost; + } + + Map costs = trajectoryInfo.getMeasuredCosts().get(i); + if (costs != null) { + cost = costs.get(name); + if (cost != null) { + result += cost; + } + } + } + + if (calculateTrajectoryLengthWeight) { + result += trajectory.size() * trajectoryLengthWeight; + } + + return result; + } + + @Override + public void init(ThreadContext context) { + super.init(context); + DesignSpaceManager dsm = context.getDesignSpaceManager(); + if (activationCostProcessors != null) { + for (Entry, ActivationFitnessProcessor> entry : activationCostProcessors.entrySet()) { + dsm.registerActivationCostProcessor(name, entry.getKey(), entry.getValue()); + } + } + } + + @Override + public IObjective createNew() { + return this; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ISolutionFoundHandler.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ISolutionFoundHandler.java new file mode 100644 index 00000000..8d74e856 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ISolutionFoundHandler.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.solutionstore; + +import org.eclipse.viatra.dse.api.SolutionTrajectory; +import org.eclipse.viatra.dse.api.strategy.interfaces.IStrategy; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.solutionstore.SolutionStore.ISolutionSaver; + +/** + * Contains callback methods which are called when a solution is found by the exploration {@link IStrategy}. + * + * @author Andras Szabolcs Nagy + * + */ +public interface ISolutionFoundHandler { + + /** + * Called when a solution is saved by the {@link ISolutionSaver}. Later, this solution can be omitted from the final + * set of solutions. + * + * @param context + * @param trajectory + */ + void solutionFound(ThreadContext context, SolutionTrajectory trajectory); + + /** + * Called when the exploration found a solution but it was not saved because of certain conditions. + * + * @param context + * @param trajectory + */ + void solutionTriedToSave(ThreadContext context, SolutionTrajectory trajectory); +} \ No newline at end of file diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ISolutionNameProvider.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ISolutionNameProvider.java new file mode 100644 index 00000000..36e6b5b7 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ISolutionNameProvider.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.solutionstore; + +/** + * Provides file name when a model is searialzed. + * @author Andras Szabolcs Nagy + * + */ +public interface ISolutionNameProvider { + String getName(); +} \ No newline at end of file diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/IdBasedSolutionNameProvider.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/IdBasedSolutionNameProvider.java new file mode 100644 index 00000000..43460015 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/IdBasedSolutionNameProvider.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.solutionstore; + +/** + * Provides file name with a String [prefix][id].[extension] pattern. + * @author Andras Szabolcs Nagy + * + */ +public class IdBasedSolutionNameProvider implements ISolutionNameProvider { + + private int id = 1; + private String prefix; + private String extension; + + public IdBasedSolutionNameProvider(String prefix, String extension) { + this.extension = extension; + this.prefix = prefix; + + } + + @Override + public String getName() { + StringBuilder sb = new StringBuilder(prefix); + sb.append(id++); + sb.append('.'); + sb.append(extension); + return sb.toString(); + } + +} \ No newline at end of file diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/LogSolutionHandler.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/LogSolutionHandler.java new file mode 100644 index 00000000..118f0c75 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/LogSolutionHandler.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.solutionstore; + +import org.apache.log4j.Logger; +import org.eclipse.viatra.dse.api.SolutionTrajectory; +import org.eclipse.viatra.dse.base.ThreadContext; + +public class LogSolutionHandler implements ISolutionFoundHandler { + + Logger logger = Logger.getLogger(LogSolutionHandler.class); + + @Override + public void solutionFound(ThreadContext context, SolutionTrajectory trajectory) { + logger.info("Solution registered: " + trajectory.toPrettyString()); + } + + @Override + public void solutionTriedToSave(ThreadContext context, SolutionTrajectory trajectory) { + logger.debug("Not good enough solution: " + trajectory.toPrettyString()); + } +} \ No newline at end of file diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ModelSaverSolutionFoundHandler.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ModelSaverSolutionFoundHandler.java new file mode 100644 index 00000000..bbbe60de --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/ModelSaverSolutionFoundHandler.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.solutionstore; + +import java.util.HashSet; + +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.viatra.dse.api.SolutionTrajectory; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.util.EMFHelper; + +public class ModelSaverSolutionFoundHandler implements ISolutionFoundHandler { + + private HashSet savedSolutions = new HashSet(); + private ISolutionNameProvider solutionNameProvider; + + public ModelSaverSolutionFoundHandler() { + solutionNameProvider = new IdBasedSolutionNameProvider("solution", "xmi"); + } + + public ModelSaverSolutionFoundHandler(String extension) { + solutionNameProvider = new IdBasedSolutionNameProvider("solution", extension); + } + + public ModelSaverSolutionFoundHandler(String prefix, String extension) { + solutionNameProvider = new IdBasedSolutionNameProvider(prefix, extension); + } + + public ModelSaverSolutionFoundHandler(ISolutionNameProvider solutionNameProvider) { + this.solutionNameProvider = solutionNameProvider; + } + + @Override + public void solutionTriedToSave(ThreadContext context, SolutionTrajectory trajectory) { + } + + @Override + public void solutionFound(ThreadContext context, SolutionTrajectory trajectory) { + Object stateCode = trajectory.getSolution().getStateCode(); + + if (savedSolutions.contains(stateCode)) { + return; + } + + savedSolutions.add(stateCode); + Notifier clonedModel = EMFHelper.clone(context.getModel()); + EMFHelper.saveModel(clonedModel, solutionNameProvider.getName()); + } +} \ No newline at end of file diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/SolutionStore.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/SolutionStore.java new file mode 100644 index 00000000..578ae277 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/solutionstore/SolutionStore.java @@ -0,0 +1,311 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.solutionstore; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.api.Solution; +import org.eclipse.viatra.dse.api.SolutionTrajectory; +import org.eclipse.viatra.dse.base.DesignSpaceManager; +import org.eclipse.viatra.dse.base.ThreadContext; +import org.eclipse.viatra.dse.objectives.Fitness; +import org.eclipse.viatra.dse.objectives.ObjectiveComparatorHelper; +import org.eclipse.viatra.dse.statecode.IStateCoderFactory; +import org.eclipse.viatra.dse.util.EMFHelper; +import org.eclipse.viatra.query.runtime.exception.ViatraQueryException; + +/** + * + * @author Andras Szabolcs Nagy + * + */ +public class SolutionStore { + + public interface ISolutionSaver { + void setSolutionsCollection(Map solutions); + boolean saveSolution(ThreadContext context, Object id, SolutionTrajectory solutionTrajectory); + } + + public interface IEnoughSolutions extends ISolutionFoundHandler { + boolean enoughSolutions(); + } + + public static class ANumberOfEnoughSolutions implements IEnoughSolutions { + + private final AtomicInteger foundSolutions; + private final AtomicBoolean foundEnoughSolutions; + + public ANumberOfEnoughSolutions(int number) { + foundSolutions = new AtomicInteger(number); + foundEnoughSolutions = new AtomicBoolean(false); + } + + @Override + public boolean enoughSolutions() { + return foundEnoughSolutions.get(); + } + + @Override + public void solutionFound(ThreadContext context, SolutionTrajectory trajectory) { + int solutionsToFind = foundSolutions.decrementAndGet(); + if (solutionsToFind == 0) { + foundEnoughSolutions.set(true); + } + } + + @Override + public void solutionTriedToSave(ThreadContext context, SolutionTrajectory trajectory) { + } + } + + public static class SimpleSolutionSaver implements ISolutionSaver { + + private Map solutions; + + @Override + public void setSolutionsCollection(Map solutions) { + this.solutions = solutions; + } + + @Override + public boolean saveSolution(ThreadContext context, Object id, SolutionTrajectory solutionTrajectory) { + Solution solution = solutions.get(id); + if (solution != null) { + if (solution.getTrajectories().contains(solutionTrajectory)) { + return false; + } else { + solution.addTrajectory(solutionTrajectory); + solutionTrajectory.setSolution(solution); + } + } else { + solution = new Solution(id, solutionTrajectory); + solutions.put(id, solution); + solutionTrajectory.setSolution(solution); + } + return true; + } + } + + public static class BestSolutionSaver implements ISolutionSaver { + + private Map solutions; + private Map trajectories = new HashMap<>(); + + @Override + public void setSolutionsCollection(Map solutions) { + this.solutions = solutions; + } + + @Override + public boolean saveSolution(ThreadContext context, Object id, SolutionTrajectory solutionTrajectory) { + + Fitness lastFitness = context.getLastFitness(); + ObjectiveComparatorHelper comparatorHelper = context.getObjectiveComparatorHelper(); + + List dominatedTrajectories = new ArrayList<>(); + + for (Entry entry : trajectories.entrySet()) { + int isLastFitnessBetter = comparatorHelper.compare(lastFitness, entry.getValue()); + if (isLastFitnessBetter < 0) { + return false; + } + if (isLastFitnessBetter > 0) { + dominatedTrajectories.add(entry.getKey()); + } + } + + boolean solutionSaved = false; + + Solution solution = solutions.get(id); + if (solution != null) { + if (!solution.getTrajectories().contains(solutionTrajectory)) { + solution.addTrajectory(solutionTrajectory); + solutionTrajectory.setSolution(solution); + solutionSaved = true; + trajectories.put(solutionTrajectory, lastFitness); + } + } else { + solution = new Solution(id, solutionTrajectory); + solutions.put(id, solution); + solutionTrajectory.setSolution(solution); + solutionSaved = true; + trajectories.put(solutionTrajectory, lastFitness); + } + + for (SolutionTrajectory st : dominatedTrajectories) { + trajectories.remove(st); + Solution s = st.getSolution(); + if (!s.getTrajectories().remove(st)) { + throw new DSEException("Should not happen."); + } + if (s.getTrajectories().isEmpty()) { + Object stateCode = s.getStateCode(); + solutions.remove(stateCode); + } + } + + return solutionSaved; + } + + } + + protected boolean acceptOnlyGoalSolutions = true; + protected final Map solutions = new HashMap(); + protected ISolutionSaver solutionSaver = new SimpleSolutionSaver(); + protected List solutionFoundHandlers = new ArrayList(1); + + protected final IEnoughSolutions enoughSolutions; + + public SolutionStore() { + this(new IEnoughSolutions() { + @Override + public void solutionFound(ThreadContext context, SolutionTrajectory trajectory) { + } + + @Override + public boolean enoughSolutions() { + return false; + } + + @Override + public void solutionTriedToSave(ThreadContext context, SolutionTrajectory trajectory) { + } + }); + } + + public SolutionStore(int numOfSolutionsToFind) { + this(new ANumberOfEnoughSolutions(numOfSolutionsToFind)); + } + + public SolutionStore(IEnoughSolutions enoughSolutionsImpl) { + enoughSolutions = enoughSolutionsImpl; + } + + public synchronized void newSolution(ThreadContext context) { + solutionSaver.setSolutionsCollection(solutions); + Fitness fitness = context.getLastFitness(); + DesignSpaceManager dsm = context.getDesignSpaceManager(); + Object id = dsm.getCurrentState(); + IStateCoderFactory stateCoderFactory = context.getGlobalContext().getStateCoderFactory(); + SolutionTrajectory solutionTrajectory = dsm.getTrajectoryInfo().createSolutionTrajectory(stateCoderFactory, context.getDesignSpaceManager()); + solutionTrajectory.setFitness(fitness); + + if (acceptOnlyGoalSolutions && !fitness.isSatisifiesHardObjectives()) { + unsavedSolutionCallbacks(context, solutionTrajectory); + return; + } + + boolean solutionSaved = solutionSaver.saveSolution(context, id, solutionTrajectory); + + if (solutionSaved) { + enoughSolutions.solutionFound(context, solutionTrajectory); + + savedSolutionCallbacks(context, solutionTrajectory); + + if (enoughSolutions.enoughSolutions()) { + context.getGlobalContext().stopAllThreads(); + } + } else { + unsavedSolutionCallbacks(context, solutionTrajectory); + } + } + + private void unsavedSolutionCallbacks(ThreadContext context, SolutionTrajectory solutionTrajectory) { + for (ISolutionFoundHandler handler : solutionFoundHandlers) { + handler.solutionTriedToSave(context, solutionTrajectory); + } + } + + private void savedSolutionCallbacks(ThreadContext context, SolutionTrajectory solutionTrajectory) { + for (ISolutionFoundHandler handler : solutionFoundHandlers) { + handler.solutionFound(context, solutionTrajectory); + } + } + + public synchronized Collection getSolutions() { + return solutions.values(); + } + + public synchronized void registerSolutionFoundHandler(ISolutionFoundHandler handler) { + if (solutionFoundHandlers == null) { + solutionFoundHandlers = new ArrayList(1); + } + solutionFoundHandlers.add(handler); + } + + public SolutionStore logSolutionsWhenFound() { + registerSolutionFoundHandler(new LogSolutionHandler()); + Logger.getLogger(LogSolutionHandler.class).setLevel(Level.INFO); + return this; + } + + public SolutionStore saveModelWhenFound() { + registerSolutionFoundHandler(new ModelSaverSolutionFoundHandler()); + return this; + } + + public SolutionStore saveModelWhenFound(String extension) { + registerSolutionFoundHandler(new ModelSaverSolutionFoundHandler(extension)); + return this; + } + + public SolutionStore saveModelWhenFound(String prefix, String extension) { + registerSolutionFoundHandler(new ModelSaverSolutionFoundHandler(prefix, extension)); + return this; + } + + public SolutionStore saveModelWhenFound(ISolutionNameProvider solutionNameProvider) { + registerSolutionFoundHandler(new ModelSaverSolutionFoundHandler(solutionNameProvider)); + return this; + } + + public SolutionStore acceptGoalSolutionsOnly() { + acceptOnlyGoalSolutions = true; + return this; + } + + public SolutionStore acceptAnySolutions() { + acceptOnlyGoalSolutions = false; + return this; + } + + public SolutionStore withSolutionSaver(ISolutionSaver solutionSaver) { + this.solutionSaver = solutionSaver; + return this; + } + + public SolutionStore storeBestSolutionsOnly() { + this.solutionSaver = new BestSolutionSaver(); + return this; + } + + public void saveModels(Notifier model, ISolutionNameProvider solutionNameProvider) { + try { + for (Solution solution : solutions.values()) { + SolutionTrajectory trajectory = solution.getArbitraryTrajectory(); + trajectory.doTransformationUndoable(model); + EMFHelper.saveModel(model, solutionNameProvider.getName()); + trajectory.undoTransformation(); + } + } catch (ViatraQueryException e) { + Logger.getLogger(SolutionStore.class).error("Exception happened during model saving.", e); + } + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecode/IStateCoder.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecode/IStateCoder.java new file mode 100644 index 00000000..f163f1a5 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecode/IStateCoder.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecode; + +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; + +/** + *

+ * To be able to efficiently explore a design space, a state that has been explored before through an other trajectory + * needs to be recognized and ignored accordingly. + *

+ * + *

+ * This is done by generating a pseudo-unique value (object) that is only depended on the relevant parts of the model's + * internal state, that is, the values of two states can only be equal if the states themselves can be considered equal. + *

+ * + *

+ * The processing engine however assumes, that any two states that share this pseudo-unique value has the same + * characteristics, meaning they have the same amount and type of outgoing transitions available, and firing the + * appropriate transitions from both states also result in states that share their pseudo-unique identifier. If this + * condition is not satisfied, the exploration process's result will be non-deterministic, and in consequence, solutions + * can be lost. + *

+ * + *

+ * In addition to providing pseudo-unique identifiers to model states, the state coder must provide pseud-unique + * identifiers to the outgoing transitions as well, but they only need to be unique on the scope of the particular + * state, not globally. Global addressing thus can be achieved by considering the pseudo-unique identifier of the state + * and the pseudo-unique identifier of the transition together if needed. + *

+ * + *

+ * Both identifiers can be arbitrary objects, and equality is checked by calling {@link Object#equals(Object)} on the + * two identifiers. + *

+ * + *

+ * For any particular implementation an {@link IStateCoderFactory} implementation must also be supplied that handles the + * creation of {@link IStateCoder} instances. + *

+ * + *

+ * Usually it is unnecessary to represent everything from the model in a state code, only the parts which are modified + * by the transformation rules. + *

+ * + * @author Miklos Foldenyi, Andras Szabolcs Nagy + * + */ +public interface IStateCoder { + + /** + * Initializes the state coder on the given model. + * + * @param notifier + */ + void init(Notifier notifier); + + /** + * Returns a pseudo-unique identifier that describes the underlying model's current internal state. + * + * @return an arbitrary {@link Object} that can be used as the identifier. + */ + Object createStateCode(); + + /** + * Returns a pseudo-unique identifier that describes the given {@link IPatternMatch} in the context of the + * underlying model's current internal state. + * + * @return an arbitrary {@link Object} that can be used as the identifier in the given state. + */ + Object createActivationCode(IPatternMatch match); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecode/IStateCoderFactory.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecode/IStateCoderFactory.java new file mode 100644 index 00000000..cf8bdf8d --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecode/IStateCoderFactory.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecode; + +/** + * Interface for a factory class that creates instances of {@link IStateCoder} objects. This is required because state + * coders have to be created on-demand if the design space exploration process decides that a new thread is to be + * spawned. Since each thread requires it's own working model instance and a state coder is linked to the underlying + * model, a new {@link IStateCoder} needs to be created per processing thread. + * + * @author Miklos Foldenyi, Andras Szabolcs Nagy + * + */ +public interface IStateCoderFactory { + + /** + * Creates a new {@link IStateCoder} instance specific to this {@link IStateCoderFactory}. + * + * @return the new {@link IStateCoder} instance specific to this working model. + */ + IStateCoder createStateCoder(); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IObjectsProvider.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IObjectsProvider.java new file mode 100644 index 00000000..afcba7b6 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IObjectsProvider.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +import java.util.Collection; + +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; + +/** + * Implementation of this interface is responsible to provide {@link EObject}s of a given {@link EClass} for + * {@link TheStateCoder} + * + * @author Andras Szabolcs Nagy + * + */ +public interface IObjectsProvider { + + /** + * Initialize the {@link IObjectsProvider} on a given model and {@link StatecodingDependencyGraph}. + * + * @param notifier + * The root of the model. + * @param statecodingDependencyGraph + * The state coding dependency graph. + */ + void init(Notifier notifier, StatecodingDependencyGraph statecodingDependencyGraph); + + /** + * Returns the instances of an {@link EClass} in a model. + * + * @param eClass + * The class of the objects. + * @return The collection of the instances. + */ + Collection getEObjects(EClass eClass); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IObjectsProviderFactory.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IObjectsProviderFactory.java new file mode 100644 index 00000000..931eb1a2 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IObjectsProviderFactory.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +/** + * Interface for creating {@link IObjectsProvider} instances. + * + * @author Andras Szabolcs Nagy + */ +public interface IObjectsProviderFactory { + + /** + * Creates an {@link IObjectsProvider} implementation. + * + * @return The newly created {@link IObjectsProvider}. + */ + IObjectsProvider createObjectsProvider(); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IncrementalObjectProvider.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IncrementalObjectProvider.java new file mode 100644 index 00000000..e38d45d3 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IncrementalObjectProvider.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; +import org.eclipse.viatra.query.runtime.base.api.IndexingLevel; +import org.eclipse.viatra.query.runtime.base.api.NavigationHelper; +import org.eclipse.viatra.query.runtime.emf.EMFScope; +import org.eclipse.viatra.query.runtime.exception.ViatraQueryException; + +public class IncrementalObjectProvider implements IObjectsProvider { + + private Logger logger = Logger.getLogger(getClass()); + private NavigationHelper baseIndex; + + @Override + public void init(Notifier notifier, StatecodingDependencyGraph statecodingDependencyGraph) { + + try { + EMFScope scope = new EMFScope(notifier); + ViatraQueryEngine queryEngine = ViatraQueryEngine.on(scope); + + Set classes = new HashSet(); +// Set references = new HashSet(); + for (StatecodingNode node : statecodingDependencyGraph.getNodes()) { + classes.add(node.getClazz()); +// for (StatecodingDependency dependency : node.getStatecodingDependencies()) { +// // TODO inverse reference +// references.add(dependency.eReference); +// } + } + baseIndex = EMFScope.extractUnderlyingEMFIndex(queryEngine); + baseIndex.registerEClasses(classes, IndexingLevel.FULL); + } catch (ViatraQueryException e) { + logger.error("Failed to initialize VIATRA Query engine on the given notifier", e); + throw new DSEException("Failed to initialize VIATRA Query engine on the given notifier"); + } + } + + @Override + public Collection getEObjects(EClass eClass) { + return baseIndex.getAllInstances(eClass); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IncrementalObjectProviderFactory.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IncrementalObjectProviderFactory.java new file mode 100644 index 00000000..97011436 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/IncrementalObjectProviderFactory.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +public class IncrementalObjectProviderFactory implements IObjectsProviderFactory { + + @Override + public IncrementalObjectProvider createObjectsProvider() { + return new IncrementalObjectProvider(); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependency.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependency.java new file mode 100644 index 00000000..67b1982d --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependency.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +import org.eclipse.emf.ecore.EReference; + +public class StatecodingDependency { + + protected EReference eReference; + protected StatecodingNode node; + protected boolean isContained; + protected StatecodingDependencyType type; + + public StatecodingDependency(EReference eReference, StatecodingNode node, boolean isContained, + StatecodingDependencyType type) { + super(); + this.eReference = eReference; + this.node = node; + this.isContained = isContained; + this.type = type; + } + + public StatecodingDependency(EReference eReference, StatecodingNode node) { + this(eReference, node, false, StatecodingDependencyType.NORMAL); + } + +} \ No newline at end of file diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependencyGraph.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependencyGraph.java new file mode 100644 index 00000000..6f7255a3 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependencyGraph.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.emf.ecore.EClass; + +public class StatecodingDependencyGraph { + + private List nodes = new ArrayList(); + + public StatecodingNode createNode(EClass clazz) { + StatecodingNode node = new StatecodingNode(clazz); + node.setGraph(this); + addNode(node); + return node; + } + + public void addNode(StatecodingNode node) { + nodes.add(node); + } + + public StatecodingNode getNodeByClass(EClass eClass) { + for (StatecodingNode node : nodes) { + if (node.getClazz().equals(eClass)) { + return node; + } + } + return null; + } + + public List getNodes() { + return nodes; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependencyType.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependencyType.java new file mode 100644 index 00000000..bdd4677d --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingDependencyType.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +public enum StatecodingDependencyType { + + NORMAL, + INVERSE +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingNode.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingNode.java new file mode 100644 index 00000000..91fc28cf --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingNode.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; + +public class StatecodingNode { + + private StatecodingDependencyGraph graph; + + private final EClass clazz; + private Set attributes = new TreeSet(Comparator.comparing(EAttribute::getName)); + private List dependencies = new ArrayList(); + private boolean stateCodeIsId = false; + private StatecodingNodeType statecodingNodeType = StatecodingNodeType.CREATE_AND_DELETE; + + public StatecodingNode(EClass clazz) { + this.clazz = clazz; + } + + public StatecodingNode withAttribute(EAttribute attribute) { + attributes.add(attribute); + return this; + } + + public StatecodingNode withType(StatecodingNodeType type) { + statecodingNodeType = type; + return this; + } + + public StatecodingNode withUniqueness() { + stateCodeIsId = true; + return this; + } + + public StatecodingNode withDependency(EReference reference, StatecodingNode node) { + dependencies.add(new StatecodingDependency(reference, node)); + return this; + } + + public StatecodingNode withInverseDependency(EReference reference, StatecodingNode node) { + dependencies.add(new StatecodingDependency(reference, node, false, StatecodingDependencyType.INVERSE)); + return this; + } + + public void addDependency(StatecodingDependency statecodingDependency) { + dependencies.add(statecodingDependency); + } + + public EClass getClazz() { + return clazz; + } + + public boolean isStateCodeIsId() { + return stateCodeIsId; + } + + public void setStateCodeIsId(boolean stateCodeIsId) { + this.stateCodeIsId = stateCodeIsId; + } + + public StatecodingNodeType getStatecodingNodeType() { + return statecodingNodeType; + } + + public void setStatecodingNodeType(StatecodingNodeType statecodingNodeType) { + this.statecodingNodeType = statecodingNodeType; + } + + public Set getAttributes() { + return attributes; + } + + public List getStatecodingDependencies() { + return dependencies; + } + + public StatecodingDependencyGraph getGraph() { + return graph; + } + + public void setGraph(StatecodingDependencyGraph graph) { + this.graph = graph; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingNodeType.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingNodeType.java new file mode 100644 index 00000000..c902a7a6 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/StatecodingNodeType.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +public enum StatecodingNodeType { + + FIXED, + ONLY_CREATE, + ONLY_DELETE, + CREATE_AND_DELETE +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/TheStateCoder.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/TheStateCoder.java new file mode 100644 index 00000000..4601ff08 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/TheStateCoder.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; + +public class TheStateCoder implements IStateCoder { + + private StatecodingDependencyGraph sdg; + private IObjectsProvider objectProvider; + + public TheStateCoder(StatecodingDependencyGraph sdg, IObjectsProvider objectProvider) { + this.sdg = sdg; + this.objectProvider = objectProvider; + } + + @Override + public void init(Notifier notifier) { + // TODO checks + // TODO node sorting based on traversal - in factory + + // this.notifier = notifier; + // try { + // EMFScope scope = new EMFScope(notifier); + // queryEngine = ViatraQueryEngine.on(scope); + // } catch (ViatraQueryException e1) { + // logger.error("Failed to initialize VIATRA Query engine on the given notifier", e1); + // throw new DSEException("Failed to initialize VIATRA Query engine on the given notifier"); + // } + + objectProvider.init(notifier, sdg); + } + + @Override + public String createStateCode() { + + StringBuilder sb = new StringBuilder(); + + // TODO sort + for (StatecodingNode node : sdg.getNodes()) { + sb.append(node.getClazz().getName()); + sb.append(':'); + sb.append(addStateCode(node)); + sb.append('|'); + } + sb.deleteCharAt(sb.length() - 1); + + return sb.toString(); + + } + + @Override + public Object createActivationCode(IPatternMatch match) { + // TODO root object + // TODO parameterless? + + int i = 0; + StringBuilder sb = new StringBuilder(); + Object object; + do { + object = match.get(i++); + if (object != null) { + if (object instanceof EObject) { + EObject eObject = (EObject) object; + sb.append(addStateCode(sdg.getNodeByClass(eObject.eClass()), eObject)); + } else { + // TODO toString or not to toString + } + } + } while (object != null); + + return sb.toString(); + } + + public String addStateCode(StatecodingNode node, EObject eObject) { + StringBuilder sb = new StringBuilder(); + + Set attributes = node.getAttributes(); + if (!attributes.isEmpty()) { + for (EAttribute eAttribute : attributes) { + // attributes are sorted + // TODO handle collection + sb.append(eObject.eGet(eAttribute)); + sb.append(';'); + } + sb.deleteCharAt(sb.length() - 1); + } + + List dependencies = node.getStatecodingDependencies(); + int dependenciesSize = dependencies.size(); + if (dependenciesSize > 0) { + String[] codeParts = new String[dependenciesSize]; + int i = 0; + for (StatecodingDependency dependency : dependencies) { + String code = addStateCodeFromDependency(dependency, eObject); + codeParts[i++] = code; + } + Arrays.sort(codeParts); + + sb.append("("); + sb.append(codeParts[0]); + for (i = 1; i < codeParts.length; i++) { + sb.append(';'); + sb.append(codeParts[i]); + } + sb.append(")"); + } + return sb.toString(); + } + + public String addStateCode(StatecodingNode node) { + Collection eObjects = objectProvider.getEObjects(node.getClazz()); + int size = eObjects.size(); + + if (size > 0) { + String[] codeParts = new String[size]; + int i = 0; + for (EObject eObject : eObjects) { + String code = addStateCode(node, eObject); + codeParts[i++] = code; + } + Arrays.sort(codeParts); + + StringBuilder sb = new StringBuilder(); + sb.append(codeParts[0]); + for (i = 1; i < codeParts.length; i++) { + sb.append(';'); + sb.append(codeParts[i]); + } + return sb.toString(); + } + + return ""; + } + + public String addStateCodeFromDependency(StatecodingDependency sd, EObject eObject) { + + if (sd.type.equals(StatecodingDependencyType.NORMAL)) { + + Object eReferred = eObject.eGet(sd.eReference); + if (eReferred == null) { + return ""; + } else if (eReferred instanceof EList) { + EList refferedList = (EList) eReferred; + // TODO test + if (!refferedList.isEmpty()) { + + String[] codeParts = new String[refferedList.size()]; + int i = 0; + for (Object referredEObject : refferedList) { + String code = addStateCode(sd.node, (EObject) referredEObject); + codeParts[i++] = code; + } + Arrays.sort(codeParts); + + StringBuilder sb = new StringBuilder(); + sb.append('['); + sb.append(codeParts[0]); + for (i = 1; i < codeParts.length; i++) { + sb.append(';'); + sb.append(codeParts[i]); + } + sb.append(']'); + return sb.toString(); + + } + } else if (eReferred instanceof EObject) { + return addStateCode(sd.node, (EObject) eReferred); + } else { + throw new DSEException("The EObject " + eObject.toString() + " does not have a feature " + + eReferred.toString() + "."); + } + + } else { + for (EObject dependentEObject : objectProvider.getEObjects(sd.node.getClazz())) { + Object eReferred = dependentEObject.eGet(sd.eReference); + if (eReferred == null) { + continue; + } else if (eReferred instanceof EList) { + // TODO this is slow, use VIATRA Query + for (Object referredEObject : ((EList) eReferred)) { + if (referredEObject.equals(eObject)) { + return addStateCode(sd.node, (EObject) dependentEObject); + } + } + } else if (eReferred.equals(eObject)) { + // Probably never happens? + return addStateCode(sd.node, (EObject) dependentEObject); + } else { + throw new DSEException("The EObject " + eObject.toString() + " does not have a feature " + + eReferred.toString() + "."); + } + } + } + + return ""; + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/TheStateCoderFactory.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/TheStateCoderFactory.java new file mode 100644 index 00000000..eeb6e48f --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/TheStateCoderFactory.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding; + +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.dse.statecode.IStateCoderFactory; + +public class TheStateCoderFactory implements IStateCoderFactory { + + private StatecodingDependencyGraph sdg; + private IObjectsProviderFactory objectProviderFactory; + + public TheStateCoderFactory(StatecodingDependencyGraph sdg) { + this(sdg, new IncrementalObjectProviderFactory()); + } + + public TheStateCoderFactory(StatecodingDependencyGraph sdg, IObjectsProviderFactory objectProviderFactory) { + this.sdg = sdg; + this.objectProviderFactory = objectProviderFactory; + + // TODO cyclic dependency? - exception + + // TODO make plan for traversal + + // TODO If the type is FIXED and all dependency is FIXED then do not create state code for it (them) + // This is not true :( e.g. matchmaking - they are fixed, but the references must be encoded + } + + @Override + public IStateCoder createStateCoder() { + return new TheStateCoder(sdg, objectProviderFactory.createObjectsProvider()); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/simple/SimpleStateCoder.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/simple/SimpleStateCoder.java new file mode 100644 index 00000000..0f0759ae --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/simple/SimpleStateCoder.java @@ -0,0 +1,250 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding.simple; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EStructuralFeature.Setting; +import org.eclipse.viatra.dse.api.DSEException; +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.dse.util.EMFHelper.MetaModelElements; +import org.eclipse.viatra.dse.util.ValueComparableEObjectStringMap; +import org.eclipse.viatra.query.runtime.api.IPatternMatch; +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; +import org.eclipse.viatra.query.runtime.base.api.FeatureListener; +import org.eclipse.viatra.query.runtime.base.api.IndexingLevel; +import org.eclipse.viatra.query.runtime.base.api.InstanceListener; +import org.eclipse.viatra.query.runtime.base.api.NavigationHelper; +import org.eclipse.viatra.query.runtime.emf.EMFBaseIndexWrapper; +import org.eclipse.viatra.query.runtime.emf.EMFScope; +import org.eclipse.viatra.query.runtime.exception.ViatraQueryException; + +/** + * + * @author Andras Szabolcs Nagy + * + */ +public class SimpleStateCoder implements IStateCoder { + + private Set classes; + private Set features; + private NavigationHelper navigationHelper; + + private Map> objectCodes; + private int maxDepth; + + private Set changedOrNewEObjects = new HashSet(); + private Set deletedClasses = new HashSet(); + + public SimpleStateCoder(MetaModelElements metaModelElements) { + this.maxDepth = 1; + + classes = metaModelElements.classes; + features = new HashSet(metaModelElements.attributes); + features.addAll(metaModelElements.references); + } + + @Override + public void init(Notifier notifier) { + try { + EMFScope scope = new EMFScope(notifier); + ViatraQueryEngine queryEngine = ViatraQueryEngine.on(scope); + EMFBaseIndexWrapper baseIndex = (EMFBaseIndexWrapper) queryEngine.getBaseIndex(); + navigationHelper = baseIndex.getNavigationHelper(); + navigationHelper.registerObservedTypes(classes, null, features, IndexingLevel.FULL); + } catch (ViatraQueryException e) { + throw new DSEException(e); + } + + objectCodes = new HashMap>(); + for (EClass eClass : classes) { + Map codes = new ValueComparableEObjectStringMap(); + + objectCodes.put(eClass, codes); + + for (EObject eObject : navigationHelper.getDirectInstances(eClass)) { + codes.put(eObject, createObjectCodeWithDepth(eObject, maxDepth)); + } + } + + navigationHelper.addFeatureListener(features, new FeatureListener() { + + @Override + public void featureInserted(EObject host, EStructuralFeature feature, Object value) { + changedOrNewEObjects.add(host); + } + + @Override + public void featureDeleted(EObject host, EStructuralFeature feature, Object value) { + changedOrNewEObjects.add(host); + if (value instanceof EObject) { + changedOrNewEObjects.add((EObject) value); + } + } + }); + + navigationHelper.addInstanceListener(classes, new InstanceListener() { + + @Override + public void instanceInserted(EClass clazz, EObject instance) { + changedOrNewEObjects.add(instance); + } + + @Override + public void instanceDeleted(EClass clazz, EObject instance) { + deletedClasses.add(instance); + } + }); + } + + private String createObjectCodeWithDepth(EObject eObject, int depth) { + + StringBuilder sb = new StringBuilder(); + + Collection attributes = eObject.eClass().getEAllAttributes(); + for (EAttribute eAttribute : attributes) { + Object value = eObject.eGet(eAttribute); + sb.append(value); + sb.append(','); + } + if (!attributes.isEmpty()) { + sb.deleteCharAt(sb.length() - 1); + } + if (depth > 0) { + sb.append('-'); + Collection eReferences = eObject.eClass().getEAllReferences(); + for (EReference eReference : eReferences) { + Object value = eObject.eGet(eReference); + if (value == null) { + sb.append("null,"); + } else if (value instanceof EObject) { + sb.append(createObjectCodeWithDepth((EObject) value, depth - 1)); + sb.append(','); + } else { + List referencedEObjects = (List) value; + if (!referencedEObjects.isEmpty()) { + + String[] codes = new String[referencedEObjects.size()]; + int index = 0; + for (EObject referencedEObject : referencedEObjects) { + codes[index++] = createObjectCodeWithDepth(referencedEObject, depth - 1); + } + Arrays.sort(codes); + sb.append('('); + for (String code : codes) { + sb.append(code); + } + sb.append("),"); + } + } + } + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); + } + + @Override + public Object createStateCode() { + + refreshObjectCodes(); + + StringBuilder sb = new StringBuilder(); + + for (EClass eClass : classes) { + + Set instances = navigationHelper.getDirectInstances(eClass); + + if (!instances.isEmpty()) { + + sb.append(eClass.getName()); + sb.append(':'); + + String[] codesToSort = new String[instances.size()]; + int index = 0; + Map codes = objectCodes.get(eClass); + for (EObject eObject : instances) { + codesToSort[index++] = codes.get(eObject); + } + Arrays.sort(codesToSort); + for (String string : codesToSort) { + sb.append(string); + sb.append(';'); + } + sb.deleteCharAt(sb.length() - 1); + sb.append('|'); + } + } + if (sb.length() != 0) { + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); + } + + private void refreshObjectCodes() { + for (EObject eObject : deletedClasses) { + EClass eClass = eObject.eClass(); + objectCodes.get(eClass).remove(eObject); + } + deletedClasses.clear(); + + Set objectsToRecode = new HashSet(); + for (EObject eObject : changedOrNewEObjects) { + objectsToRecode.add(eObject); + for (Setting setting : navigationHelper.getInverseReferences(eObject)) { + objectsToRecode.add(setting.getEObject()); + } + } + + for (EObject eObject : objectsToRecode) { + EClass eClass = eObject.eClass(); + objectCodes.get(eClass).put(eObject, createObjectCodeWithDepth(eObject, maxDepth)); + } + changedOrNewEObjects.clear(); + } + + @Override + public Object createActivationCode(IPatternMatch match) { + + StringBuilder sb = new StringBuilder(); + String[] tokens = match.specification().getFullyQualifiedName().split("\\."); + sb.append(tokens[tokens.length - 1]); + sb.append(':'); + Object param; + for (int i = 0; (param = match.get(i)) != null; i++) { + EObject eObject = (EObject) param; + + Collection attributes = eObject.eClass().getEAllAttributes(); + for (EAttribute eAttribute : attributes) { + Object value = eObject.eGet(eAttribute); + sb.append(value); + sb.append(','); + } + if (!attributes.isEmpty()) { + sb.deleteCharAt(sb.length() - 1); + } + + sb.append('|'); + } + sb.deleteCharAt(sb.length() - 1); + return sb.toString().intern(); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/simple/SimpleStateCoderFactory.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/simple/SimpleStateCoderFactory.java new file mode 100644 index 00000000..d776e8a8 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/statecoding/simple/SimpleStateCoderFactory.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.statecoding.simple; + +import java.util.Collection; +import java.util.HashSet; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.viatra.dse.statecode.IStateCoder; +import org.eclipse.viatra.dse.statecode.IStateCoderFactory; +import org.eclipse.viatra.dse.util.EMFHelper; +import org.eclipse.viatra.dse.util.EMFHelper.MetaModelElements; + +/** + * + * @author Andras Szabolcs Nagy + * + */ +public class SimpleStateCoderFactory implements IStateCoderFactory { + + private MetaModelElements metaModelElements; + + public SimpleStateCoderFactory(Collection metaModelPackages) { + metaModelElements = EMFHelper.getAllMetaModelElements(new HashSet(metaModelPackages)); + } + + @Override + public IStateCoder createStateCoder() { + return new SimpleStateCoder(metaModelElements); + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java new file mode 100644 index 00000000..14b3acfb --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java @@ -0,0 +1,424 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.util; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.command.BasicCommandStack; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil.Copier; +import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; +import org.eclipse.emf.edit.command.AddCommand; +import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; + +/** + * This class contains static helper methods. + * @author Andras Szabolcs Nagy + */ +public final class EMFHelper { + + private static final Logger logger = Logger.getLogger(EMFHelper.class); + + private EMFHelper() { + } + + public static class EmfHelperException extends RuntimeException { + private static final long serialVersionUID = 7635796550669616626L; + + public EmfHelperException(String string) { + super(string); + } + public EmfHelperException(String string, Throwable e) { + super(string, e); + } + } + + /** + * Gets the {@link EditingDomain} of either an {@link EObject}, {@link Resource} or {@link ResourceSet}. + * @param notifier The {@link Notifier}. + * @return The EditingDomain. + */ + public static EditingDomain getEditingDomain(Notifier notifier) { + Objects.requireNonNull(notifier); + if (notifier instanceof EObject) { + EObject eObject = (EObject) notifier; + return AdapterFactoryEditingDomain.getEditingDomainFor(eObject); + } else if (notifier instanceof Resource) { + Resource resource = (Resource) notifier; + EList contents = resource.getContents(); + if (contents.isEmpty()) { + return null; + } + return AdapterFactoryEditingDomain.getEditingDomainFor(contents.get(0)); + } else if (notifier instanceof ResourceSet) { + ResourceSet resourceSet = (ResourceSet) notifier; + if (resourceSet.getResources().isEmpty()) { + return null; + } + return getEditingDomain(resourceSet.getResources().get(0)); + } + + return null; + } + + /** + * Creates (or gets if already exists) an {@link EditingDomain} over the given {@link Notifier}, + * either an {@link EObject}, {@link Resource} or {@link ResourceSet}. + * @param notifier The {@link Notifier}. + * @return The EditingDomain. + */ + public static EditingDomain createEditingDomain(Notifier notifier) { + + EditingDomain domain = getEditingDomain(notifier); + if (domain != null) { + return domain; + } + + registerExtensionForXmiSerializer("dummyext"); + + if (notifier instanceof EObject) { + EObject eObject = (EObject) notifier; + + domain = new AdapterFactoryEditingDomain(null, new BasicCommandStack()); + Resource resource = domain.getResourceSet().createResource(URI.createFileURI("dummy.dummyext")); + domain.getCommandStack().execute(new AddCommand(domain, resource.getContents(), eObject)); + + return domain; + + } else if (notifier instanceof Resource) { + Resource resource = (Resource) notifier; + + ResourceSet resourceSet = resource.getResourceSet(); + if (resourceSet != null) { + return new AdapterFactoryEditingDomain(null, new BasicCommandStack(), resourceSet); + } else { + domain = new AdapterFactoryEditingDomain(null, new BasicCommandStack(), (ResourceSet) null); + resourceSet = domain.getResourceSet(); + domain.getCommandStack().execute(new AddCommand(domain, resourceSet.getResources(), resource)); + return domain; + } + + } else if (notifier instanceof ResourceSet) { + return new AdapterFactoryEditingDomain(null, new BasicCommandStack(), (ResourceSet) notifier); + } else { + throw new EmfHelperException("Not supported argument type."); + } + } + + /** + * Saves the EMF model (EObject or Resource) into the given file. An {@link XMIResourceFactoryImpl} will be + * registered if not already. + * + * Doesn't throw exception but logs an error if the save was unsuccessful. + * + * @param model Can be an {@link EObject} or a {@link Resource}. + * @param fileName + */ + public static void saveModel(Notifier model, String fileName) { + + Objects.requireNonNull(model); + Preconditions.checkArgument(fileName != null && !fileName.isEmpty(), "File name is null or empty."); + + int extensionIndex = fileName.lastIndexOf('.'); + + Preconditions.checkState(extensionIndex > -1 && extensionIndex != fileName.length() - 1, "Bad file extension."); + + String ext = fileName.substring(extensionIndex + 1); + + registerExtensionForXmiSerializer(ext); + + URI uri = URI.createFileURI(fileName); + Resource resource; + + if (model instanceof ResourceSet) { + throw new EmfHelperException("Unsupported type: ResourceSet"); + } else if (model instanceof Resource) { + resource = (Resource) model; + } else if (model instanceof EObject) { + EObject root = (EObject) model; + ResourceSet resSet = new ResourceSetImpl(); + resource = resSet.createResource(uri); + resource.getContents().add(root); + } else { + throw new EmfHelperException("Unkown type: " + model.getClass()); + } + + resource.setURI(uri); + saveResource(resource); + } + + private static void saveResource(Resource resource) { + try { + resource.save(Collections.emptyMap()); + } catch (IOException e) { + logger.error(e); + } + } + + /** + * Loads a model as a {@link Resource}. In headless mode, don't forget to call XYZPackage.eINSTANCE. + */ + public static Resource loadModel(String fileName) throws IOException { + Preconditions.checkArgument(fileName != null && !fileName.isEmpty(), "File name is null or empty."); + int extensionIndex = fileName.lastIndexOf('.'); + Preconditions.checkState(extensionIndex > -1 && extensionIndex != fileName.length() - 1, "Bad file extension."); + + String ext = fileName.substring(extensionIndex + 1); + registerExtensionForXmiSerializer(ext); + + ResourceSetImpl rSet = new ResourceSetImpl(); + URI fileUri = URI.createFileURI(fileName); + Resource resource = rSet.createResource(fileUri); + + resource.load(null); + return resource; + } + + /** + * Retrieves the root EObject from a Resource or ResourceSet. + *
    + *
  • Returns null if there is no content.
  • + *
  • Returns the notifier itself if it is an EObject.
  • + *
  • Logs a warn if there are multiple roots.
  • + *
+ * + * @param notifier + * @return The root EObject or null. + */ + public static EObject getRootEObject(Notifier notifier) { + if (notifier instanceof EObject) { + return (EObject) notifier; + } else if (notifier instanceof Resource) { + Resource resource = (Resource) notifier; + List contents = resource.getContents(); + if (contents.size() > 1) { + logger.warn("Resource has more than one root."); + } + if (contents.isEmpty()) { + return null; + } else { + return contents.get(0); + } + } else if (notifier instanceof ResourceSet) { + ResourceSet resourceSet = (ResourceSet) notifier; + List resources = resourceSet.getResources(); + if (resources.size() > 1) { + logger.warn("ResourceSet has more than one resources."); + } + if (resources.isEmpty()) { + return null; + } else { + return getRootEObject(resources.get(0)); + } + } else { + throw new EmfHelperException("Unkown type: " + notifier.getClass()); + } + } + + /** + * Registers an {@link XMIResourceFactoryImpl} for the given extension. + * @param ext The extension as a String. + */ + public static void registerExtensionForXmiSerializer(String ext) { + Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE; + Map m = reg.getExtensionToFactoryMap(); + m.computeIfAbsent(ext, e -> new XMIResourceFactoryImpl()); + } + + /** + * Clones the given model. Either an {@link EObject}, {@link Resource} or {@link ResourceSet}. + * @param notifier The root container of the model. + * @return The cloned model. + */ + public static Notifier clone(Notifier notifier) { + Copier copier = new Copier(); + Notifier clonedModel = clone(notifier, copier, null); + copier.copyReferences(); + return clonedModel; + } + + private static Notifier clone(Notifier notifier, Copier copier, ResourceSet resourceSetToCloneTo) { + Objects.requireNonNull(copier); + + if (notifier instanceof EObject) { + EObject eObject = (EObject) notifier; + return copier.copy(eObject); + } else if (notifier instanceof Resource) { + Resource resource = (Resource) notifier; + ResourceSet rSetTemp = resourceSetToCloneTo; + if (resourceSetToCloneTo == null) { + rSetTemp = new ResourceSetImpl(); + } + Resource clonedResource = rSetTemp.createResource(URI.createFileURI("dummy.dummyext")); + + for (EObject eObject : resource.getContents()) { + EObject clonedEObject = copier.copy(eObject); + clonedResource.getContents().add(clonedEObject); + } + + return clonedResource; + } else if (notifier instanceof ResourceSet) { + ResourceSet resourceSet = (ResourceSet) notifier; + ResourceSetImpl clonedResourceSet = new ResourceSetImpl(); + + for (Resource resource : resourceSet.getResources()) { + clone(resource, copier, clonedResourceSet); + } + + return clonedResourceSet; + } else { + throw new EmfHelperException("Not supported argument type."); + } + } + + public static class ENamedElementComparator implements Comparator { + @Override + public int compare(ENamedElement eClass1, ENamedElement eClass2) { + return eClass1.getName().compareTo(eClass2.getName()); + } + } + + /** + * This class is used to store + *
    + *
  • {@link EClass}es,
  • + *
  • {@link EAttribute}s,
  • + *
  • {@link EReference}s,
  • + *
  • EAttributes by EClasses,
  • + *
  • EReferences by EClasses
  • + *
+ * for a given set of {@link EPackage}s. + * + */ + public static class MetaModelElements { + public Set metaModelPackages; + public Set classes; + public Set attributes; + public Set references; + public Map> attributesOfClass; + public Map> referencesOfClass; + } + + /** + * Traverses the full metamodel on the given {@link EPackage}s and returns all the classes, attributes and + * references it contains. + * + * @param metaModelPackages + * The set of {@link EPackage}s. + * @return A {@link MetaModelElements} instance containing the metamodel elements. + */ + public static MetaModelElements getAllMetaModelElements(Set metaModelPackages) { + return getMetaModelElements(metaModelPackages, true, true, true); + } + + /** + * Return a {@link MetaModelElements} instance populated with its {@link MetaModelElements#classes}. + * + * @param metaModelPackages + * The set of {@link EPackage}s. + * @return AA {@link MetaModelElements} instance. + */ + public static MetaModelElements getClasses(Set metaModelPackages) { + return getMetaModelElements(metaModelPackages, true, false, false); + } + + /** + * Return a {@link MetaModelElements} instance populated with its {@link MetaModelElements#references} and + * {@link MetaModelElements#referencesOfClass}. + * + * @param metaModelPackages + * The set of {@link EPackage}s. + * @return AA {@link MetaModelElements} instance. + */ + public static MetaModelElements getReferences(Set metaModelPackages) { + return getMetaModelElements(metaModelPackages, false, true, false); + } + + /** + * Return a {@link MetaModelElements} instance populated with its {@link MetaModelElements#attributes} and + * {@link MetaModelElements#attributesOfClass}. + * + * @param metaModelPackages + * The set of {@link EPackage}s. + * @return AA {@link MetaModelElements} instance. + */ + public static MetaModelElements getAttrbiutes(Set metaModelPackages) { + return getMetaModelElements(metaModelPackages, false, false, true); + } + + private static MetaModelElements getMetaModelElements(Set metaModelPackages, boolean getClasses, + boolean getReferences, boolean getAttrbiutes) { + + Comparator comparator = new ENamedElementComparator(); + + MetaModelElements result = new MetaModelElements(); + result.metaModelPackages = metaModelPackages; + if (getClasses) { + result.classes = new TreeSet(comparator); + } + if (getReferences) { + result.references = new HashSet(); + result.referencesOfClass = new HashMap>(); + } + if (getAttrbiutes) { + result.attributes = new HashSet(); + result.attributesOfClass = new HashMap>(); + } + for (EPackage ePackage : metaModelPackages) { + for (EClassifier eClassifier : ePackage.getEClassifiers()) { + if (eClassifier instanceof EClass) { + EClass eClass = ((EClass) eClassifier); + if (getClasses) { + result.classes.add(eClass); + } + if (getReferences) { + result.referencesOfClass.put(eClass, new TreeSet(comparator)); + for (EReference eReference : eClass.getEAllReferences()) { + result.references.add(eReference); + result.referencesOfClass.get(eClass).add(eReference); + } + } + if (getAttrbiutes) { + result.attributesOfClass.put(eClass, new TreeSet(comparator)); + for (EAttribute eAttribute : eClass.getEAllAttributes()) { + result.attributes.add(eAttribute); + result.attributesOfClass.get(eClass).add(eAttribute); + } + } + } + } + } + return result; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/Hasher.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/Hasher.java new file mode 100644 index 00000000..0c5d7eba --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/Hasher.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.util; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.eclipse.viatra.dse.api.DSEException; + +/** + * Utility class that encapsulates a {@link MessageDigest} instance to aid calculating hash values more easily, and to + * reuse a {@link MessageDigest} instance. + * + */ +public final class Hasher { + private MessageDigest md; + + private static final int HEX = 16; + + private Hasher(MessageDigest md) { + this.md = md; + } + + /** + * Calculates and returns a hash value. + * + * @param data + * the data to be hashed in a {@link String}. + * @return the hash value in some {@link String} representation. + */ + public String hash(String data) { + md.update(data.getBytes(), 0, data.length()); + return new String(md.digest()); + } + + @SuppressWarnings("unused") + private String alternateHashBest(String data) { + md.update(data.getBytes(), 0, data.length()); + return new String(md.digest()); + } + + @SuppressWarnings("unused") + private String alternateHashSecondBest(String data) { + md.update(data.getBytes(), 0, data.length()); + return new BigInteger(1, md.digest()).toString(HEX); + } + + @SuppressWarnings("unused") + private String alternateHashThirdBest(String data) { + md.update(data.getBytes(), 0, data.length()); + byte[] array = md.digest(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < array.length; i++) { + sb.append(Integer.toHexString((int) array[i])); + } + return sb.toString(); + } + + /** + * Returns a {@link Hasher} with an internal {@link MessageDigest} that is based on the protocol named + * {@code protocoll}. + * + * @param protocoll + * the name of the hash algorythm. + * @return the initialized {@link Hasher} + * + * @throws DSEException + * on initialization failure. + */ + public static Hasher getHasher(String protocoll) { + try { + return new Hasher(MessageDigest.getInstance(protocoll)); + } catch (NoSuchAlgorithmException e) { + throw new DSEException(e); + } + } + + public static final String SHA1_PROTOCOLL = "SHA-1"; +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/ValueComparableEObjectStringMap.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/ValueComparableEObjectStringMap.java new file mode 100644 index 00000000..49af05d1 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/ValueComparableEObjectStringMap.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.util; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Functions; +import com.google.common.collect.Ordering; + +/** + * + * This custom {@link TreeMap} implementation enables to store {@link EObject}-{@link String} pairs sorted by values + * (strings). It works as expected if the map is modified in any way, hence the map will still be sorted by values on + * the new set of entries. + * + * It is allowed to have two entries with the same EObject key (and also with same values). + * + * The short coming of the class is that EObjects are compared to each other by their + * {@link System#identityHashCode(Object)}, which may lead to unexpected errors. + * + * @author Andras Szabolcs Nagy + * + */ +public class ValueComparableEObjectStringMap extends TreeMap { + + private static final class EObjectComparator implements Comparator { + @Override + public int compare(EObject o1, EObject o2) { + return Integer.valueOf(System.identityHashCode(o1)).compareTo(Integer.valueOf(System.identityHashCode(o2))); + } + } + + private final Map innerMap; + + public ValueComparableEObjectStringMap() { + this(new HashMap()); + } + + private ValueComparableEObjectStringMap(Map innerMap) { + super(Ordering.natural().onResultOf(Functions.forMap(innerMap)).compound(new EObjectComparator())); + this.innerMap = innerMap; + } + + @Override + public String put(EObject keyEObject, String stringValue) { + if (innerMap.containsKey(keyEObject)) { + remove(keyEObject); + } + innerMap.put(keyEObject, stringValue); + return super.put(keyEObject, stringValue); + } +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/DesignSpaceVisualizerOptions.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/DesignSpaceVisualizerOptions.java new file mode 100644 index 00000000..bb8000d5 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/DesignSpaceVisualizerOptions.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.visualizer; + +public class DesignSpaceVisualizerOptions { + + public boolean showExplorationTrace = true; + public boolean showStateCodes = true; + public boolean showTransitionCodes = true; + + public DesignSpaceVisualizerOptions withOutExploraionTrace() { + showExplorationTrace = false; + return this; + } + + public DesignSpaceVisualizerOptions withOutstateCodes() { + showStateCodes = false; + return this; + } + + public DesignSpaceVisualizerOptions withOutTransitionCodes() { + showTransitionCodes = false; + return this; + } + + public boolean isShowExplorationTrace() { + return showExplorationTrace; + } + + public void setShowExplorationTrace(boolean showExplorationTrace) { + this.showExplorationTrace = showExplorationTrace; + } + + public boolean isShowStateCodes() { + return showStateCodes; + } + + public void setShowStateCodes(boolean showStateCodes) { + this.showStateCodes = showStateCodes; + } + + public boolean isShowTransitionCodes() { + return showTransitionCodes; + } + + public void setShowTransitionCodes(boolean showTransitionCodes) { + this.showTransitionCodes = showTransitionCodes; + } + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/IDesignSpaceVisualizer.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/IDesignSpaceVisualizer.java new file mode 100644 index 00000000..da598b91 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/IDesignSpaceVisualizer.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.visualizer; + +import org.eclipse.viatra.dse.api.DesignSpaceExplorer; +import org.eclipse.viatra.dse.base.ThreadContext; + +/** + * + * An implementation of this interface is notified about the traversal of the design space from every traversing thread, + * if registered to the {@link DesignSpaceExplorer}. Its purpose is to able to visualize the design space (a directed + * graph with IDs of the nodes and transitions) and to able to visualize the order of the exploration (the trace of a + * thread). + * + * @author Andras Szabolcs Nagy + * + */ +public interface IDesignSpaceVisualizer extends IExploreEventHandler { + + /** + * Initializes the instance with a starting thread's context. Can be called multiple times and concurrently. + * + * @see DesignSpaceVisualizerOptions + * @param context + */ + void init(ThreadContext context); + + /** + * Saves the captured data. + */ + void save(); + +} diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/IExploreEventHandler.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/IExploreEventHandler.java new file mode 100644 index 00000000..9f902f31 --- /dev/null +++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/visualizer/IExploreEventHandler.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.dse.visualizer; + +import org.eclipse.viatra.dse.base.DesignSpaceManager; + +/** + * An implementation of this interface is notified about every move in the design space (firing a rule activation or + * undoing it) of a single thread, if registered to the corresponding {@link DesignSpaceManager}. Its methods are called + * synchronously, therefore the implementation can have an impact on the performance. Also note, if the same instance is + * registered to multiple threads ({@link DesignSpaceManager}), it has to be thread safe. + * + * @author Andras Szabolcs Nagy + * + */ +public interface IExploreEventHandler { + + /** + * Called by the {@link DesignSpaceManager}, after a rule activation (transition) is fired. Multiple calls with the + * same transition can occur. + * + * @param transition The fired transition. + */ + void transitionFired(Object transition); + + /** + * Called by the {@link DesignSpaceManager}, after undoing the previously fired rule activation (transition). + * Multiple calls with the same transition can occur. + * + * @param transition The undone transition. + */ + void undo(Object transition); + +} -- cgit v1.2.3-54-g00ecf