From f06427cd7375551582461f91b3458339a8227f9b Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Mon, 2 Nov 2020 02:02:40 +0100 Subject: Optimizing generator with linear objective functions --- .../optimization/CostElementMatchers.xtend | 137 ++++++++++++++ .../reasoner/optimization/CostObjectiveHint.xtend | 68 +++++++ .../optimization/IObjectiveBoundsProvider.xtend | 8 + .../optimization/ThreeValuedCostObjective.xtend | 99 +++++----- .../ThreeValuedCostObjectiveProvider.xtend | 205 +++++++++++++++++++++ 5 files changed, 465 insertions(+), 52 deletions(-) create mode 100644 Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/CostElementMatchers.xtend create mode 100644 Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/CostObjectiveHint.xtend create mode 100644 Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/IObjectiveBoundsProvider.xtend create mode 100644 Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/ThreeValuedCostObjectiveProvider.xtend (limited to 'Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization') diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/CostElementMatchers.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/CostElementMatchers.xtend new file mode 100644 index 00000000..885b14e8 --- /dev/null +++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/CostElementMatchers.xtend @@ -0,0 +1,137 @@ +package hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner.optimization + +import com.google.common.collect.ImmutableList +import hu.bme.mit.inf.dslreasoner.logic.model.logicproblem.LogicproblemPackage +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialinterpretationPackage +import java.util.List +import org.eclipse.emf.ecore.EObject +import org.eclipse.viatra.query.runtime.api.IPatternMatch +import org.eclipse.viatra.query.runtime.api.ViatraQueryMatcher +import org.eclipse.xtend.lib.annotations.Data +import hu.bme.mit.inf.dslreasoner.logic.model.logicproblem.LogicProblem +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation + +@FunctionalInterface +interface ParameterScopeBound { + def double getUpperBound() +} + +@Data +class CostElementMatch { + val IPatternMatch match + val boolean must + + def isMulti() { + CostElementMatchers.isMultiMatch(match) + } +} + +@Data +class CostElementMatchers { + val ViatraQueryMatcher currentMatcher + val ViatraQueryMatcher mayMatcher + val ViatraQueryMatcher mustMatcher + val List parameterScopeBounds + val int weight + + def getCurrentNumberOfMatches() { + currentMatcher.countMatches + } + + def getMinimumNumberOfMatches() { + mustMatcher.countMatches + } + + def getMaximumNumberOfMatches() { + var double sum = 0 + val iterator = mayMatcher.streamAllMatches.iterator + while (iterator.hasNext) { + val match = iterator.next + var double product = 1 + val numberOfParameters = parameterScopeBounds.size + for (var int i = 0; i < numberOfParameters; i++) { + if (isMulti(match.get(i + 2))) { + val scopeBound = parameterScopeBounds.get(i) + product *= scopeBound.upperBound + } + + } + sum += product + } + sum + } + + def getMatches() { + ImmutableList.copyOf(mayMatcher.streamAllMatches.iterator.map [ match | + new CostElementMatch(match, mustMatcher.isMatch(match)) + ]) + } + + def projectMayMatch(IPatternMatch match, int... indices) { + mayMatcher.projectMatch(match, indices) + } + + private static def projectMatch(ViatraQueryMatcher matcher, IPatternMatch match, int... indices) { + checkMatch(match) + val n = matcher.specification.parameters.length - 2 + if (indices.length != n) { + throw new IllegalArgumentException("Invalid number of projection indices") + } + val newMatch = matcher.newEmptyMatch + newMatch.set(0, match.get(0)) + newMatch.set(1, match.get(1)) + for (var int i = 0; i < n; i++) { + newMatch.set(i + 2, match.get(indices.get(i))) + } + if (!matcher.hasMatch(newMatch)) { + throw new IllegalArgumentException("Projected match does not exist") + } + return newMatch + } + + private static def isMatch(ViatraQueryMatcher matcher, IPatternMatch match) { + val n = matcher.specification.parameters.length + if (n != match.specification.parameters.length) { + throw new IllegalArgumentException("Invalid number of match arguments") + } + val newMatch = matcher.newEmptyMatch + for (var int i = 0; i < n; i++) { + newMatch.set(i, match.get(i)) + } + return matcher.hasMatch(newMatch) + } + + static def isMulti(Object o) { + if (o instanceof EObject) { + switch (feature : o.eContainmentFeature) { + case LogicproblemPackage.eINSTANCE.logicProblem_Elements, + case PartialinterpretationPackage.eINSTANCE.partialInterpretation_NewElements: + false + case PartialinterpretationPackage.eINSTANCE.partialInterpretation_OpenWorldElements: + true + default: + throw new IllegalStateException("Unknown containment feature for element: " + feature) + } + } else { + false + } + } + + static def isMultiMatch(IPatternMatch match) { + checkMatch(match) + val n = match.specification.parameters.length + for (var int i = 2; i < n; i++) { + if (isMulti(match.get(i))) { + return true + } + } + false + } + + private static def checkMatch(IPatternMatch match) { + val n = match.specification.parameters.length + if (n < 2 || !(match.get(0) instanceof LogicProblem) || !(match.get(1) instanceof PartialInterpretation)) { + throw new IllegalArgumentException("Match is not from the partial interpretation") + } + } +} diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/CostObjectiveHint.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/CostObjectiveHint.xtend new file mode 100644 index 00000000..2434073d --- /dev/null +++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/CostObjectiveHint.xtend @@ -0,0 +1,68 @@ +package hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner.optimization + +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality.BoundSaturationListener +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality.ExtendedLinearExpressionBuilder +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality.LinearTypeConstraintHint +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality.LinearTypeExpressionBuilderFactory +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality.PolyhedronExtensionOperator +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.patterns.PatternGenerator +import java.util.Map +import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery +import org.eclipse.xtend.lib.annotations.Accessors + +abstract class CostObjectiveHint implements LinearTypeConstraintHint, BoundSaturationListener { + @Accessors ThreeValuedCostObjective objective + @Accessors IObjectiveBoundsProvider boundsProvider + + Integer bestUpper = null + + override getAdditionalPatterns(PatternGenerator patternGenerator, Map fqnToPQuery) { + '''''' + } + + override createConstraintUpdater(LinearTypeExpressionBuilderFactory builderFactory) { + null + } + + def isExact() { + false + } + + def PolyhedronExtensionOperator createPolyhedronExtensionOperator( + Map costElementMatchers) { + null + } + + def setObjective(ThreeValuedCostObjective objective) { + if (this.objective !== null) { + throw new IllegalStateException("Objective was already set") + } + this.objective = objective + } + + def setBoundsProvider(IObjectiveBoundsProvider boundsProvider) { + if (this.boundsProvider !== null) { + throw new IllegalStateException("Objective bounds provider was already set") + } + this.boundsProvider = boundsProvider + } + + protected def buildWithBounds(ExtendedLinearExpressionBuilder builder) { + val bounds = builder.build(this) + if (objective !== null && boundsProvider !== null) { + boundsProvider.computeRequiredBounds(objective, bounds) + } + if (exact && bestUpper !== null) { + bounds.tightenLowerBound(bestUpper) + } + bounds + } + + override boundsSaturated(Integer lower, Integer upper) { + if (upper !== null && (bestUpper === null || bestUpper < upper)) { + bestUpper = upper + } + objective?.boundsSaturated(lower, upper) + } + +} diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/IObjectiveBoundsProvider.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/IObjectiveBoundsProvider.xtend new file mode 100644 index 00000000..3c4d36a5 --- /dev/null +++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/IObjectiveBoundsProvider.xtend @@ -0,0 +1,8 @@ +package hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner.optimization + +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality.Bounds +import org.eclipse.viatra.dse.objectives.IObjective + +interface IObjectiveBoundsProvider { + def void computeRequiredBounds(IObjective objective, Bounds bounds) +} diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/ThreeValuedCostObjective.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/ThreeValuedCostObjective.xtend index 0a6fd55b..9b1a7e9f 100644 --- a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/ThreeValuedCostObjective.xtend +++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/ThreeValuedCostObjective.xtend @@ -1,85 +1,80 @@ package hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner.optimization -import com.google.common.collect.ImmutableList -import java.util.Collection +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality.BoundSaturationListener +import java.util.Map import org.eclipse.viatra.dse.base.ThreadContext -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.xtend.lib.annotations.Data +import org.eclipse.xtend.lib.annotations.Accessors -@Data -class ThreeValuedCostElement { - val IQuerySpecification> currentMatchQuery - val IQuerySpecification> mayMatchQuery - val IQuerySpecification> mustMatchQuery - val int weight -} - -class ThreeValuedCostObjective extends AbstractThreeValuedObjective { - val Collection costElements - Collection matchers +class ThreeValuedCostObjective extends AbstractThreeValuedObjective implements BoundSaturationListener { + @Accessors val Map matchers + double lowerBoundHint = Double.NEGATIVE_INFINITY + double upperBoundHint = Double.POSITIVE_INFINITY - new(String name, Collection costElements, ObjectiveKind kind, ObjectiveThreshold threshold, + new(String name, Map matchers, ObjectiveKind kind, ObjectiveThreshold threshold, int level) { super(name, kind, threshold, level) - this.costElements = costElements + this.matchers = matchers } override createNew() { - new ThreeValuedCostObjective(name, costElements, kind, threshold, level) + // new ThreeValuedCostObjective(name, matchers, kind, threshold, level) + throw new UnsupportedOperationException("ThreeValuedCostObjective can only be used from a single thread") } override init(ThreadContext context) { - val queryEngine = context.queryEngine - matchers = ImmutableList.copyOf(costElements.map [ element | - new CostElementMatchers( - queryEngine.getMatcher(element.currentMatchQuery), - queryEngine.getMatcher(element.mayMatchQuery), - queryEngine.getMatcher(element.mustMatchQuery), - element.weight - ) - ]) } override getRawFitness(ThreadContext context) { - var int cost = 0 - for (matcher : matchers) { - cost += matcher.weight * matcher.currentMatcher.countMatches + var double cost = 0 + for (matcher : matchers.values) { + cost += matcher.weight * matcher.currentNumberOfMatches } - cost as double + cost } override getLowestPossibleFitness(ThreadContext threadContext) { - var int cost = 0 - for (matcher : matchers) { + var double cost = 0 + for (matcher : matchers.values) { if (matcher.weight >= 0) { - cost += matcher.weight * matcher.mustMatcher.countMatches - } else if (matcher.mayMatcher.countMatches > 0) { - // TODO Count may matches. - return Double.NEGATIVE_INFINITY + cost += matcher.weight * matcher.minimumNumberOfMatches + } else { + cost += matcher.weight * matcher.maximumNumberOfMatches } } - cost as double + val boundWithHint = Math.max(lowerBoundHint, cost) + if (boundWithHint > upperBoundHint) { + throw new IllegalStateException("Inconsistent cost bounds") + } + boundWithHint } override getHighestPossibleFitness(ThreadContext threadContext) { - var int cost = 0 - for (matcher : matchers) { + var double cost = 0 + for (matcher : matchers.values) { if (matcher.weight <= 0) { - cost += matcher.weight * matcher.mustMatcher.countMatches - } else if (matcher.mayMatcher.countMatches > 0) { - return Double.POSITIVE_INFINITY + cost += matcher.weight * matcher.minimumNumberOfMatches + } else { + cost += matcher.weight * matcher.maximumNumberOfMatches } } - cost as double + val boundWithHint = Math.min(upperBoundHint, cost) + if (boundWithHint < lowerBoundHint) { + throw new IllegalStateException("Inconsistent cost bounds") + } + boundWithHint } - @Data - private static class CostElementMatchers { - val ViatraQueryMatcher currentMatcher - val ViatraQueryMatcher mayMatcher - val ViatraQueryMatcher mustMatcher - val int weight + override boundsSaturated(Integer lower, Integer upper) { + lowerBoundHint = if (lower === null) { + Double.NEGATIVE_INFINITY + } else { + lower + } + upperBoundHint = if (upper === null) { + Double.POSITIVE_INFINITY + } else { + upper + } + println('''Bounds saturated: «lower»..«upper»''') } } diff --git a/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/ThreeValuedCostObjectiveProvider.xtend b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/ThreeValuedCostObjectiveProvider.xtend new file mode 100644 index 00000000..c2750acd --- /dev/null +++ b/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner/src/hu/bme/mit/inf/dslreasoner/viatrasolver/reasoner/optimization/ThreeValuedCostObjectiveProvider.xtend @@ -0,0 +1,205 @@ +package hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner.optimization + +import com.google.common.collect.ImmutableList +import com.google.common.collect.ImmutableMap +import com.google.common.collect.Lists +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.BoolTypeReference +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.ComplexTypeReference +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.IntTypeReference +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.RealTypeReference +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Relation +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.StringTypeReference +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.TypeDeclaration +import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.TypeReference +import hu.bme.mit.inf.dslreasoner.viatra2logic.viatra2logicannotations.TransfomedViatraQuery +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality.PolyhedronExtensionOperator +import hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.patterns.ModalPatternQueries +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialBooleanInterpretation +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialComplexTypeInterpretation +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialIntegerInterpretation +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialInterpretation +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialRealInterpretation +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.PartialStringInterpretation +import hu.bme.mit.inf.dslreasoner.viatrasolver.partialinterpretationlanguage.partialinterpretation.Scope +import hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner.CostObjectiveConfiguration +import hu.bme.mit.inf.dslreasoner.viatrasolver.reasoner.CostObjectiveElementConfiguration +import java.util.Collection +import java.util.Map +import org.eclipse.viatra.dse.objectives.IObjective +import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine +import org.eclipse.xtend.lib.annotations.Data + +@Data +class ThreeValuedCostObjectiveProviderResult { + val Collection objectives + val Collection hints + val Collection extensionOperators + val IObjective[][] leveledExtremalObjectives + val boolean optimizationProblem +} + +class ThreeValuedCostObjectiveProvider { + static val COST_OBJECTIVE_LEVEL = 3 + + val ViatraQueryEngine queryEngine + val Map modalRelationQueries + val Map qualifiedNameToRelationMap + val ParameterScopeBound defaultBounds + val ParameterScopeBound booleanBounds + val ParameterScopeBound integerBounds + val ParameterScopeBound realBounds + val ParameterScopeBound stringBounds + val Map typeDeclarationToBoundsMap + + new(ViatraQueryEngine queryEngine, PartialInterpretation interpretation, + Map modalRelationQueries) { + this.queryEngine = queryEngine + this.modalRelationQueries = modalRelationQueries + qualifiedNameToRelationMap = ImmutableMap.copyOf( + interpretation.problem.annotations.filter(TransfomedViatraQuery). + toMap([patternFullyQualifiedName], [target])) + defaultBounds = new PartialInterpretationBasedParameterScopeBound(interpretation) + var ParameterScopeBound booleanBounds = null + var ParameterScopeBound integerBounds = null + var ParameterScopeBound realBounds = null + var ParameterScopeBound stringBounds = null + val typeDeclarationToBoundsMapBuilder = ImmutableMap.builder + for (scope : interpretation.scopes) { + val bounds = new ScopeBasedParameterScopeBound(scope) + switch (typeInterpretation : scope.targetTypeInterpretation) { + PartialBooleanInterpretation: + if (booleanBounds === null) { + booleanBounds = bounds + } else { + throw new IllegalStateException("Duplicate partial boolean interpretation") + } + PartialIntegerInterpretation: + if (integerBounds === null) { + integerBounds = bounds + } else { + throw new IllegalStateException("Duplicate partial integer interpretation") + } + PartialRealInterpretation: + if (realBounds === null) { + realBounds = bounds + } else { + throw new IllegalStateException("Duplicate partial real interpretation") + } + PartialStringInterpretation: + if (stringBounds === null) { + stringBounds = bounds + } else { + throw new IllegalStateException("Duplicate partial string interpretation") + } + PartialComplexTypeInterpretation: + typeDeclarationToBoundsMapBuilder.put(typeInterpretation.interpretationOf, bounds) + } + } + this.booleanBounds = booleanBounds ?: defaultBounds + this.integerBounds = integerBounds ?: defaultBounds + this.realBounds = realBounds ?: defaultBounds + this.stringBounds = stringBounds ?: defaultBounds + typeDeclarationToBoundsMap = typeDeclarationToBoundsMapBuilder.build + } + + def getCostObjectives(Collection costObjectives) { + val objectives = ImmutableList.builder + val hints = ImmutableList.builder + val extensionOperators = ImmutableList.builder + val extremalObjectives = Lists.newArrayListWithExpectedSize(costObjectives.size) + for (entry : costObjectives.indexed) { + val objectiveName = '''costObjective«entry.key»''' + val objectiveConfig = entry.value + val costObjective = transformCostObjective(objectiveConfig, objectiveName) + objectives.add(costObjective) + if (objectiveConfig.findExtremum) { + extremalObjectives += costObjective + } + val hint = objectiveConfig.hint + if (hint !== null) { + hints.add(hint) + hint.objective = costObjective + val extensionOperator = hint.createPolyhedronExtensionOperator(costObjective.matchers) + if (extensionOperator !== null) { + extensionOperators.add(extensionOperator) + } + } + } + new ThreeValuedCostObjectiveProviderResult( + objectives.build, + hints.build, + extensionOperators.build, + newArrayList(extremalObjectives), + !extremalObjectives.empty + ) + } + + private def transformCostObjective(CostObjectiveConfiguration configuration, String name) { + val costElements = ImmutableMap.copyOf(configuration.elements.toMap([patternQualifiedName], [ + transformCostElement + ])) + new ThreeValuedCostObjective(name, costElements, configuration.kind, configuration.threshold, + COST_OBJECTIVE_LEVEL) + } + + private def transformCostElement(CostObjectiveElementConfiguration elementConfig) { + val relationName = elementConfig.patternQualifiedName + val modalQueries = modalRelationQueries.get(relationName) + if (modalQueries === null) { + throw new IllegalArgumentException("Unknown relation queries: " + relationName) + } + val relation = qualifiedNameToRelationMap.get(relationName) + if (relation === null) { + throw new IllegalArgumentException("Unknown transformed relation: " + relationName) + } + val parameterBounds = ImmutableList.copyOf(relation.parameters.map[parameterBound]) + new CostElementMatchers( + queryEngine.getMatcher(modalQueries.currentQuery), + queryEngine.getMatcher(modalQueries.mayQuery), + queryEngine.getMatcher(modalQueries.mustQuery), + parameterBounds, + elementConfig.weight + ) + } + + private def getParameterBound(TypeReference typeReference) { + switch (typeReference) { + BoolTypeReference: booleanBounds + IntTypeReference: integerBounds + RealTypeReference: realBounds + StringTypeReference: stringBounds + ComplexTypeReference: typeDeclarationToBoundsMap.getOrDefault(typeReference.referred, defaultBounds) + } + } + + private static abstract class AbstractParameterScopeBound implements ParameterScopeBound { + override getUpperBound() { + val rawValue = rawUpperBound + if (rawValue < 0) { + Double.POSITIVE_INFINITY + } else { + rawValue + } + } + + protected def int getRawUpperBound() + } + + @Data + private static class ScopeBasedParameterScopeBound extends AbstractParameterScopeBound { + val Scope scope + + override protected getRawUpperBound() { + scope.maxNewElements + } + } + + @Data + private static class PartialInterpretationBasedParameterScopeBound extends AbstractParameterScopeBound { + val PartialInterpretation interpretation + + override protected getRawUpperBound() { + interpretation.maxNewElements + } + } +} -- cgit v1.2.3-70-g09d2