Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f76a951
Initial plan
Copilot Sep 17, 2025
228c86a
Enhance validateBasePetriConfig with AdConfig-based bounds checking
Copilot Sep 17, 2025
de13373
Add upper bound validation to validateBasePetriConfig
Copilot Sep 17, 2025
651ee07
Fix EDITORCONFIG violations: remove trailing whitespace and fix line …
Copilot Sep 17, 2025
7800e8f
Fix string interpolation and make upper bound validation more lenient
Copilot Sep 17, 2025
e64bef3
Fix upper bound validation logic: only check if bounds are too small,…
Copilot Sep 17, 2025
4079eec
Merge branch 'dev' into copilot/fix-388
jvoigtlaender Sep 17, 2025
3d776b5
Use iii for multi-line string interpolations instead of i
Copilot Sep 17, 2025
cb31062
Replace countOfPetriNodesBounds (0, Nothing) with calculated optimal …
Copilot Sep 17, 2025
f29be56
"checking" whether the calculated bounds are really tight
jvoigtlaender Sep 17, 2025
e342669
Tighten all countOfPetriNodesBounds in examples for more precise vali…
Copilot Sep 17, 2025
fd5a887
Update countOfPetriNodesBounds upper limit
jvoigtlaender Sep 18, 2025
77e0824
Update countOfPetriNodesBounds values
jvoigtlaender Sep 18, 2025
bf46f4c
Update countOfPetriNodesBounds upper limits
jvoigtlaender Sep 18, 2025
917a416
Update countOfPetriNodesBounds upper limit to 20
jvoigtlaender Sep 18, 2025
1e931af
Update countOfPetriNodesBounds values in Config.hs
jvoigtlaender Sep 18, 2025
6fc6dec
Merge branch 'dev' into copilot/fix-388
jvoigtlaender Sep 28, 2025
4ff7886
Merge branch 'dev' into copilot/fix-388
jvoigtlaender Oct 17, 2025
0ab4c7e
Merge branch 'dev' into copilot/fix-388
jvoigtlaender Oct 23, 2025
9445501
Merge branch 'dev' into copilot/fix-388
jvoigtlaender Oct 23, 2025
a727aaf
Merge branch 'dev' into copilot/fix-388
jvoigtlaender Dec 12, 2025
ac9cd41
Merge branch 'dev' into copilot/fix-388
jvoigtlaender Jan 27, 2026
c6908d1
record empirical findings
jvoigtlaender Jan 28, 2026
d011ca2
record newer findings
jvoigtlaender Jan 30, 2026
34fa297
record some empirical results
guard-who Feb 2, 2026
ae9b8f7
more recording from experiments
jvoigtlaender Feb 3, 2026
3606cb4
Fix condition for Petri nodes bounds validation
jvoigtlaender Feb 3, 2026
38c802d
Merge branch 'dev' into copilot/fix-388
jvoigtlaender Feb 4, 2026
31e4cf4
more precise statements
jvoigtlaender Feb 4, 2026
ae2e60f
record new information
jvoigtlaender Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ task2023_41 = FindAuxiliaryPetriNodesConfig {
flowFinalNodes = 2,
cycles = 0
},
countOfPetriNodesBounds = (0, Nothing),
countOfPetriNodesBounds = (20, Just 26), -- generates successfully
maxInstances = Just 2000,
hideNodeNames = False,
hideBranchConditions = True,
Expand All @@ -51,7 +51,7 @@ task2023_42 = FindAuxiliaryPetriNodesConfig {
flowFinalNodes = 0,
cycles = 2
},
countOfPetriNodesBounds = (0, Nothing),
countOfPetriNodesBounds = (23, Just 28), -- fails to generate, but works with (0, Nothing) and (22, Just 40)
maxInstances = Just 2000,
hideNodeNames = False,
hideBranchConditions = True,
Expand All @@ -77,7 +77,7 @@ task2024_47 = FindAuxiliaryPetriNodesConfig {
flowFinalNodes = 2,
cycles = 0
},
countOfPetriNodesBounds = (21, Just 27),
countOfPetriNodesBounds = (23, Just 23), -- generates successfully; but (0, Just 22) times out with maxInstances = Nothing
maxInstances = Just 2000,
hideNodeNames = False,
hideBranchConditions = True,
Expand All @@ -103,7 +103,7 @@ task2024_48 = FindAuxiliaryPetriNodesConfig {
flowFinalNodes = 0,
cycles = 2
},
countOfPetriNodesBounds = (31, Just 41),
countOfPetriNodesBounds = (33, Just 33), -- generates successfully; but (0, Just 32) times out with maxInstances = Nothing
maxInstances = Just 2000,
hideNodeNames = False,
hideBranchConditions = True,
Expand Down
10 changes: 5 additions & 5 deletions example/src/Modelling/ActivityDiagram/MatchPetri/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ task2023_39 = MatchPetriConfig {
flowFinalNodes = 2,
cycles = 1
},
countOfPetriNodesBounds = (0, Nothing),
countOfPetriNodesBounds = (25, Just 25), -- generates successfully, but (0, Just 24) times out with maxInstances = Nothing
maxInstances = Just 10000,
hideBranchConditions = True,
petriLayout = [Fdp],
Expand Down Expand Up @@ -54,8 +54,8 @@ task2023_40 = MatchPetriConfig {
flowFinalNodes = 3,
cycles = 3
},
countOfPetriNodesBounds = (0, Nothing),
maxInstances = Just 2000,
countOfPetriNodesBounds = (28, Just 34), -- fails to generate, but works with (37, Just 37); value smaller than 37 leads to NoInstanceAvailable; and (0, Just 36) times out with maxInstances = Nothing
maxInstances = Just 2000, -- fails to generate with Nothing, it diverges
hideBranchConditions = True,
petriLayout = [Fdp],
petriSvgHighlighting = True,
Expand Down Expand Up @@ -99,7 +99,7 @@ task2024_70 = MatchPetriConfig {
flowFinalNodes = 1,
cycles = 0
},
countOfPetriNodesBounds = (0, Nothing),
countOfPetriNodesBounds = (20, Just 30), -- fails to generate, even with (0, Nothing): NoInstanceAvailable
maxInstances = Just 10000,
hideBranchConditions = True,
petriLayout = [Fdp],
Expand Down Expand Up @@ -128,7 +128,7 @@ task2024_71 = MatchPetriConfig {
flowFinalNodes = 3,
cycles = 3
},
countOfPetriNodesBounds = (0, Nothing),
countOfPetriNodesBounds = (28, Just 34), -- fails to generate: NoInstanceAvailable; diverges when (0, Nothing)
maxInstances = Just 2000,
hideBranchConditions = True,
petriLayout = [Fdp],
Expand Down
8 changes: 4 additions & 4 deletions example/src/Modelling/ActivityDiagram/SelectPetri/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ task2023_37 = SelectPetriConfig {
flowFinalNodes = 2,
cycles = 1
},
countOfPetriNodesBounds = (0, Nothing),
countOfPetriNodesBounds = (26, Just 26), -- generates successfully, but (0, Just 25) times out with maxInstances = Nothing
maxInstances = Just 2000,
hideNodeNames = False,
hideBranchConditions = True,
Expand Down Expand Up @@ -57,8 +57,8 @@ task2023_38 = SelectPetriConfig {
flowFinalNodes = 2,
cycles = 1
},
countOfPetriNodesBounds = (0, Nothing),
maxInstances = Just 1,
countOfPetriNodesBounds = (17, Just 20), -- generates successfully
maxInstances = Just 2000,
hideNodeNames = True,
hideBranchConditions = True,
hidePetriNodeLabels = True,
Expand Down Expand Up @@ -99,7 +99,7 @@ task2024_44 = SelectPetriConfig {
flowFinalNodes = 2,
cycles = 1
},
countOfPetriNodesBounds = (0, Nothing),
countOfPetriNodesBounds = (17, Just 19), -- generates successfully, but (0, Just 18) times out with maxInstances = Nothing
maxInstances = Just 2000,
hideNodeNames = True,
hideBranchConditions = True,
Expand Down
2 changes: 1 addition & 1 deletion modelling-tasks.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ library
exposed-modules:
Modelling.ActivityDiagram.ActionSequences
Modelling.ActivityDiagram.Alloy
Modelling.ActivityDiagram.Auxiliary.PetriValidation
Modelling.ActivityDiagram.Auxiliary.Parser
Modelling.ActivityDiagram.Config
Modelling.ActivityDiagram.Datatype
Expand Down Expand Up @@ -85,7 +86,6 @@ library
Modelling.Types
other-modules:
Modelling.ActivityDiagram.Auxiliary.ActionSequences
Modelling.ActivityDiagram.Auxiliary.PetriValidation
Modelling.Auxiliary.Diagrams
Modelling.CdOd.Phrasing
Modelling.CdOd.Phrasing.Common
Expand Down
1 change: 1 addition & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ library:
exposed-modules:
- Modelling.ActivityDiagram.ActionSequences
- Modelling.ActivityDiagram.Alloy
- Modelling.ActivityDiagram.Auxiliary.PetriValidation
- Modelling.ActivityDiagram.Auxiliary.Parser
- Modelling.ActivityDiagram.Config
- Modelling.ActivityDiagram.Datatype
Expand Down
66 changes: 65 additions & 1 deletion src/Modelling/ActivityDiagram/Auxiliary/PetriValidation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,63 @@ module Modelling.ActivityDiagram.Auxiliary.PetriValidation (
) where

