• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LearnLib / automatalib / 13138848026

04 Feb 2025 02:53PM UTC coverage: 92.108% (+2.2%) from 89.877%
13138848026

push

github

mtf90
[maven-release-plugin] prepare release automatalib-0.12.0

16609 of 18032 relevant lines covered (92.11%)

1.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.95
/modelchecking/m3c/src/main/java/net/automatalib/modelchecker/m3c/solver/AbstractDDSolver.java
1
/* Copyright (C) 2013-2025 TU Dortmund University
2
 * This file is part of AutomataLib <https://automatalib.net>.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.automatalib.modelchecker.m3c.solver;
17

18
import java.util.ArrayList;
19
import java.util.BitSet;
20
import java.util.Collection;
21
import java.util.Collections;
22
import java.util.HashMap;
23
import java.util.HashSet;
24
import java.util.Iterator;
25
import java.util.List;
26
import java.util.Map;
27
import java.util.Map.Entry;
28
import java.util.Objects;
29
import java.util.Set;
30

31
import net.automatalib.common.util.HashUtil;
32
import net.automatalib.common.util.mapping.Mapping;
33
import net.automatalib.common.util.mapping.MutableMapping;
34
import net.automatalib.graph.ContextFreeModalProcessSystem;
35
import net.automatalib.graph.ProceduralModalProcessGraph;
36
import net.automatalib.modelchecker.m3c.formula.AndNode;
37
import net.automatalib.modelchecker.m3c.formula.AtomicNode;
38
import net.automatalib.modelchecker.m3c.formula.BoxNode;
39
import net.automatalib.modelchecker.m3c.formula.DependencyGraph;
40
import net.automatalib.modelchecker.m3c.formula.EquationalBlock;
41
import net.automatalib.modelchecker.m3c.formula.FormulaNode;
42
import net.automatalib.modelchecker.m3c.formula.NotNode;
43
import net.automatalib.modelchecker.m3c.formula.OrNode;
44
import net.automatalib.modelchecker.m3c.formula.TrueNode;
45
import net.automatalib.modelchecker.m3c.formula.visitor.CTLToMuCalc;
46
import net.automatalib.modelchecker.m3c.transformer.AbstractPropertyTransformer;
47
import net.automatalib.modelchecker.m3c.transformer.TransformerSerializer;
48
import net.automatalib.modelchecking.ModelChecker;
49
import net.automatalib.ts.modal.transition.ModalEdgeProperty;
50
import net.automatalib.ts.modal.transition.ProceduralModalEdgeProperty;
51
import org.checkerframework.checker.initialization.qual.UnderInitialization;
52
import org.checkerframework.checker.nullness.qual.KeyFor;
53
import org.checkerframework.checker.nullness.qual.NonNull;
54
import org.checkerframework.checker.nullness.qual.Nullable;
55

56
/**
57
 * Base implementation of the model checker which supports different types of property transformers. The
58
 * {@link ModelChecker} is (currently) implemented on the basis of the {@link WitnessTreeExtractor} including all its
59
 * restrictions.
60
 *
61
 * @param <T>
62
 *         property transformer type
63
 * @param <L>
64
 *         edge label type
65
 * @param <AP>
66
 *         atomic proposition type
67
 */
