Skip to content

Commit 3bf484d

Browse files
authored
Merge pull request #372 from Echtzeitsysteme/feature/implication-shortcuts
Special transformations for operators => and <=>
2 parents f3f862c + 537ef78 commit 3bf484d

31 files changed

Lines changed: 1519 additions & 12 deletions
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package org.emoflon.gips.build.gipsl.preprocess;
2+
3+
import java.util.Objects;
4+
5+
import org.eclipse.emf.ecore.util.EcoreUtil;
6+
import org.emoflon.gips.gipsl.gipsl.GipsArithmeticExpression;
7+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanBracket;
8+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanConjunction;
9+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanDisjunction;
10+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanExpression;
11+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanImplication;
12+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanLiteral;
13+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanNegation;
14+
import org.emoflon.gips.gipsl.gipsl.GipsConstraint;
15+
import org.emoflon.gips.gipsl.gipsl.GipsRelationalExpression;
16+
import org.emoflon.gips.gipsl.gipsl.GipslFactory;
17+
import org.emoflon.gips.gipsl.gipsl.GipslPackage;
18+
19+
public class GipslPreprocessor {
20+
21+
private final static PreprocessorRule[] rules = new PreprocessorRule[] { //
22+
new RuleEquivalenceShortcutA(), //
23+
new RuleEquivalenceShortcutB(), //
24+
new RuleEquivalenceShortcutC(), //
25+
new RuleEquivalenceShortcutD(), //
26+
new RuleImplicationShortcutA() };
27+
28+
private final GipslFactory factory;
29+
30+
public GipslPreprocessor() {
31+
this(GipslPackage.eINSTANCE.getGipslFactory());
32+
}
33+
34+
public GipslPreprocessor(GipslFactory factory) {
35+
this.factory = Objects.requireNonNull(factory, "factory");
36+
}
37+
38+
public GipsBooleanExpression preprocess(GipsConstraint constraint) {
39+
GipsBooleanExpression expression = constraint.getExpression();
40+
41+
// This part can be used for multi-pass rule application (whenever this may
42+
// becomes relevant in the future). It is commented out because, at present, no
43+
// rule requires multiple passes.
44+
// GipsBooleanExpression currentPass = applyRules(expression);
45+
// GipsBooleanExpression previousPass = null;
46+
//
47+
// while (currentPass != null) {
48+
// previousPass = currentPass;
49+
// currentPass = applyRules(currentPass);
50+
// }
51+
//
52+
// return previousPass != null ? previousPass : expression;
53+
54+
GipsBooleanExpression newExpression = tryToApplyRules(expression);
55+
return newExpression != null ? newExpression : expression;
56+
}
57+
58+
private GipsBooleanExpression tryToApplyRules(GipsBooleanExpression expression) {
59+
// depth first
60+
var newExpression = switch (expression) {
61+
case GipsBooleanImplication implication -> tryExpand(implication);
62+
case GipsBooleanConjunction conjunction -> tryExpand(conjunction);
63+
case GipsBooleanDisjunction disjunction -> tryExpand(disjunction);
64+
case GipsBooleanBracket bracket -> tryExpand(bracket);
65+
case GipsBooleanNegation negation -> tryExpand(negation);
66+
case GipsBooleanLiteral literal -> null;
67+
case GipsArithmeticExpression arithmetic -> null;
68+
case GipsRelationalExpression relational -> null;
69+
default -> throw new IllegalArgumentException("Unexpected value: " + expression);
70+
};
71+
72+
if (newExpression != null) {
73+
var replacement = tryAllRules(newExpression);
74+
return replacement != null ? replacement : newExpression;
75+
}
76+
77+
return tryAllRules(expression);
78+
}
79+
80+
private GipsBooleanExpression tryExpand(GipsBooleanImplication expression) {
81+
var newLeft = tryToApplyRules(expression.getLeft());
82+
var newRight = tryToApplyRules(expression.getRight());
83+
if (newLeft == null && newRight == null)
84+
return null;
85+
86+
var newExpression = factory.createGipsBooleanImplication();
87+
newExpression.setOperator(expression.getOperator());
88+
newExpression.setLeft(newLeft != null ? newLeft : EcoreUtil.copy(expression.getLeft()));
89+
newExpression.setRight(newRight != null ? newRight : EcoreUtil.copy(expression.getRight()));
90+
return newExpression;
91+
}
92+
93+
private GipsBooleanExpression tryExpand(GipsBooleanConjunction expression) {
94+
var newLeft = tryToApplyRules(expression.getLeft());
95+
var newRight = tryToApplyRules(expression.getRight());
96+
if (newLeft == null && newRight == null)
97+
return null;
98+
99+
var newExpression = factory.createGipsBooleanConjunction();
100+
newExpression.setLeft(newLeft != null ? newLeft : EcoreUtil.copy(expression.getLeft()));
101+
newExpression.setRight(newRight != null ? newRight : EcoreUtil.copy(expression.getRight()));
102+
return newExpression;
103+
}
104+
105+
private GipsBooleanExpression tryExpand(GipsBooleanDisjunction expression) {
106+
var newLeft = tryToApplyRules(expression.getLeft());
107+
var newRight = tryToApplyRules(expression.getRight());
108+
if (newLeft == null && newRight == null)
109+
return null;
110+
111+
var newExpression = factory.createGipsBooleanDisjunction();
112+
newExpression.setLeft(newLeft != null ? newLeft : EcoreUtil.copy(expression.getLeft()));
113+
newExpression.setRight(newRight != null ? newRight : EcoreUtil.copy(expression.getRight()));
114+
return newExpression;
115+
}
116+
117+
private GipsBooleanExpression tryExpand(GipsBooleanBracket expression) {
118+
var newOperand = tryToApplyRules(expression.getOperand());
119+
if (newOperand == null)
120+
return null;
121+
122+
var newExpression = factory.createGipsBooleanBracket();
123+
newExpression.setOperand(newOperand);
124+
return newExpression;
125+
}
126+
127+
private GipsBooleanExpression tryExpand(GipsBooleanNegation expression) {
128+
var newOperand = tryToApplyRules(expression.getOperand());
129+
if (newOperand == null)
130+
return null;
131+
132+
var newExpression = factory.createGipsBooleanNegation();
133+
newExpression.setOperand(newOperand);
134+
return newExpression;
135+
}
136+
137+
private GipsBooleanExpression tryAllRules(GipsBooleanExpression expression) {
138+
for (var rule : rules) {
139+
GipsBooleanExpression newExpression = rule.tryRule(factory, expression);
140+
if (newExpression != null)
141+
return newExpression;
142+
}
143+
144+
return null;
145+
}
146+
147+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.emoflon.gips.build.gipsl.preprocess;
2+
3+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanExpression;
4+
import org.emoflon.gips.gipsl.gipsl.GipslFactory;
5+
6+
public interface PreprocessorRule {
7+
8+
/**
9+
* Attempts to apply the given rule to the expression. The original expression
10+
* will not be modified. Instead, this method will return a replacement if the
11+
* expression can be modified by the rule.
12+
*
13+
* @param factory
14+
* @param expression which should be 'modified' by this rule
15+
* @return the replacement expression, if the rule was applicable
16+
*/
17+
GipsBooleanExpression tryRule(GipslFactory factory, GipsBooleanExpression expression);
18+
19+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package org.emoflon.gips.build.gipsl.preprocess;
2+
3+
import java.util.ArrayList;
4+
import java.util.LinkedList;
5+
import java.util.List;
6+
7+
import org.eclipse.emf.ecore.util.EcoreUtil;
8+
import org.emoflon.gips.build.preference.PluginPreferences;
9+
import org.emoflon.gips.gipsl.generator.GeneratorUtil;
10+
import org.emoflon.gips.gipsl.gipsl.GipsArithmeticExpression;
11+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanExpression;
12+
import org.emoflon.gips.gipsl.gipsl.GipsProductOperator;
13+
import org.emoflon.gips.gipsl.gipsl.GipsSumOperator;
14+
import org.emoflon.gips.gipsl.gipsl.GipslFactory;
15+
import org.emoflon.gips.gipsl.gipsl.RelationalOperator;
16+
import org.emoflon.gips.gipsl.special.pattern.EquivalenceShortcutA;
17+
18+
/**
19+
*
20+
* Input:
21+
* <ul>
22+
* <li>A >= 1 <-> B == 1 (& C == 1 & ...)
23+
* </ul>
24+
*
25+
* With:
26+
* <ul>
27+
* <li>M >= 1
28+
* <li>A in [0, M]
29+
* <li>B, C, ... in [0, 1]
30+
* </ul>
31+
*
32+
* Output:
33+
* <ul>
34+
* <li>B (+ C + ...) + (1-n) <= A
35+
* <li>A <= B*M
36+
* <li>A <= C*M
37+
* <li>...
38+
* </ul>
39+
*
40+
*/
41+
public class RuleEquivalenceShortcutA implements PreprocessorRule {
42+
43+
private final EquivalenceShortcutA pattern = new EquivalenceShortcutA();
44+
45+
@Override
46+
public GipsBooleanExpression tryRule(GipslFactory factory, GipsBooleanExpression expression) {
47+
if (!pattern.matchPattern(expression))
48+
return null;
49+
50+
List<GipsBooleanExpression> conjuncts = new LinkedList<>();
51+
52+
if (pattern.otherNodes.size() == 1) {
53+
// B <= A
54+
var relational = factory.createGipsRelationalExpression();
55+
relational.setOperator(RelationalOperator.SMALLER_OR_EQUAL);
56+
relational.setLeft(EcoreUtil.copy(pattern.otherNodes.get(0)));
57+
relational.setRight(EcoreUtil.copy(pattern.nodeA));
58+
conjuncts.add(relational);
59+
60+
} else {
61+
List<GipsArithmeticExpression> summands = new ArrayList<>(conjuncts.size() + 2);
62+
63+
for (var element : pattern.otherNodes)
64+
summands.add(EcoreUtil.copy(element));
65+
66+
var minusN = factory.createGipsArithmeticLiteral();
67+
minusN.setValue(Integer.toString(1 - pattern.otherNodes.size()));
68+
summands.add(minusN);
69+
70+
// B + C + ... + (1-n)
71+
var sum = GeneratorUtil.sum(factory, GipsSumOperator.PLUS, summands);
72+
73+
// B + C + ... + (1-n) <= A
74+
var relational = factory.createGipsRelationalExpression();
75+
relational.setOperator(RelationalOperator.SMALLER_OR_EQUAL);
76+
relational.setLeft(sum);
77+
relational.setRight(EcoreUtil.copy(pattern.nodeA));
78+
conjuncts.add(relational);
79+
}
80+
81+
// A <= B*M
82+
// ...
83+
for (var element : pattern.otherNodes) {
84+
var bigM = GeneratorUtil.createArithmeticLiteral(factory,
85+
Double.toString(PluginPreferences.getBigMValue()));
86+
87+
var bigMProduct = factory.createGipsArithmeticProduct();
88+
bigMProduct.setOperator(GipsProductOperator.MULT);
89+
bigMProduct.setLeft(EcoreUtil.copy(element));
90+
bigMProduct.setRight(bigM);
91+
92+
var relational = factory.createGipsRelationalExpression();
93+
relational.setOperator(RelationalOperator.SMALLER_OR_EQUAL);
94+
relational.setLeft(EcoreUtil.copy(pattern.nodeA));
95+
relational.setRight(bigMProduct);
96+
conjuncts.add(relational);
97+
}
98+
99+
return GeneratorUtil.conjunction(factory, conjuncts);
100+
}
101+
102+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package org.emoflon.gips.build.gipsl.preprocess;
2+
3+
import java.util.ArrayList;
4+
import java.util.LinkedList;
5+
import java.util.List;
6+
7+
import org.eclipse.emf.ecore.util.EcoreUtil;
8+
import org.emoflon.gips.gipsl.generator.GeneratorUtil;
9+
import org.emoflon.gips.gipsl.gipsl.GipsArithmeticExpression;
10+
import org.emoflon.gips.gipsl.gipsl.GipsBooleanExpression;
11+
import org.emoflon.gips.gipsl.gipsl.GipsSumOperator;
12+
import org.emoflon.gips.gipsl.gipsl.GipslFactory;
13+
import org.emoflon.gips.gipsl.gipsl.RelationalOperator;
14+
import org.emoflon.gips.gipsl.special.pattern.EquivalenceShortcutB;
15+
16+
/**
17+
*
18+
* Input:
19+
* <ul>
20+
* <li>A == 1 <-> B == 1 (& C == 1 & ...)
21+
* </ul>
22+
*
23+
* With:
24+
* <ul>
25+
* <li>A in [0, 1]
26+
* <li>B in [0, 1]
27+
* </ul>
28+
*
29+
* Output:
30+
* <ul>
31+
* <li>B (+ C + ...) + (1-n) <= A
32+
* <li>A <= B
33+
* <li>A <= C
34+
* <li>...
35+
* </ul>
36+
*
37+
*/
38+
public class RuleEquivalenceShortcutB implements PreprocessorRule {
39+
40+
private final EquivalenceShortcutB pattern = new EquivalenceShortcutB();
41+
42+
@Override
43+
public GipsBooleanExpression tryRule(GipslFactory factory, GipsBooleanExpression expression) {
44+
if (!pattern.matchPattern(expression))
45+
return null;
46+
47+
List<GipsBooleanExpression> conjuncts = new LinkedList<>();
48+
49+
if (pattern.otherNodes.size() == 1) {
50+
// B <= A
51+
var relational = factory.createGipsRelationalExpression();
52+
relational.setOperator(RelationalOperator.SMALLER_OR_EQUAL);
53+
relational.setLeft(EcoreUtil.copy(pattern.otherNodes.get(0)));
54+
relational.setRight(EcoreUtil.copy(pattern.nodeA));
55+
conjuncts.add(relational);
56+
57+
} else {
58+
List<GipsArithmeticExpression> summands = new ArrayList<>(conjuncts.size() + 2);
59+
60+
for (var element : pattern.otherNodes)
61+
summands.add(EcoreUtil.copy(element));
62+
63+
var minusN = factory.createGipsArithmeticLiteral();
64+
minusN.setValue(Integer.toString(1 - pattern.otherNodes.size()));
65+
summands.add(minusN);
66+
67+
// B + C + ... + (1-n)
68+
var sum = GeneratorUtil.sum(factory, GipsSumOperator.PLUS, summands);
69+
70+
// B + C + ... + (1-n) <= A
71+
var relational = factory.createGipsRelationalExpression();
72+
relational.setOperator(RelationalOperator.SMALLER_OR_EQUAL);
73+
relational.setLeft(sum);
74+
relational.setRight(EcoreUtil.copy(pattern.nodeA));
75+
conjuncts.add(relational);
76+
}
77+
78+
// A <= B
79+
// ...
80+
for (var element : pattern.otherNodes) {
81+
var relational = factory.createGipsRelationalExpression();
82+
relational.setOperator(RelationalOperator.SMALLER_OR_EQUAL);
83+
relational.setLeft(EcoreUtil.copy(pattern.nodeA));
84+
relational.setRight(EcoreUtil.copy(element));
85+
conjuncts.add(relational);
86+
}
87+
88+
return GeneratorUtil.conjunction(factory, conjuncts);
89+
}
90+
91+
}

0 commit comments

Comments
 (0)