import qualified Modelling.ActivityDiagram.Config as Config (
AdConfig (activityFinalNodes, flowFinalNodes, actionLimits, forkJoinPairs, cycles),
AdConfig (activityFinalNodes, flowFinalNodes, actionLimits, objectNodeLimits, forkJoinPairs, decisionMergePairs, cycles),
)

import Control.Applicative (Alternative ((<|>)))
import Data.GraphViz.Commands (GraphvizCommand(..))
import Data.Maybe (isJust, fromJust)
import Data.String.Interpolate (iii)

-- | Calculate minimum number of Petri net nodes based on AdConfig values
calculateMinimumPetriNodes :: Config.AdConfig -> Int
calculateMinimumPetriNodes adConfig =
let
-- At least one initial node (always 1 place)
initialNodes = 1

-- Minimum action nodes (each action becomes a transition, plus places before/after)
minActionNodes = fst (Config.actionLimits adConfig)

-- Minimum object nodes (each becomes a place)
minObjectNodes = fst (Config.objectNodeLimits adConfig)

-- Final nodes (each becomes a place)
finalNodes = Config.activityFinalNodes adConfig + Config.flowFinalNodes adConfig

-- Fork/Join pairs (each pair adds auxiliary nodes: roughly 2 nodes per pair)
forkJoinNodes = Config.forkJoinPairs adConfig * 2