68
abstract class AbstractDDSolver<T extends AbstractPropertyTransformer<T, L, AP>, L, AP>
2✔
69
        implements ModelChecker<L, ContextFreeModalProcessSystem<L, AP>, FormulaNode<L, AP>, WitnessTree<L, AP>> {
70

71
    // Attributes that are constant for a given CFMPS
72
    private final @KeyFor("workUnits") L mainProcess;
73

74
    // Attributes that change for each formula
75
    private TransformerSerializer<T, L, AP> serializer;
76
    private DependencyGraph<L, AP> dependencyGraph;
77
    private int currentBlockIndex;
78
    // Per-procedure attributes
79
    private final Map<L, WorkUnit<?, ?>> workUnits;
80
    // Per-action attributes
81
    private Map<L, T> mustTransformers;
82
    private Map<L, T> mayTransformers;
83

84
    AbstractDDSolver(ContextFreeModalProcessSystem<L, AP> cfmps) {
85
        this(validateCFMPS(cfmps), cfmps);
2✔
86
    }
2✔
87

88
    // utility constructor to prevent finalizer attacks, see SEI CERT Rule OBJ-11
89
    @SuppressWarnings("assignment") // validateCFPS verifies that mainProcess is a key in cfmps.getPMPGs()
90
    private AbstractDDSolver(L mainProcess, ContextFreeModalProcessSystem<L, AP> cfmps) {
2✔
91
        final Map<L, ProceduralModalProcessGraph<?, L, ?, AP, ?>> pmpgs = cfmps.getPMPGs();
2✔
92

93
        this.workUnits = new HashMap<>(HashUtil.capacity(pmpgs.size()));
2✔
94
        for (Map.Entry<L, ProceduralModalProcessGraph<?, L, ?, AP, ?>> e : pmpgs.entrySet()) {
2✔
95
            final L label = e.getKey();
2✔
96
            final ProceduralModalProcessGraph<?, L, ?, AP, ?> pmpg = e.getValue();
2✔
97
            workUnits.put(label, initializeWorkUnits(label, pmpg));
2✔
98
        }
2✔
99

100
        this.mainProcess = mainProcess;
2✔
101
    }
2✔
102

103
    private static <L, AP> L validateCFMPS(ContextFreeModalProcessSystem<L, AP> cfmps) {
104
        // TODO handle empty CFMPSs
105
        final L mainProcess = cfmps.getMainProcess();
2✔
106
        final Map<L, ProceduralModalProcessGraph<?, L, ?, AP, ?>> pmpGs = cfmps.getPMPGs();
2✔
107

108
        if (mainProcess == null || !pmpGs.containsKey(mainProcess)) {
2✔
109
            throw new IllegalArgumentException("The main process is undefined or has no corresponding MPG.");
2✔
110
        }
111

112
        pmpGs.forEach(AbstractDDSolver::checkPMPG);
2✔
113

114
        return mainProcess;
2✔
115
    }
116

117
    private static <N, L, AP> void checkPMPG(L label, ProceduralModalProcessGraph<N, L, ?, AP, ?> pmpg) {
118
        final N initialNode = pmpg.getInitialNode();
2✔
119
        if (initialNode == null) {
2✔
120
            throw new IllegalArgumentException("PMPG '" + label + "' has no initial node");
×
121
        }
122

123
        final N finalNode = pmpg.getFinalNode();
2✔
124
        if (finalNode == null) {
2✔
125
            throw new IllegalArgumentException("PMPG '" + label + "' has no final node");
×
126
        }
127

128
        if (!isGuarded(pmpg, initialNode)) {
2✔
129
            throw new IllegalArgumentException(String.format(
2✔
130
                    "PMPG '%s' is not guarded. All initial transitions must be labelled with atomic actions.",
131
                    label));
132
        }
133

134
        if (!isTerminating(pmpg, finalNode)) {
2✔
135
            throw new IllegalArgumentException(String.format(
×
136
                    "PMPG '%s' is not terminating. The final node is not allowed to have outgoing transitions.",
137
                    label));
138
        }
139
    }
2✔
140

141
    private static <N, L, E, AP> boolean isGuarded(ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg, N initialNode) {
142
        for (E initialTransition : pmpg.getOutgoingEdges(initialNode)) {
2✔
143
            if (pmpg.getEdgeProperty(initialTransition).isProcess()) {
2✔
144
                return false;
2✔
145
            }
146
        }
2✔
147
        return true;
2✔
148
    }
149

150
    private static <N, L, E, AP> boolean isTerminating(ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg, N finalNode) {
151
        return pmpg.getOutgoingEdges(finalNode).isEmpty();
2✔
152
    }
153

154
    private <N> WorkUnit<N, ?> initializeWorkUnits(@UnderInitialization AbstractDDSolver<T, L, AP> this,
155
                                                   L label,
156
                                                   ProceduralModalProcessGraph<N, L, ?, AP, ?> pmpg) {
157
        return new WorkUnit<>(label, pmpg, initPredecessorsMapping(pmpg));
2✔
158
    }
159

160
    private static <N, L, E, AP> Mapping<N, Set<N>> initPredecessorsMapping(ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg) {
161

162
        final MutableMapping<N, Set<N>> nodeToPredecessors = pmpg.createStaticNodeMapping();
2✔
163

164
        for (N sourceNode : pmpg.getNodes()) {
2✔
165
            for (E outgoingEdge : pmpg.getOutgoingEdges(sourceNode)) {
2✔
166
                final N targetNode = pmpg.getTarget(outgoingEdge);
2✔
167
                Set<N> nodePredecessors = nodeToPredecessors.get(targetNode);
2✔
168

169
                if (nodePredecessors == null) {
2✔
170
                    nodePredecessors = new HashSet<>();
2✔
171
                    nodeToPredecessors.put(targetNode, nodePredecessors);
2✔
172
                }
173

174
                nodePredecessors.add(sourceNode);
2✔
175
            }
2✔
176
        }
2✔
177

178
        return nodeToPredecessors;
2✔
179
    }
180

181
    @Override
182
    public @Nullable WitnessTree<L, AP> findCounterExample(ContextFreeModalProcessSystem<L, AP> cfmps,
183
                                                           Collection<? extends L> inputs,
184
                                                           FormulaNode<L, AP> formulaNode) {
185
        final NotNode<L, AP> negatedFormula = new NotNode<>(formulaNode);
2✔
186
        final FormulaNode<L, AP> ast = ctlToMuCalc(negatedFormula).toNNF();
2✔
187

188
        initialize(ast);
2✔
189

190
        try {
191
            this.solveInternal(false, Collections.emptyList());
2✔
192

193
            final boolean sat = isSat();
2✔
194

195
            if (sat) {
2✔
196
                final Map<L, AbstractDDSolver<?, L, AP>.WorkUnit<?, ?>> units = Collections.unmodifiableMap(workUnits);
2✔
197
                return WitnessTreeExtractor.computeWitness(cfmps,
2✔
198
                                                           units,
199
                                                           dependencyGraph,
200
                                                           ast,
201
                                                           getAllAPDeadlockedNode());
2✔
202
            }
203
            return null;
2✔
204
        } finally {
205
            shutdownDDManager();
2✔
206
        }
207
    }
208

209
    public boolean solve(FormulaNode<L, AP> formula) {
210
        final FormulaNode<L, AP> ast = ctlToMuCalc(formula).toNNF();
2✔
211

212
        initialize(ast);
2✔
213

214
        try {
215
            this.solveInternal(false, Collections.emptyList());
2✔
216

217
            return isSat();
2✔
218
        } finally {
219
            shutdownDDManager();
2✔
220
        }
221
    }
222

223
    public SolverHistory<T, L, AP> solveAndRecordHistory(FormulaNode<L, AP> formula) {
224
        final List<SolverState<?, T, L, AP>> history = new ArrayList<>();
2✔
225
        final FormulaNode<L, AP> ast = ctlToMuCalc(formula).toNNF();
2✔
226

227
        initialize(ast);
2✔
228

229
        try {
230
            final Map<L, SolverData<?, T, L, AP>> data = new HashMap<>(HashUtil.capacity(this.workUnits.size()));
2✔
231
            for (Entry<L, WorkUnit<?, ?>> e : this.workUnits.entrySet()) {
2✔
232
                data.put(e.getKey(), createProcessData(e.getValue()));
2✔
233
            }
2✔
234

235
            this.solveInternal(true, history);
2✔
236

237
            final Map<L, List<String>> serializedMustTransformers = serializePropertyTransformerMap(mustTransformers);
2✔
238
            final Map<L, List<String>> serializedMayTransformers = serializePropertyTransformerMap(mayTransformers);
2✔
239
            final boolean isSat = isSat();
2✔
240

241
            return new SolverHistory<>(data, serializedMustTransformers, serializedMayTransformers, history, isSat);
2✔
242
        } finally {
243
            shutdownDDManager();
2✔
244
        }
245
    }
246

247
    private <N, E> SolverData<N, T, L, AP> createProcessData(WorkUnit<N, E> unit) {
248
        return new SolverData<>(unit.pmpg, serializePropertyTransformers(unit), computeSatisfiedSubformulas(unit));
2✔
249
    }
250

251
    private Map<L, List<String>> serializePropertyTransformerMap(Map<L, T> transformers) {
252
        final Map<L, List<String>> serializedTransformers = new HashMap<>(HashUtil.capacity(transformers.size()));
2✔
253
        for (Map.Entry<L, T> entry : transformers.entrySet()) {
2✔
254
            serializedTransformers.put(entry.getKey(), serializer.serialize(entry.getValue()));
2✔
255
        }
2✔
256
        return serializedTransformers;
2✔
257
    }
258

259
    private void solveInternal(boolean recordHistory, List<SolverState<?, T, L, AP>> history) {
260
        boolean workSetIsEmpty = false;
2✔
261
        while (!workSetIsEmpty) {
2✔
262
            workSetIsEmpty = true;
2✔
263
            for (Entry<L, WorkUnit<?, ?>> entry : workUnits.entrySet()) {
2✔
264
                workSetIsEmpty &= solveInternal(entry.getValue(), recordHistory, history);
2✔
265
            }
2✔
266
        }
267
    }
2✔
268

269
    private <N> boolean solveInternal(WorkUnit<N, ?> unit,
270
                                      boolean recordHistory,
271
                                      List<SolverState<?, T, L, AP>> history) {
272
        if (!unit.workSet.isEmpty()) {
2✔
273
            final Iterator<N> iter = unit.workSet.iterator();
2✔
274
            final N node = iter.next();
2✔
275
            iter.remove();
2✔
276

277
            final L label = unit.label;
2✔
278
            final List<T> compositions = updateNodeAndGetCompositions(unit, node);
2✔
279

280
            if (recordHistory) {
2✔
281
                final List<List<String>> serializedCompositions = new ArrayList<>(compositions.size());
2✔
282
                for (T composition : compositions) {
2✔
283
                    serializedCompositions.add(serializer.serialize(composition));
2✔
284
                }
2✔
285
                history.add(new SolverState<>(serializer.serialize(unit.propTransformers.get(node)),
2✔
286
                                              serializedCompositions,
287
                                              node,
288
                                              label,
289
                                              copyWorkSet(),
2✔
290
                                              getSatisfiedSubformulas(unit, node)));
2✔
291
            }
292

293
            return false;
2✔
294
        }
295

296
        return true;
2✔
297
    }
298

299
    private <N> List<T> updateNodeAndGetCompositions(WorkUnit<N, ?> unit, N node) {
300
        assert !Objects.equals(node, unit.pmpg.getFinalNode()) : "End node must not be updated!";
2✔
301
        final T nodeTransformer = getTransformer(unit, node);
2✔
302
        final List<T> compositions = createCompositions(unit, node);
2✔
303
        final T updatedTransformer = getUpdatedPropertyTransformer(unit, node, nodeTransformer, compositions);
2✔
304
        updateTransformerAndWorkSet(unit, node, nodeTransformer, updatedTransformer);
2✔
305
        return compositions;
2✔
306
    }
307

308
    private Map<L, Set<?>> copyWorkSet() {
309
        final Map<L, Set<?>> copy = new HashMap<>(HashUtil.capacity(workUnits.size()));
2✔
310
        for (Map.Entry<L, WorkUnit<?, ?>> e : workUnits.entrySet()) {
2✔
311
            copy.put(e.getKey(), copyWorkSet(e.getValue()));
2✔
312
        }
2✔
313
        return copy;
2✔
314
    }
315

316
    private <N> Set<N> copyWorkSet(WorkUnit<N, ?> unit) {
317
        return new HashSet<>(unit.workSet);
2✔
318
    }
319

320
    private <N> Mapping<N, List<FormulaNode<L, AP>>> computeSatisfiedSubformulas(WorkUnit<N, ?> unit) {
321
        final MutableMapping<N, List<FormulaNode<L, AP>>> result = unit.pmpg.createStaticNodeMapping();
2✔
322
        for (N node : unit.pmpg.getNodes()) {
2✔
323
            result.put(node, getSatisfiedSubformulas(unit, node));
2✔
324
        }
2✔
325

326
        return result;
2✔
327
    }
328

329
    private <N> List<FormulaNode<L, AP>> getSatisfiedSubformulas(WorkUnit<N, ?> unit, N node) {
330
        final BitSet output =
2✔
331
                unit.propTransformers.get(node).evaluate(dependencyGraph.toBoolArray(getAllAPDeadlockedNode()));
2✔
332
        final List<FormulaNode<L, AP>> satisfiedSubFormulas = new ArrayList<>();
2✔
333
        for (FormulaNode<L, AP> n : dependencyGraph.getFormulaNodes()) {
2✔
334
            if (output.get(n.getVarNumber())) {
2✔
335
                satisfiedSubFormulas.add(n);
2✔
336
            }
337
        }
2✔
338
        return satisfiedSubFormulas;
2✔
339
    }
340

341
    private BitSet getAllAPDeadlockedNode() {
342
        return getAllAPDeadlockedNode(workUnits.get(mainProcess).pmpg);
2✔
343
    }
344

345
    private <N, E, TP extends ProceduralModalEdgeProperty> BitSet getAllAPDeadlockedNode(ProceduralModalProcessGraph<N, L, E, AP, TP> mainMpg) {
346
        final BitSet satisfiedVariables = new BitSet();
2✔
347
        @SuppressWarnings("nullness") // we have checked non-nullness of final nodes in the constructor
348
        final @NonNull N finalNode = mainMpg.getFinalNode();
2✔
349
        for (int blockIdx = dependencyGraph.getBlocks().size() - 1; blockIdx >= 0; blockIdx--) {
2✔
350
            final EquationalBlock<L, AP> block = dependencyGraph.getBlock(blockIdx);
2✔
351
            for (FormulaNode<L, AP> node : block.getNodes()) {
2✔
352
                if (node instanceof TrueNode) {
2✔
353
                    satisfiedVariables.set(node.getVarNumber());
2✔
354
                } else if (node instanceof AtomicNode) {
2✔
355
                    final AP atomicProposition = ((AtomicNode<L, AP>) node).getProposition();
2✔
356
                    final Set<AP> finalNodeAPs = mainMpg.getAtomicPropositions(finalNode);
2✔
357
                    if (finalNodeAPs.contains(atomicProposition)) {
2✔
358
                        satisfiedVariables.set(node.getVarNumber());
2✔
359
                    }
360
                } else if (node instanceof BoxNode) {
2✔
361
                    /* End node has no outgoing edges */
362
                    satisfiedVariables.set(node.getVarNumber());
2✔
363
                } else if (node instanceof AndNode) {
2✔
364
                    final AndNode<L, AP> andNode = (AndNode<L, AP>) node;
2✔
365
                    if (satisfiedVariables.get(andNode.getVarNumberLeft()) &&
2✔
366
                        satisfiedVariables.get(andNode.getVarNumberRight())) {
2✔
367
                        satisfiedVariables.set(andNode.getVarNumber());
2✔
368
                    }
369
                } else if (node instanceof OrNode) {
2✔
370
                    final OrNode<L, AP> orNode = (OrNode<L, AP>) node;
2✔
371
                    if (satisfiedVariables.get(orNode.getVarNumberLeft()) ||
2✔
372
                        satisfiedVariables.get(orNode.getVarNumberRight())) {
2✔
373
                        satisfiedVariables.set(orNode.getVarNumber());
2✔
374
                    }
375
                } else if (node instanceof NotNode) {
2✔
376
                    final NotNode<L, AP> notNode = (NotNode<L, AP>) node;
2✔
377
                    if (!satisfiedVariables.get(notNode.getVarNumberChild())) {
2✔
378
                        satisfiedVariables.set(notNode.getVarNumber());
2✔
379
                    }
380
                }
381
            }
2✔
382
        }
383
        return satisfiedVariables;
2✔
384
    }
385

386
    private <N> T getTransformer(WorkUnit<N, ?> unit, N node) {
387
        return unit.propTransformers.get(node);
2✔
388
    }
389

390
    private <N, E> List<T> createCompositions(WorkUnit<N, E> unit, N node) {
391
        final ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg = unit.pmpg;
2✔
392
        final List<T> compositions = new ArrayList<>();
2✔
393
        for (E edge : pmpg.getOutgoingEdges(node)) {
2✔
394
            final N targetNode = pmpg.getTarget(edge);
2✔
395
            final T edgeTransformer = getEdgeTransformer(unit, edge);
2✔
396
            final T succTransformer = getTransformer(unit, targetNode);
2✔
397
            final T composition = edgeTransformer.compose(succTransformer);
2✔
398
            compositions.add(composition);
2✔
399
        }
2✔
400
        return compositions;
2✔
401
    }
402

403
    private <N> T getUpdatedPropertyTransformer(WorkUnit<N, ?> unit, N node, T nodeTransformer, List<T> compositions) {
404
        final EquationalBlock<L, AP> currentBlock = dependencyGraph.getBlock(currentBlockIndex);
2✔
405
        final Set<AP> atomicPropositions = unit.pmpg.getAtomicPropositions(node);
2✔
406
        return nodeTransformer.createUpdate(atomicPropositions, compositions, currentBlock);
2✔
407
    }
408

409
    private <N> void updateTransformerAndWorkSet(WorkUnit<N, ?> unit, N node, T nodeTransformer, T updatedTransformer) {
410
        if (!nodeTransformer.equals(updatedTransformer)) {
2✔
411
            unit.propTransformers.put(node, updatedTransformer);
2✔
412
            updateWorkSet(unit, node);
2✔
413
        }
414
        if (workSetIsEmpty() && currentBlockIndex > 0) {
2✔
415
            currentBlockIndex--;
2✔
416
            resetWorkSet();
2✔
417
        }
418
    }
2✔
419

420
    private <E> T getEdgeTransformer(WorkUnit<?, E> unit, E edge) {
421
        final T edgeTransformer;
422
        final ProceduralModalProcessGraph<?, L, E, AP, ?> pmpg = unit.pmpg;
2✔
423
        final L label = pmpg.getEdgeLabel(edge);
2✔
424
        if (isProcessEdge(pmpg, edge)) {
2✔
425
            final WorkUnit<?, ?> edgeUnit = workUnits.get(label);
2✔
426
            assert edgeUnit != null;
2✔
427
            edgeTransformer = getInitialEdgeTransformer(edgeUnit);
2✔
428
        } else {
2✔
429
            if (isMustEdge(pmpg, edge)) {
2✔
430
                if (mustTransformers.containsKey(label)) {
2✔
431
                    edgeTransformer = mustTransformers.get(label);
2✔
432
                } else {
433
                    edgeTransformer = createInitTransformerEdge(dependencyGraph, label, pmpg.getEdgeProperty(edge));
2✔
434
                    mustTransformers.put(label, edgeTransformer);
2✔
435
                }
436
            } else {
437
                if (mayTransformers.containsKey(label)) {
2✔
438
                    edgeTransformer = mayTransformers.get(label);
2✔
439
                } else {
440
                    edgeTransformer = createInitTransformerEdge(dependencyGraph, label, pmpg.getEdgeProperty(edge));
2✔
441
                    mayTransformers.put(label, edgeTransformer);
2✔
442
                }
443
            }
444
        }
445
        return edgeTransformer;
2✔
446
    }
447

448
    private <E> boolean isMustEdge(ProceduralModalProcessGraph<?, L, E, AP, ?> pmpg, E edge) {
449
        return pmpg.getEdgeProperty(edge).isMust();
2✔
450
    }
451

452
    private <N> void updateWorkSet(WorkUnit<N, ?> unit, N node) {
453
        if (Objects.equals(unit.pmpg.getInitialNode(), node)) {
2✔
454
            updateWorkSetStartNode(unit.label);
2✔
455
        }
456
        addPredecessorsToWorkSet(unit, node);
2✔
457
    }
2✔
458

459
    private boolean workSetIsEmpty() {
460
        for (WorkUnit<?, ?> unit : workUnits.values()) {
2✔
461
            if (!unit.workSet.isEmpty()) {
2✔
462
                return false;
2✔
463
            }
464
        }
2✔
465
        return true;
2✔
466
    }
467

468
    private void resetWorkSet() {
469
        for (WorkUnit<?, ?> value : this.workUnits.values()) {
2✔
470
            resetWorkSet(value);
2✔
471
        }
2✔
472
    }
2✔
473

474
    private <N> void resetWorkSet(WorkUnit<N, ?> unit) {
475
        unit.workSet = newWorkSet(unit.pmpg);
2✔
476
    }
2✔
477

478
    private <E> boolean isProcessEdge(ProceduralModalProcessGraph<?, L, E, AP, ?> pmpg, E edge) {
479
        return pmpg.getEdgeProperty(edge).isProcess();
2✔
480
    }
481

482
    private <N> T getInitialEdgeTransformer(WorkUnit<N, ?> unit) {
483
        @SuppressWarnings("nullness") // we have checked non-nullness of initial nodes in the constructor
484
        final @NonNull N initialNode = unit.pmpg.getInitialNode();
2✔
485
        return unit.propTransformers.get(initialNode);
2✔
486
    }
487

488
    private void updateWorkSetStartNode(L label) {
489
        for (WorkUnit<?, ?> unit : workUnits.values()) {
2✔
490
            updateWorkSetStartNode(unit, label);
2✔
491
        }
2✔
492
    }
2✔
493

494
    private <N, E> void updateWorkSetStartNode(WorkUnit<N, E> unit, L labelOfUpdatedProcess) {
495
        final ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg = unit.pmpg;
2✔
496
        for (N node : pmpg) {
2✔
497
            for (E outgoingEdge : pmpg.getOutgoingEdges(node)) {
2✔
498
                if (Objects.equals(pmpg.getEdgeLabel(outgoingEdge), labelOfUpdatedProcess)) {
2✔
499
                    addToWorkSet(unit, node);
2✔
500
                }
501
            }
2✔
502
        }
2✔
503
    }
2✔
504

505
    private <N> void addPredecessorsToWorkSet(WorkUnit<N, ?> unit, N node) {
506
        final Set<N> preds = unit.predecessors.get(node);
2✔
507
        if (preds != null) {
2✔
508
            for (N pred : preds) {
2✔
509
                addToWorkSet(unit, pred);
2✔
510
            }
2✔
511
        }
512
    }
2✔
513

514
    private <N> void addToWorkSet(WorkUnit<N, ?> unit, N node) {
515
        unit.workSet.add(node);
2✔
516
    }
2✔
517

518
    private <N> Set<N> newWorkSet(ProceduralModalProcessGraph<N, L, ?, AP, ?> pmpg) {
519
        // Add all nodes to work set except final node, which is never updated
520
        final Set<N> workset = new HashSet<>(pmpg.getNodes());
2✔
521
        workset.remove(pmpg.getFinalNode());
2✔
522

523
        return workset;
2✔
524
    }
525

526
    private FormulaNode<L, AP> ctlToMuCalc(FormulaNode<L, AP> ctlFormula) {
527
        CTLToMuCalc<L, AP> transformation = new CTLToMuCalc<>();
2✔
528
        return transformation.toMuCalc(ctlFormula);
2✔
529
    }
530

531
    private <N> Mapping<N, List<String>> serializePropertyTransformers(WorkUnit<N, ?> unit) {
532
        final MutableMapping<N, List<String>> result = unit.pmpg.createStaticNodeMapping();
2✔
533
        for (N node : unit.pmpg.getNodes()) {
2✔
534
            result.put(node, serializer.serialize(unit.propTransformers.get(node)));
2✔
535
        }
2✔
536
        return result;
2✔
537
    }
538

539
    private void initialize(FormulaNode<L, AP> ast) {
540
        this.dependencyGraph = new DependencyGraph<>(ast);
2✔
541
        this.currentBlockIndex = dependencyGraph.getBlocks().size() - 1;
2✔
542

543
        initDDManager(this.dependencyGraph);
2✔
544

545
        this.serializer = getSerializer();
2✔
546
        this.mustTransformers = new HashMap<>();
2✔
547
        this.mayTransformers = new HashMap<>();
2✔
548

549
        for (WorkUnit<?, ?> unit : workUnits.values()) {
2✔
550
            initialize(unit);
2✔
551
        }
2✔
552
    }
2✔
553

554
    private <N> void initialize(WorkUnit<N, ?> unit) {
555
        unit.workSet = newWorkSet(unit.pmpg);
2✔
556
        unit.propTransformers = initTransformers(unit.pmpg);
2✔
557
    }
2✔
558

559
    private <N> MutableMapping<N, T> initTransformers(ProceduralModalProcessGraph<N, L, ?, AP, ?> pmpg) {
560
        final MutableMapping<N, T> transformers = pmpg.createStaticNodeMapping();
2✔
561
        final N finalNode = pmpg.getFinalNode();
2✔
562

563
        for (N n : pmpg) {
2✔
564
            if (Objects.equals(n, finalNode)) {
2✔
565
                transformers.put(n, createInitTransformerEndNode(dependencyGraph));
2✔
566
            } else {
567
                transformers.put(n, createInitTransformerNode(dependencyGraph));
2✔
568
            }
569
        }
2✔
570
        return transformers;
2✔
571
    }
572

573
    @SuppressWarnings("argument") // we have checked that mainProcess belongs to workUnits in the constructor
574
    private boolean isSat() {
575
        return isSat(workUnits.get(mainProcess));
2✔
576
    }
577

578
    private <N> boolean isSat(WorkUnit<N, ?> unit) {
579
        @SuppressWarnings("nullness") // we have checked non-nullness of initial nodes in the constructor
580
        final @NonNull N initialNode = unit.pmpg.getInitialNode();
2✔
581
        return isSat(unit, initialNode);
2✔
582
    }
583

584
    private <N> boolean isSat(WorkUnit<N, ?> unit, N initialNode) {
585
        final List<FormulaNode<L, AP>> satisfiedFormulas = getSatisfiedSubformulas(unit, initialNode);
2✔
586
        for (FormulaNode<L, AP> node : satisfiedFormulas) {
2✔
587
            if (node.getVarNumber() == 0) {
2✔
588
                return true;
2✔
589
            }
590
        }
2✔
591
        return false;
2✔
592
    }
593

594
    protected abstract void initDDManager(DependencyGraph<L, AP> dependencyGraph);
595

596
    protected abstract <TP extends ModalEdgeProperty> T createInitTransformerEdge(DependencyGraph<L, AP> dependencyGraph,
597
                                                                                  L edgeLabel,
598
                                                                                  TP edgeProperty);
599

600
    protected abstract T createInitTransformerEndNode(DependencyGraph<L, AP> dependencyGraph);
601

602
    protected abstract T createInitTransformerNode(DependencyGraph<L, AP> dependencyGraph);
603

604
    protected abstract void shutdownDDManager();
605

606
    protected abstract TransformerSerializer<T, L, AP> getSerializer();
607

608
    class WorkUnit<N, E> {
609

610
        final L label;
611
        final ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg;
612
        private final Mapping<N, Set<N>> predecessors;
613
        MutableMapping<N, T> propTransformers;
614
        private Set<N> workSet; // Keeps track of which node's property transformers have to be updated.
615

616
        WorkUnit(L label, ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg, Mapping<N, Set<N>> predecessors) {
2✔
617
            this.label = label;
2✔
618
            this.pmpg = pmpg;
2✔
619
            this.predecessors = predecessors;
2✔
620
        }
2✔
621
    }
622

623
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc