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

LearnLib / automatalib / 29161451305

11 Jul 2026 03:18PM UTC coverage: 93.029% (+0.02%) from 93.007%
29161451305

push

github

mtf90
generify some nomenclature

6 of 6 new or added lines in 1 file covered. (100.0%)

10 existing lines in 3 files now uncovered.

17562 of 18878 relevant lines covered (93.03%)

1.73 hits per line

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

98.93
/modelchecking/m3c/src/main/java/net/automatalib/modelchecker/m3c/solver/AbstractDDSolver.java
1
/* Copyright (C) 2013-2026 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.Collections;
21
import java.util.HashMap;
22
import java.util.HashSet;
23
import java.util.Iterator;
24
import java.util.List;
25
import java.util.Map;
26
import java.util.Map.Entry;
27
import java.util.Objects;
28
import java.util.Set;
29

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

54
/**
55
 * Base implementation of the model checker which supports different types of property transformers.
56
 *
57
 * @param <T>
58
 *         property transformer type
59
 * @param <L>
60
 *         edge label type
61
 * @param <AP>
62
 *         atomic proposition type
63
 */
64
public abstract class AbstractDDSolver<T extends AbstractPropertyTransformer<T, L, AP>, L, AP> {
2✔
65

66
    // Attributes that are constant for a given CFMPS
67
    private final ContextFreeModalProcessSystem<L, AP> cfmps;
68
    private final @KeyFor("workUnits") L mainProcess;
69

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

80
    AbstractDDSolver(ContextFreeModalProcessSystem<L, AP> cfmps) {
81
        this(validateCFMPS(cfmps), cfmps);
2✔
82
    }
2✔
83

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

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

96
        this.cfmps = cfmps;
2✔
97
        this.mainProcess = mainProcess;
2✔
98
    }
2✔
99

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

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

109
        pmpGs.forEach(AbstractDDSolver::checkPMPG);
2✔
110

111
        return mainProcess;
2✔
112
    }
113

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

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

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

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

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

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

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

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

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

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

166
                if (nodePredecessors == null) {
2✔
167
                    nodePredecessors = new HashSet<>();
2✔
168
                    nodeToPredecessors.put(targetNode, nodePredecessors);
2✔
169
                }
170

171
                nodePredecessors.add(sourceNode);
2✔
172
            }
2✔
173
        }
2✔
174

175
        return nodeToPredecessors;
2✔
176
    }
177

178
    public @Nullable WitnessTree<L, AP> findWitness(FormulaNode<L, AP> formulaNode) {
179
        final FormulaNode<L, AP> ast = ctlToMuCalc(formulaNode).toNNF();
2✔
180

181
        initialize(ast);
2✔
182

183
        try {
184
            this.solveInternal(false, Collections.emptyList());
2✔
185

186
            final boolean sat = isSat();
2✔
187

188
            if (sat) {
2✔
189
                final Map<L, AbstractDDSolver<?, L, AP>.WorkUnit<?, ?>> units = Collections.unmodifiableMap(workUnits);
2✔
190
                return WitnessTreeExtractor.computeWitness(cfmps,
2✔
191
                                                           units,
192
                                                           dependencyGraph,
193
                                                           ast,
194
                                                           getAllAPDeadlockedNode());
2✔
195
            }
196
            return null;
2✔
197
        } finally {
198
            shutdownDDManager();
2✔
199
        }
200
    }
201

202
    public boolean solve(FormulaNode<L, AP> formula) {
203
        final FormulaNode<L, AP> ast = ctlToMuCalc(formula).toNNF();
2✔
204

205
        initialize(ast);
2✔
206

207
        try {
208
            this.solveInternal(false, Collections.emptyList());
2✔
209

210
            return isSat();
2✔
211
        } finally {
212
            shutdownDDManager();
2✔
213
        }
214
    }
215

216
    // this method is public because it is used by external tools such as CINCO
