aboutsummaryrefslogtreecommitdiffstats
path: root/Solvers/VIATRA-Solver/hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra/src/hu/bme/mit/inf/dslreasoner/viatrasolver/logic2viatra/cardinality/RelationConstraintCalculator.xtend
blob: 7fec452f0b3c106ddd6263cc12ac36b33954280c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package hu.bme.mit.inf.dslreasoner.viatrasolver.logic2viatra.cardinality

import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet
import hu.bme.mit.inf.dslreasoner.ecore2logic.ecore2logicannotations.InverseRelationAssertion
import hu.bme.mit.inf.dslreasoner.ecore2logic.ecore2logicannotations.LowerMultiplicityAssertion
import hu.bme.mit.inf.dslreasoner.ecore2logic.ecore2logicannotations.UpperMultiplicityAssertion
import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.ComplexTypeReference
import hu.bme.mit.inf.dslreasoner.logic.model.logiclanguage.Relation
import hu.bme.mit.inf.dslreasoner.logic.model.logicproblem.LogicProblem
import java.util.HashMap
import java.util.List
import org.eclipse.xtend.lib.annotations.Data

@Data
class RelationConstraints {
	val List<RelationMultiplicityConstraint> multiplicityConstraints
}

@Data
class RelationMultiplicityConstraint {
	Relation relation
	Relation inverseRelation
	boolean containment
	boolean container
	int lowerBound
	int upperBound
	int inverseUpperBound

	def isUpperBoundFinite() {
		upperBound >= 0
	}

	private def isInverseUpperBoundFinite() {
		inverseUpperBound >= 0
	}

	private def canHaveMultipleSourcesPerTarget() {
		inverseUpperBound != 1
	}

	def constrainsUnfinished() {
		lowerBound >= 1 && (!container || lowerBound >= 2)
	}

	def constrainsUnrepairable() {
		// TODO Optimize the unrepairable matches computation,
		// or come up with a heuristic when does computing unrepairables worth the overhead.
		constrainsUnfinished && canHaveMultipleSourcesPerTarget && reference
	}

	def constrainsRemainingInverse() {
		lowerBound >= 1 && !containment && !container && inverseUpperBoundFinite && reference
	}

	def constrainsRemainingContents() {
		containment
	}

	def isActive() {
		constrainsUnfinished || constrainsUnrepairable || constrainsRemainingInverse || constrainsRemainingContents
	}

	def isSourceTypeComplex() {
		getParamTypeReference(0) instanceof ComplexTypeReference
	}
	
	def isTargetTypeComplex() {
		getParamTypeReference(1) instanceof ComplexTypeReference
	}
	
	def isReference() {
		sourceTypeComplex && targetTypeComplex
	}

	def getSourceType() {
		getParamType(0)
	}

	def getTargetType() {
		getParamType(1)
	}

	private def getParamTypeReference(int i) {
		val parameters = relation.parameters
		if (i < parameters.size) {
			return parameters.get(i)
		}
		throw new IllegalArgumentException("Argument index out of range")
	}
	
	private def getParamType(int i) {
		val reference = getParamTypeReference(i)
		if (reference instanceof ComplexTypeReference) {
			return reference.referred
		}
		throw new IllegalArgumentException("Constraint with primitive type")
	}
}

class RelationConstraintCalculator {
	def calculateRelationConstraints(LogicProblem problem) {
		val containmentRelations = switch (problem.containmentHierarchies.size) {
			case 0:
				<Relation>emptySet
			case 1:
				ImmutableSet.copyOf(problem.containmentHierarchies.head.containmentRelations)
			default:
				throw new IllegalArgumentException("Only a single containment hierarchy is supported")
		}
		val inverseRelations = new HashMap<Relation, Relation>
		val lowerMultiplicities = new HashMap<Relation, Integer>
		val upperMultiplicities = new HashMap<Relation, Integer>
		for (relation : problem.relations) {
			lowerMultiplicities.put(relation, 0)
			upperMultiplicities.put(relation, -1)
		}
		for (annotation : problem.annotations) {
			switch (annotation) {
				InverseRelationAssertion: {
					inverseRelations.put(annotation.inverseA, annotation.inverseB)
					inverseRelations.put(annotation.inverseB, annotation.inverseA)
				}
				LowerMultiplicityAssertion:
					lowerMultiplicities.put(annotation.relation, annotation.lower)
				UpperMultiplicityAssertion:
					upperMultiplicities.put(annotation.relation, annotation.upper)
			}
		}
		val multiplicityConstraintsBuilder = ImmutableList.builder()
		for (relation : problem.relations) {
			val containment = containmentRelations.contains(relation)
			val lowerMultiplicity = lowerMultiplicities.get(relation)
			val upperMultiplicity = upperMultiplicities.get(relation)
			var container = false
			var inverseUpperMultiplicity = -1
			val inverseRelation = inverseRelations.get(relation)
			if (inverseRelation !== null) {
				inverseUpperMultiplicity = upperMultiplicities.get(inverseRelation)
				container = containmentRelations.contains(inverseRelation)
			}
			if (containment) {
				inverseUpperMultiplicity = 1
			}
			val constraint = new RelationMultiplicityConstraint(relation, inverseRelation, containment, container,
				lowerMultiplicity, upperMultiplicity, inverseUpperMultiplicity)
			if (constraint.isActive) {
				if (relation.parameters.size != 2) {
					throw new IllegalArgumentException('''Relation «relation.name» has multiplicity or containment constraints, but it is not binary''')
				}
				multiplicityConstraintsBuilder.add(constraint)
			}
		}
		new RelationConstraints(multiplicityConstraintsBuilder.build)
	}
}