-- Decision/Merge pairs (each pair adds auxiliary nodes: roughly 2 nodes per pair)
decisionMergeNodes = Config.decisionMergePairs adConfig * 2

in initialNodes + minActionNodes + minObjectNodes + finalNodes + forkJoinNodes + decisionMergeNodes

-- | Calculate maximum number of Petri net nodes based on AdConfig values
calculateMaximumPetriNodes :: Config.AdConfig -> Int
calculateMaximumPetriNodes adConfig =
let
-- At least one initial node (always 1 place)
initialNodes = 1

-- Maximum action nodes
maxActionNodes = snd (Config.actionLimits adConfig)

-- Maximum object nodes
maxObjectNodes = snd (Config.objectNodeLimits adConfig)

-- Final nodes (each becomes a place)
finalNodes = Config.activityFinalNodes adConfig + Config.flowFinalNodes adConfig

-- Standard nodes (excluding auxiliary nodes)
standardNodes = initialNodes + maxActionNodes + maxObjectNodes + finalNodes

-- Auxiliary nodes from Fork/Join pairs, Decision/Merge pairs, and Cycles
-- Heuristic: about as many auxiliary nodes as standard nodes
auxiliaryNodes = standardNodes

in standardNodes + auxiliaryNodes

-- | Base validation logic common to multiple Petri-based configurations
validateBasePetriConfig
:: Config.AdConfig
Expand All @@ -35,6 +84,21 @@ validateBasePetriConfig adConfig countOfPetriNodesBounds maxInstances presenceOf
| Just False <- presenceOfSinkTransitionsForFinals,
fst (Config.actionLimits adConfig) + Config.forkJoinPairs adConfig < 1
= Just "The option 'presenceOfSinkTransitionsForFinals = Just False' can only be achieved if the number of Actions, Fork Nodes and Join Nodes together is positive"
| fst countOfPetriNodesBounds > 0 && fst countOfPetriNodesBounds < calculateMinimumPetriNodes adConfig
= Just [iii|
The minimum value of 'countOfPetriNodesBounds' (#{fst countOfPetriNodesBounds}) is too small.
Based on the AdConfig values, the minimum number of Petri net nodes should be at least #{calculateMinimumPetriNodes adConfig}.
This is calculated from: 1 initial node + #{fst (Config.actionLimits adConfig)} minimum action nodes +
#{fst (Config.objectNodeLimits adConfig)} minimum object nodes +
#{Config.activityFinalNodes adConfig + Config.flowFinalNodes adConfig} final nodes +
#{Config.forkJoinPairs adConfig * 2} fork/join auxiliary nodes +
#{Config.decisionMergePairs adConfig * 2} decision/merge auxiliary nodes.
|]
| Just high <- snd countOfPetriNodesBounds, high > calculateMaximumPetriNodes adConfig
= Just [iii|
The maximum value of 'countOfPetriNodesBounds' (#{high}) is too large.
Based on the AdConfig values, the maximum number of Petri net nodes should be at most #{calculateMaximumPetriNodes adConfig}.
|]
| otherwise
= Nothing

Expand Down
25 changes: 24 additions & 1 deletion test/Modelling/ActivityDiagram/SelectPetriSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Modelling.ActivityDiagram.SelectPetri (SelectPetriConfig(..), checkSelect
import Test.Hspec (Spec, describe, it, context, shouldBe, shouldSatisfy)
import Data.Maybe (isJust)
import Modelling.ActivityDiagram.Config (
AdConfig (actionLimits, forkJoinPairs),
AdConfig (actionLimits, objectNodeLimits, forkJoinPairs, activityFinalNodes, flowFinalNodes),
defaultAdConfig,
)

Expand All @@ -25,3 +25,26 @@ spec =
presenceOfSinkTransitionsForFinals = Just False
}
`shouldSatisfy` isJust
context "when countOfPetriNodesBounds minimum is too small for AdConfig" $
it "returns validation error about minimum Petri net nodes" $
checkSelectPetriConfig defaultSelectPetriConfig {
adConfig = defaultAdConfig {
actionLimits = (3, 5),
activityFinalNodes = 1,
flowFinalNodes = 0
},
countOfPetriNodesBounds = (1, Nothing) -- Too small for minimum calculation
}
`shouldSatisfy` isJust
context "when countOfPetriNodesBounds maximum is too small for AdConfig" $
it "returns validation error about maximum Petri net nodes being too small" $
checkSelectPetriConfig defaultSelectPetriConfig {
adConfig = defaultAdConfig {
actionLimits = (1, 5), -- Can achieve up to 5 actions
objectNodeLimits = (0, 3), -- Can achieve up to 3 objects
activityFinalNodes = 1,
flowFinalNodes = 0
},
countOfPetriNodesBounds = (1, Just 8) -- Too small - achievable is 1+5+3+1 = 10 standard + ~10 auxiliary = ~20
}
`shouldSatisfy` isJust
Loading