217
    public SolverHistory<T, L, AP> solveAndRecordHistory(FormulaNode<L, AP> formula) {
218
        final List<SolverState<?, T, L, AP>> history = new ArrayList<>();
2✔
219
        final FormulaNode<L, AP> ast = ctlToMuCalc(formula).toNNF();
2✔
220

221
        initialize(ast);
2✔
222

223
        try {
224
            final Map<L, SolverData<?, T, L, AP>> data = new HashMap<>(HashUtil.capacity(this.workUnits.size()));
2✔
225
            for (Entry<L, WorkUnit<?, ?>> e : this.workUnits.entrySet()) {
2✔
226
                data.put(e.getKey(), createProcessData(e.getValue()));
2✔
227
            }
2✔
228

229
            this.solveInternal(true, history);
2✔
230

231
            final Map<L, List<String>> serializedMustTransformers = serializePropertyTransformerMap(mustTransformers);
2✔
232
            final Map<L, List<String>> serializedMayTransformers = serializePropertyTransformerMap(mayTransformers);
2✔
233
            final boolean isSat = isSat();
2✔
234

235
            return new SolverHistory<>(data, serializedMustTransformers, serializedMayTransformers, history, isSat);
2✔
236
        } finally {
237
            shutdownDDManager();
2✔
238
        }
239
    }
240

241
    private <N, E> SolverData<N, T, L, AP> createProcessData(WorkUnit<N, E> unit) {
242
        return new SolverData<>(unit.pmpg, serializePropertyTransformers(unit), computeSatisfiedSubformulas(unit));
2✔
243
    }
244

245
    private Map<L, List<String>> serializePropertyTransformerMap(Map<L, T> transformers) {
246
        final Map<L, List<String>> serializedTransformers = new HashMap<>(HashUtil.capacity(transformers.size()));
2✔
247
        for (Map.Entry<L, T> entry : transformers.entrySet()) {
2✔
248
            serializedTransformers.put(entry.getKey(), serializer.serialize(entry.getValue()));
2✔
249
        }
2✔
250
        return serializedTransformers;
2✔
251
    }
252

253
    private void solveInternal(boolean recordHistory, List<SolverState<?, T, L, AP>> history) {
254
        boolean workSetIsEmpty = false;
2✔
255
        while (!workSetIsEmpty) {
2✔
256
            workSetIsEmpty = true;
2✔
257
            for (Entry<L, WorkUnit<?, ?>> entry : workUnits.entrySet()) {
2✔
258
                workSetIsEmpty &= solveInternal(entry.getValue(), recordHistory, history);
2✔
259
            }
2✔
260
        }
261
    }
2✔
262

263
    private <N> boolean solveInternal(WorkUnit<N, ?> unit,
264
                                      boolean recordHistory,
265
                                      List<SolverState<?, T, L, AP>> history) {
266
        if (!unit.workSet.isEmpty()) {
2✔
267
            final Iterator<N> iter = unit.workSet.iterator();
2✔
268
            final N node = iter.next();
2✔
269
            iter.remove();
2✔
270

271
            final L label = unit.label;
2✔
272
            final List<T> compositions = updateNodeAndGetCompositions(unit, node);
2✔
273

274
            if (recordHistory) {
2✔
275
                final List<List<String>> serializedCompositions = new ArrayList<>(compositions.size());
2✔
276
                for (T composition : compositions) {
2✔
277
                    serializedCompositions.add(serializer.serialize(composition));
2✔
278
                }
2✔
279
                history.add(new SolverState<>(serializer.serialize(unit.propTransformers.get(node)),
2✔
280
                                              serializedCompositions,
281
                                              node,
282
                                              label,
283
                                              copyWorkSet(),
2✔
284
                                              getSatisfiedSubformulas(unit, node)));
2✔
285
            }
286

287
            return false;
2✔
288
        }
289

290
        return true;
2✔
291
    }
292

293
    private <N> List<T> updateNodeAndGetCompositions(WorkUnit<N, ?> unit, N node) {
294
        assert !Objects.equals(node, unit.pmpg.getFinalNode()) : "End node must not be updated!";
2✔
295
        final T nodeTransformer = getTransformer(unit, node);
2✔
296
        final List<T> compositions = createCompositions(unit, node);
2✔
297
        final T updatedTransformer = getUpdatedPropertyTransformer(unit, node, nodeTransformer, compositions);
2✔
298
        updateTransformerAndWorkSet(unit, node, nodeTransformer, updatedTransformer);
2✔
299
        return compositions;
2✔
300
    }
301

302
    private Map<L, Set<?>> copyWorkSet() {
303
        final Map<L, Set<?>> copy = new HashMap<>(HashUtil.capacity(workUnits.size()));
2✔
304
        for (Map.Entry<L, WorkUnit<?, ?>> e : workUnits.entrySet()) {
2✔
305
            copy.put(e.getKey(), copyWorkSet(e.getValue()));
2✔
306
        }
2✔
307
        return copy;
2✔
308
    }
309

310
    private <N> Set<N> copyWorkSet(WorkUnit<N, ?> unit) {
311
        return new HashSet<>(unit.workSet);
2✔
312
    }
313

314
    private <N> Mapping<N, List<FormulaNode<L, AP>>> computeSatisfiedSubformulas(WorkUnit<N, ?> unit) {
315
        final MutableMapping<N, List<FormulaNode<L, AP>>> result = unit.pmpg.createStaticNodeMapping();
2✔
316
        for (N node : unit.pmpg.getNodes()) {
2✔
317
            result.put(node, getSatisfiedSubformulas(unit, node));
2✔
318
        }
2✔
319

320
        return result;
2✔
321
    }
322

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

335
    private BitSet getAllAPDeadlockedNode() {
336
        return getAllAPDeadlockedNode(workUnits.get(mainProcess).pmpg);
2✔
337
    }
338

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

376
    private <N> T getTransformer(WorkUnit<N, ?> unit, N node) {
377
        return unit.propTransformers.get(node);
2✔
378
    }
379

380
    private <N, E> List<T> createCompositions(WorkUnit<N, E> unit, N node) {
381
        final ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg = unit.pmpg;
2✔
382
        final List<T> compositions = new ArrayList<>();
2✔
383
        for (E edge : pmpg.getOutgoingEdges(node)) {
2✔
384
            final N targetNode = pmpg.getTarget(edge);
2✔
385
            final T edgeTransformer = getEdgeTransformer(unit, edge);
2✔
386
            final T succTransformer = getTransformer(unit, targetNode);
2✔
387
            final T composition = edgeTransformer.compose(succTransformer);
2✔
388
            compositions.add(composition);
2✔
389
        }
2✔
390
        return compositions;
2✔
391
    }
392

393
    private <N> T getUpdatedPropertyTransformer(WorkUnit<N, ?> unit, N node, T nodeTransformer, List<T> compositions) {
394
        final EquationalBlock<L, AP> currentBlock = dependencyGraph.getBlock(currentBlockIndex);
2✔
395
        final Set<AP> atomicPropositions = unit.pmpg.getAtomicPropositions(node);
2✔
396
        return nodeTransformer.createUpdate(atomicPropositions, compositions, currentBlock);
2✔
397
    }
398

399
    private <N> void updateTransformerAndWorkSet(WorkUnit<N, ?> unit, N node, T nodeTransformer, T updatedTransformer) {
400
        if (!nodeTransformer.equals(updatedTransformer)) {
2✔
401
            unit.propTransformers.put(node, updatedTransformer);
2✔
402
            updateWorkSet(unit, node);
2✔
403
        }
404
        if (workSetIsEmpty() && currentBlockIndex > 0) {
2✔
405
            currentBlockIndex--;
2✔
406
            resetWorkSet();
2✔
407
        }
408
    }
2✔
409

410
    private <E> T getEdgeTransformer(WorkUnit<?, E> unit, E edge) {
411
        final T edgeTransformer;
412
        final ProceduralModalProcessGraph<?, L, E, AP, ?> pmpg = unit.pmpg;
2✔
413
        final L label = pmpg.getEdgeLabel(edge);
2✔
414
        if (isProcessEdge(pmpg, edge)) {
2✔
415
            @SuppressWarnings("nullness") // we constructed work units based on labels
416
            final @NonNull WorkUnit<?, ?> edgeUnit = workUnits.get(label);
2✔
417
            edgeTransformer = getInitialEdgeTransformer(edgeUnit);
2✔
418
        } else {
2✔
419
            if (isMustEdge(pmpg, edge)) {
2✔
420
                if (mustTransformers.containsKey(label)) {
2✔
421
                    edgeTransformer = mustTransformers.get(label);
2✔
422
                } else {
423
                    edgeTransformer = createInitTransformerEdge(dependencyGraph, label, pmpg.getEdgeProperty(edge));
2✔
424
                    mustTransformers.put(label, edgeTransformer);
2✔
425
                }
426
            } else {
427
                if (mayTransformers.containsKey(label)) {
2✔
428
                    edgeTransformer = mayTransformers.get(label);
2✔
429
                } else {
430
                    edgeTransformer = createInitTransformerEdge(dependencyGraph, label, pmpg.getEdgeProperty(edge));
2✔
431
                    mayTransformers.put(label, edgeTransformer);
2✔
432
                }
433
            }
434
        }
435
        return edgeTransformer;
2✔
436
    }
437

438
    private <E> boolean isMustEdge(ProceduralModalProcessGraph<?, L, E, AP, ?> pmpg, E edge) {
439
        return pmpg.getEdgeProperty(edge).isMust();
2✔
440
    }
441

442
    private <N> void updateWorkSet(WorkUnit<N, ?> unit, N node) {
443
        if (Objects.equals(unit.pmpg.getInitialNode(), node)) {
2✔
444
            updateWorkSetStartNode(unit.label);
2✔
445
        }
446
        addPredecessorsToWorkSet(unit, node);
2✔
447
    }
2✔
448

449
    private boolean workSetIsEmpty() {
450
        for (WorkUnit<?, ?> unit : workUnits.values()) {
2✔
451
            if (!unit.workSet.isEmpty()) {
2✔
452
                return false;
2✔
453
            }
454
        }
2✔
455
        return true;
2✔
456
    }
457

458
    private void resetWorkSet() {
459
        for (WorkUnit<?, ?> value : this.workUnits.values()) {
2✔
460
            resetWorkSet(value);
2✔
461
        }
2✔
462
    }
2✔
463

464
    private <N> void resetWorkSet(WorkUnit<N, ?> unit) {
465
        unit.workSet = newWorkSet(unit.pmpg);
2✔
466
    }
2✔
467

468
    private <E> boolean isProcessEdge(ProceduralModalProcessGraph<?, L, E, AP, ?> pmpg, E edge) {
469
        return pmpg.getEdgeProperty(edge).isProcess();
2✔
470
    }
471

472
    private <N> T getInitialEdgeTransformer(WorkUnit<N, ?> unit) {
473
        @SuppressWarnings("nullness") // we have checked non-nullness of initial nodes in the constructor
474
        final @NonNull N initialNode = unit.pmpg.getInitialNode();
2✔
475
        return unit.propTransformers.get(initialNode);
2✔
476
    }
477

478
    private void updateWorkSetStartNode(L label) {
479
        for (WorkUnit<?, ?> unit : workUnits.values()) {
2✔
480
            updateWorkSetStartNode(unit, label);
2✔
481
        }
2✔
482
    }
2✔
483

484
    private <N, E> void updateWorkSetStartNode(WorkUnit<N, E> unit, L labelOfUpdatedProcess) {
485
        final ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg = unit.pmpg;
2✔
486
        for (N node : pmpg) {
2✔
487
            for (E outgoingEdge : pmpg.getOutgoingEdges(node)) {
2✔
488
                if (Objects.equals(pmpg.getEdgeLabel(outgoingEdge), labelOfUpdatedProcess)) {
2✔
489
                    addToWorkSet(unit, node);
2✔
490
                }
491
            }
2✔
492
        }
2✔
493
    }
2✔
494

495
    private <N> void addPredecessorsToWorkSet(WorkUnit<N, ?> unit, N node) {
496
        final Set<N> preds = unit.predecessors.get(node);
2✔
497
        if (preds != null) {
2✔
498
            for (N pred : preds) {
2✔
499
                addToWorkSet(unit, pred);
2✔
500
            }
2✔
501
        }
502
    }
2✔
503

504
    private <N> void addToWorkSet(WorkUnit<N, ?> unit, N node) {
505
        unit.workSet.add(node);
2✔
506
    }
2✔
507

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

513
        return workset;
2✔
514
    }
515

516
    private FormulaNode<L, AP> ctlToMuCalc(FormulaNode<L, AP> ctlFormula) {
517
        CTLToMuCalc<L, AP> transformation = new CTLToMuCalc<>();
2✔
518
        return transformation.toMuCalc(ctlFormula);
2✔
519
    }
520

521
    private <N> Mapping<N, List<String>> serializePropertyTransformers(WorkUnit<N, ?> unit) {
522
        final MutableMapping<N, List<String>> result = unit.pmpg.createStaticNodeMapping();
2✔
523
        for (N node : unit.pmpg.getNodes()) {
2✔
524
            result.put(node, serializer.serialize(unit.propTransformers.get(node)));
2✔
525
        }
2✔
526
        return result;
2✔
527
    }
528

529
    private void initialize(FormulaNode<L, AP> ast) {
530
        this.dependencyGraph = new DependencyGraph<>(ast);
2✔
531
        this.currentBlockIndex = dependencyGraph.getBlocks().size() - 1;
2✔
532

533
        initDDManager(this.dependencyGraph);
2✔
534

535
        this.serializer = getSerializer();
2✔
536
        this.mustTransformers = new HashMap<>();
2✔
537
        this.mayTransformers = new HashMap<>();
2✔
538

539
        for (WorkUnit<?, ?> unit : workUnits.values()) {
2✔
540
            initialize(unit);
2✔
541
        }
2✔
542
    }
2✔
543

544
    private <N> void initialize(WorkUnit<N, ?> unit) {
545
        unit.workSet = newWorkSet(unit.pmpg);
2✔
546
        unit.propTransformers = initTransformers(unit.pmpg);
2✔
547
    }
2✔
548

549
    private <N> MutableMapping<N, T> initTransformers(ProceduralModalProcessGraph<N, L, ?, AP, ?> pmpg) {
550
        final MutableMapping<N, T> transformers = pmpg.createStaticNodeMapping();
2✔
551
        final N finalNode = pmpg.getFinalNode();
2✔
552

553
        for (N n : pmpg) {
2✔
554
            if (Objects.equals(n, finalNode)) {
2✔
555
                transformers.put(n, createInitTransformerEndNode(dependencyGraph));
2✔
556
            } else {
557
                transformers.put(n, createInitTransformerNode(dependencyGraph));
2✔
558
            }
559
        }
2✔
560
        return transformers;
2✔
561
    }
562

563
    @SuppressWarnings("argument") // we have checked that mainProcess belongs to workUnits in the constructor
564
    private boolean isSat() {
565
        return isSat(workUnits.get(mainProcess));
2✔
566
    }
567

568
    private <N> boolean isSat(WorkUnit<N, ?> unit) {
569
        @SuppressWarnings("nullness") // we have checked non-nullness of initial nodes in the constructor
570
        final @NonNull N initialNode = unit.pmpg.getInitialNode();
2✔
571
        return isSat(unit, initialNode);
2✔
572
    }
573

574
    private <N> boolean isSat(WorkUnit<N, ?> unit, N initialNode) {
575
        final List<FormulaNode<L, AP>> satisfiedFormulas = getSatisfiedSubformulas(unit, initialNode);
2✔
576
        for (FormulaNode<L, AP> node : satisfiedFormulas) {
2✔
577
            if (node.getVarNumber() == 0) {
2✔
578
                return true;
2✔
579
            }
580
        }
2✔
581
        return false;
2✔
582
    }
583

584
    protected abstract void initDDManager(DependencyGraph<L, AP> dependencyGraph);
585

586
    protected abstract <TP extends ModalEdgeProperty> T createInitTransformerEdge(DependencyGraph<L, AP> dependencyGraph,
587
                                                                                  L edgeLabel,
588
                                                                                  TP edgeProperty);
589

590
    protected abstract T createInitTransformerEndNode(DependencyGraph<L, AP> dependencyGraph);
591

592
    protected abstract T createInitTransformerNode(DependencyGraph<L, AP> dependencyGraph);
593

594
    protected abstract void shutdownDDManager();
595

596
    protected abstract TransformerSerializer<T, L, AP> getSerializer();
597

598
    class WorkUnit<N, E> {
599

600
        final L label;
601
        final ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg;
602
        private final Mapping<N, Set<N>> predecessors;
603
        MutableMapping<N, T> propTransformers;
604
        private Set<N> workSet; // Keeps track of which node's property transformers have to be updated.
605

606
        WorkUnit(L label, ProceduralModalProcessGraph<N, L, E, AP, ?> pmpg, Mapping<N, Set<N>> predecessors) {
2✔
607
            this.label = label;
2✔
608
            this.pmpg = pmpg;
2✔
609
            this.predecessors = predecessors;
2✔
610
        }
2✔
611
    }
612

613
}
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

© 2026 Coveralls, Inc