diff --git a/Documentation/Generators/$SetReplaceGenerators.md b/Documentation/Generators/$SetReplaceGenerators.md
new file mode 100644
index 00000000..9216c191
--- /dev/null
+++ b/Documentation/Generators/$SetReplaceGenerators.md
@@ -0,0 +1,9 @@
+# $SetReplaceGenerators
+
+**`$SetReplaceGenerators`** gives the list of all generators that can be used to evaluate
+[computational systems](/Documentation/Systems/README.md):
+
+```wl
+In[] := $SetReplaceGenerators
+Out[] = {GenerateMultihistory, GenerateSingleHistory}
+```
diff --git a/Documentation/Generators/$SetReplaceSystems.md b/Documentation/Generators/$SetReplaceSystems.md
index 8a924d14..5a3b1423 100644
--- a/Documentation/Generators/$SetReplaceSystems.md
+++ b/Documentation/Generators/$SetReplaceSystems.md
@@ -1,7 +1,7 @@
# $SetReplaceSystems
**`$SetReplaceSystems`** gives the list of all [computational systems](/Documentation/Systems/README.md) that can be
-used with [GenerateMultihistory](/Documentation/Generators/GenerateMultihistory.md) and related functions:
+used with [GenerateMultihistory](/Documentation/Generators/GenerateMultihistory.md) and other [generators](README.md):
```wl
In[] := $SetReplaceSystems
diff --git a/Documentation/Generators/EventOrderingFunctions.md b/Documentation/Generators/EventOrderingFunctions.md
deleted file mode 100644
index 68d474b4..00000000
--- a/Documentation/Generators/EventOrderingFunctions.md
+++ /dev/null
@@ -1,142 +0,0 @@
-# Event Ordering Functions
-
-Multiple matches to the same state of a computational system are sometimes possible. For example, the system below can
-match any pair of numbers, many of which overlap:
-
-```wl
-In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}],
- "MaxGeneration" -> 1,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @
- {1, 2, 3, 4}
-```
-
-
-
-Event ordering functions control the order in which these matches will be instantiated.
-
-The importance of that order depends on the system, the rules, and the evaluation parameters. For example, in the
-example above, full multihistory is generated up to generation 1. In this case, the same multihistory is generated
-regardless of the event ordering, so the event ordering is not important. For this reason, there is no argument for the
-event ordering in `GenerateFullMultihistory` (not yet implemented).
-
-However, if we evaluate a single history instead, we can get different histories for different orders (different orders
-are made here by rearranging the order of the initial state, as
-[`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md) only supports a single ordering
-function at the moment):
-
-```wl
-In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & /@
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] /@
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}],
- "MaxDestroyerEvents" -> 1,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] /@
- {{1, 2, 3, 4}, {1, 3, 2, 4}}
-```
-
-
-
-This system, however, is [confluent](https://en.wikipedia.org/wiki/Confluence_(abstract_rewriting)). So, the final state
-will always be the same even if the histories are different, assuming the system is evaluated to completion.
-
-However, this is not the case for all systems. For example, see what happens if we change `+` to `-`:
-
-```wl
-In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & /@
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] /@
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a - b}],
- "MaxDestroyerEvents" -> 1,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] /@
- {{1, 2, 3, 5}, {1, 5, 2, 3}}
-```
-
-
-
-For this reason, generators such as `GenerateSingleHistory` (not yet implemented) require specification of the ordering
-function as one of the arguments, as, without it, the evaluation will be ambiguous.
-
-**`EventOrderingFunctions`** allows one to obtain the list of event ordering functions that can be used with a
-[computational system](/Documentation/Systems/README.md):
-
-```wl
-In[] := EventOrderingFunctions[MultisetSubstitutionSystem]
-Out[] = {"InputCount", "SortedInputTokenIndices", "InputTokenIndices", "RuleIndex", "InstantiationIndex"}
-```
-
-The individual values returned correspond to partial sorting criteria supported by the system. They are used in the
-order they are passed to [generators](README.md). The first criterion is applied first. If ambiguities are remaining,
-the second criterion is used, etc.
-
-## InputCount
-
-As few tokens as possible will be matched. This is particularly useful for systems such as
-[`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md) where a single rule can match
-multiple inputs with different token counts.
-
-For example, the [multiset](/Documentation/Systems/MultisetSubstitutionSystem.md) pattern `{a___}` will match `{6, 7}`
-before `{1, 2, 3}` with this ordering function.
-
-## SortedInputTokenIndices
-
-As events are instantiated, each token in a [`Multihistory`](/Documentation/Types/Multihistory/README.md) has an index
-corresponding to when that token was first created. (If tokens are created simultaneously, indices correspond to the
-order in the rule output or in the initial state.)
-
-`"SortedInputTokenIndices"` ordering function sorts the tokens in a particular match by index and then selects the
-lexicographically smallest result. This corresponds to effectively
-`{"MinInputTokenIndex", "SecondMinInputTokenIndex", ...}`. If one of the sorted index lists is a prefix of another, they
-are considered equal by this ordering function. [`"InputCount"`](#inputcount) will need to be used to resolve the
-ambiguity.
-
-In other words, this ordering function attempts to match the oldest token possible. And if multiple matches remain, it
-attempts to use the oldest of the remaining tokens, etc.
-
-For example, the [multiset](/Documentation/Systems/MultisetSubstitutionSystem.md) pattern `{a_, b_, c_}` will match
-tokens with indices `{7, 1, 6}` before `{3, 2}` (since `1 < 2`), and `{4, 6, 2}` before `{5, 2, 6}` (since `2 == 2` and
-`4 < 5`). However, `{3, 2, 1}` and `{3, 4, 1, 2}` will be considered equal by this ordering function as `{1, 2, 3}` is a
-prefix of `{1, 2, 3, 4}`.
-
-## InputTokenIndices
-
-This function is similar to [`"SortedInputTokenIndices"`](#sortedinputtokenindices), except tokens are not sorted before
-being lexicographically compared. This corresponds to greedily matching the first rule input to a token with the
-smallest index, then following with the second input, etc.
-
-For example, the [multiset](/Documentation/Systems/MultisetSubstitutionSystem.md) pattern `{a_, b_, c_}` will match
-tokens with indices `{3, 2}` before `{7, 1, 6}` (since `3 < 7`), and `{3, 1, 2}` before `{3, 4}` (since `3 == 3` and
-`1 < 4`). Similar to [`"SortedInputTokenIndices"`](#sortedinputtokenindices), `{1, 4}` and `{1, 4, 2}` will be consider
-equal by this function, and will be passed to the next one.
-
-## InputIndex
-
-This is equivalent to [`"SortedInputTokenIndices"`](#sortedinputtokenindices) and
-[`"InputTokenIndices"`](#inputtokenindices) in systems that only take a single token as an input, such as
-[`AtomicStateSystem`](/Documentation/Systems/AtomicStateSystem.md).
-
-## RuleIndex
-
-This function attempts to use rules in the same order they are specified in the argument for the computational system.
-Only if there are no matches for the first rule, the second rule will be attempted, etc. It does not affect single-rule
-systems.
-
-## InstantiationIndex
-
-In some cases, even the same sequence of input tokens can lead to different outputs. For example, the
-[`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md) pattern `{a__, b__}` (note
-[`BlankSequence`](https://reference.wolfram.com/language/ref/BlankSequence.html)'s) can match tokens `{1, 2, 3}` as
-either `{a} -> {1}`, `{b} -> {2, 3}` or `{a} -> {1, 2}`, `{b} -> {3}`, yielding different outputs in a rule such as
-`{a__, b__} :> {{a}, {b}}`.
-
-To resolve this ambiguity, `"InstantiationIndex"` ordering function can be used. The specific order of instantiations
-depends on the system. In [`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md), the
-order is the same as in [`ReplaceList`](https://reference.wolfram.com/language/ref/ReplaceList.html).
-
-If this ordering is used first, the system only does a single instantiation for each sequence of tokens unless there are
-no other matches available.
diff --git a/Documentation/Generators/EventSelectionParameters.md b/Documentation/Generators/EventSelectionParameters.md
deleted file mode 100644
index e159322c..00000000
--- a/Documentation/Generators/EventSelectionParameters.md
+++ /dev/null
@@ -1,168 +0,0 @@
-# Event Selection Parameters
-
-Event selection parameters control which matches will be instantiated during the evaluation. Unlike the
-[stopping conditions](StoppingConditionParameters.md), these constraints are local. In other words, the evaluation does
-not terminate if any of these constraints are encountered. Only particular matches are skipped instead.
-
-**`EventSelectionParameters`** allows one to obtain the list of event selection parameters that can be used with a
-[computational system](/Documentation/Systems/README.md):
-
-```wl
-In[] := EventSelectionParameters[MultisetSubstitutionSystem]
-Out[] = {"MaxGeneration", "MaxDestroyerEvents", "MinEventInputs", "MaxEventInputs"}
-```
-
-The values returned by this function can be used as keys for the corresponding arguments of [generators](README.md).
-
-## MaxGeneration
-
-Roughly speaking, **generation** corresponds to how many "steps" it took to get to a particular token or event starting
-from the initial state. More precisely, the generation of the tokens in the initial state is defined to be zero. The
-generation of an event is defined as the maximum of the generations of its inputs plus one. The generation of a token is
-the same as the generation of its creator event.
-
-A neat feature of the `TokenEventGraph` property is that it arranges tokens and events in layers corresponding to their
-generations. In the following example, the tokens and events are labeled with their generation numbers:
-
-```wl
-In[] := #["ExpressionsEventsGraph",
- VertexLabels -> Placed["Name", After, Replace[{{"Expression", n_} :> #["ExpressionGenerations"][[n]],
- {"Event", n_} :> #["EventGenerations"][[n]]}]]] & @
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a__} /; Total[{a}] == 5 :> {Total[{a}] - 1, Total[{a}] + 1}],
- {},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- "MaxEvents" -> 3] @ {1, 2, 3}
-```
-
-
-
-Restricting the number of generations to one will prevent the last two events from occurring. Note, however, that
-another event is created instead:
-
-```wl
-In[] := #["ExpressionsEventsGraph"] & @ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a__} /; Total[{a}] == 5 :> {Total[{a}] - 1, Total[{a}] + 1}],
- "MaxGeneration" -> 1,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- "MaxEvents" -> 3] @ {1, 2, 3}
-```
-
-
-
-Since `"MaxGeneration"` is a selection parameter rather than a [stopping condition](StoppingConditionParameters.md), it
-will continue evaluation even after encountering matches exceeding the generations constraint, which might also result
-in a different [event ordering](EventOrderingFunctions.md) than if using, e.g.,
-[`"MaxEvents"`](StoppingConditionParameters.md#maxevents). For this reason, `"MaxGenerations"` (like other selection
-parameters) does not have a corresponding termination reason.
-
-```wl
-In[] := #[[2, "TerminationReason"]] & @
- GenerateMultihistory[MultisetSubstitutionSystem[{a__} /; Total[{a}] == 5 :> {Total[{a}] - 1, Total[{a}] + 1}],
- "MaxGeneration" -> 1,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- "MaxEvents" -> 3] @ {1, 2, 3}
-Out[] = "Complete"
-```
-
-## MaxDestroyerEvents
-
-`"MaxDestroyerEvents"` controls the number of (inconsistent) events that are allowed to take the same token as an input.
-If `"MaxDestroyerEvents"` is set to one, a single-history system will be generated similar to `GenerateSingleHistory`:
-
-```wl
-In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- "MaxDestroyerEvents" -> 1,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @ {1, 2, 3}
-```
-
-
-
-If unset (i.e., set to [`Infinity`](https://reference.wolfram.com/language/ref/Infinity.html)), it will generate a full
-multihistory (similar to `GenerateFullMultihistory`) subject to other selection and stopping parameters:
-
-```wl
-In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- {"MaxDestroyerEvents" -> Infinity, "MaxGeneration" -> 1},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @ {1, 2, 3}
-```
-
-
-
-If set to a finite number, it will generate a partial multihistory:
-
-```wl
-In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- "MaxDestroyerEvents" -> 5,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @ {1, 2, 3}
-```
-
-
-
-Note that in this case, like in the case of a single history, changing [ordering functions](EventOrderingFunctions.md)
-will change the result.
-
-## MinEventInputs and MaxEventInputs
-
-These parameters control the min and max numbers of input tokens allowed per event. There are two use cases for these
-parameters. The first is controlling the number of inputs in systems where rules with variable numbers of inputs are
-possible, such as [`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md). Compare, for
-example, `"MinEventInputs" -> 0` (default):
-
-```wl
-In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a___} :> {Total[{a}]}],
- {"MinEventInputs" -> 0},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {"MaxEvents" -> 10}] @ {1, 2, 3}
-```
-
-
-
-and `"MinEventInputs" -> 2`:
-
-```wl
-In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a___} :> {Total[{a}]}],
- {"MinEventInputs" -> 2},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {"MaxEvents" -> 10}] @ {1, 2, 3}
-```
-
-
-
-The other use case is optimization. By default, systems like
-[`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md) consider all subsets of tokens to
-find matches, which can be slow, especially to finalize the complete evaluation, as it requires going through all
-subsets to find out that no more matches are possible. However, if the range of match sizes is known ahead of time, it
-can be used to make the evaluation faster. Compare:
-
-```wl
-In[] := First @ AbsoluteTiming @
- GenerateMultihistory[MultisetSubstitutionSystem[{a___} /; Length[{a}] == 4 :> {Total[{a}]}],
- #,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {"MaxEvents" -> 20}] @ {1, 2, 3, 4} & /@
- {{}, {"MinEventInputs" -> 4, "MaxEventInputs" -> 4}}
-Out[] = {0.682851, 0.011029}
-```
diff --git a/Documentation/Generators/GenerateMultihistory.md b/Documentation/Generators/GenerateMultihistory.md
index dcf1753a..be7e19d7 100644
--- a/Documentation/Generators/GenerateMultihistory.md
+++ b/Documentation/Generators/GenerateMultihistory.md
@@ -1,24 +1,15 @@
# GenerateMultihistory
-**`GenerateMultihistory`** is the most verbose and configurable generator. It can create both single histories, full
-multihistories, as well as partial ones. It, however, requires one to specify all groups of parameters
-([system](/Documentation/Systems/README.md), [event selection](EventSelectionParameters.md), deduplication,
-[event ordering](EventOrderingFunctions.md) and [stopping conditions](StoppingConditionParameters.md)), nothing is
-implied automatically:
-
-```wl
-GenerateMultihistory[
- system, eventSelectionSpec, tokenDeduplicationSpec, eventOrderingSpec, stoppingConditionSpec] @ init
-```
+**`GenerateMultihistory`** is the most configurable multihistory generator. With no parameters specified, it attempts to
+generate all possible histories starting from the initial state. However, it can be configured to generate partial
+multihistories and even single histories, although [`GenerateSingleHistory`](GenerateSingleHistory.md) is more
+convenient for that.
For example, for a [`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md):
```wl
-In[] := multihistory = GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- {"MaxDestroyerEvents" -> 3},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {"MaxEvents" -> 10}] @ {1, 2, 3, 4}
+In[] := multihistory = GenerateMultihistory[
+ MultisetSubstitutionSystem[{a_, b_} :> {a + b}], MaxDestroyerEvents -> 3, MaxEvents -> 10] @ {1, 2, 3, 4}
```
@@ -29,11 +20,3 @@ In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] &
```
-
-Everything that can be generated with more specialized `GenerateSingleHistory` and `GenerateFullMultihistory` can be
-reproduced with `GenerateMultihistory` as well. This can be done by setting `"MaxDestroyerEvents" -> 1` to emulate
-`GenerateSingleHistory` and setting `"MaxDestroyerEvents" -> Infinity` and leaving the
-[stopping condition](StoppingConditionParameters.md) empty to emulate `GenerateFullMultihistory`.
-
-However, by setting `"MaxDestroyerEvents"` to finite values larger than 1, one can generate multihistories not possible
-with other generators.
diff --git a/Documentation/Generators/GenerateSingleHistory.md b/Documentation/Generators/GenerateSingleHistory.md
new file mode 100644
index 00000000..2cbb4ba8
--- /dev/null
+++ b/Documentation/Generators/GenerateSingleHistory.md
@@ -0,0 +1,27 @@
+# GenerateSingleHistory
+
+**`GenerateSingleHistory`** generates single histories, which it achieves by setting
+[`MaxDestroyerEvents`](MaxDestroyerEvents.md) to one. Note that for nondeterministic systems, the history generated may
+depend on the event order.
+
+For example, for a [`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md):
+
+```wl
+In[] := multihistory = GenerateSingleHistory[
+ MultisetSubstitutionSystem[{a_, b_} :> {a + b, a - b, a * b}], MaxEvents -> 10] @ {1, 2}
+```
+
+
+
+```wl
+In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
+ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @ multihistory
+```
+
+
+
+Note that there is a distinction between single-history and single-path systems. Single-path systems are defined as ones
+where there is only one event possible from every state. A Turing machine would be an example of a single-path system.
+On the other hand, single-history systems allow multiple events from a single state, but all these events must be
+consistent, i.e., take non-overlapping inputs. Neither single-path nor single-history systems have branchlike-separated
+events.
diff --git a/Documentation/Generators/MaxDestroyerEvents.md b/Documentation/Generators/MaxDestroyerEvents.md
new file mode 100644
index 00000000..22f8672f
--- /dev/null
+++ b/Documentation/Generators/MaxDestroyerEvents.md
@@ -0,0 +1,37 @@
+# MaxDestroyerEvents
+
+`MaxDestroyerEvents` is an event-selection parameter that controls the number of inconsistent events allowed to take the
+same token as an input. [`GenerateSingleHistory`](GenerateSingleHistory.md) limits the evaluation to a single history by
+setting `MaxDestroyerEvents` to one.
+
+```wl
+In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
+ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
+ GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}], MaxDestroyerEvents -> 1] @ {1, 2, 3}
+```
+
+
+
+If unset (which defaults to [`Infinity`](https://reference.wolfram.com/language/ref/Infinity.html)), it will generate a
+full multihistory object subject to other selection and stopping parameters:
+
+```wl
+In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
+ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
+ GenerateMultihistory[
+ MultisetSubstitutionSystem[{a_, b_} :> {a + b}], MaxDestroyerEvents -> Infinity, MaxGeneration -> 1] @ {1, 2, 3}
+```
+
+
+
+If set to a finite number, it will generate a partial multihistory:
+
+```wl
+In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
+ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
+ GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}], MaxDestroyerEvents -> 5] @ {1, 2, 3}
+```
+
+
+
+Note that results generally depend on the event order in this case, similar to single histories.
diff --git a/Documentation/Generators/MaxEventInputs.md b/Documentation/Generators/MaxEventInputs.md
new file mode 100644
index 00000000..faebc8f0
--- /dev/null
+++ b/Documentation/Generators/MaxEventInputs.md
@@ -0,0 +1,18 @@
+# MaxEventInputs
+
+`MaxEventInputs` and [`MinEventInputs`](MinEventInputs.md) are event-selection parameters that control the maximum and
+minimum numbers of input tokens allowed per event.
+
+In addition to [being useful](MinEventInputs.md) for rules with variable numbers of inputs, `MaxEventInputs` is
+sometimes useful for optimization. By default, systems like
+[`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md) consider all subsets of tokens to
+find matches, which can be slow. However, if the range of match sizes is known ahead of time, one can set it explicitly
+to reduce the number of matches enumerated. Compare:
+
+```wl
+In[] := First @ AbsoluteTiming @
+ GenerateMultihistory[
+ MultisetSubstitutionSystem[{a___} /; Length[{a}] == 4 :> {Total[{a}]}], MaxEvents -> 20, #] @ {1, 2, 3, 4} & /@
+ {{}, {MinEventInputs -> 4, MaxEventInputs -> 4}}
+Out[] = {0.793215, 0.014419}
+```
diff --git a/Documentation/Generators/MaxEvents.md b/Documentation/Generators/MaxEvents.md
new file mode 100644
index 00000000..460b76ca
--- /dev/null
+++ b/Documentation/Generators/MaxEvents.md
@@ -0,0 +1,14 @@
+# MaxEvents
+
+`MaxEvents` is the most basic stopping condition. It stops the evaluation once the given number of events is reached
+(regardless of causal dependencies between these events):
+
+```wl
+In[] := #["ExpressionsEventsGraph"] & @
+ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
+ GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}], MaxEvents -> 9] @ {1, 2, 3, 4}
+```
+
+
+
+Compare to [`MaxGeneration`](MaxGeneration.md), which controls the depth of the evaluation instead.
diff --git a/Documentation/Generators/MaxGeneration.md b/Documentation/Generators/MaxGeneration.md
new file mode 100644
index 00000000..56e3b76e
--- /dev/null
+++ b/Documentation/Generators/MaxGeneration.md
@@ -0,0 +1,47 @@
+# MaxGeneration
+
+`MaxGeneration` is an event-selection parameter specifying the maximum generation of created tokens.
+
+Roughly speaking, **generation** corresponds to how many "steps" it took to get to a particular token or event starting
+from the initial state. More precisely, the generation of the tokens in the initial state is defined to be zero. The
+generation of an event is defined as the maximum of the generations of its inputs plus one. The generation of a token is
+the same as the generation of its creator event.
+
+A neat feature of the `TokenEventGraph` property is that it arranges tokens and events in layers corresponding to their
+generations. In the following example, the tokens and events are labeled with their generation numbers:
+
+```wl
+In[] := #["ExpressionsEventsGraph",
+ VertexLabels -> Placed["Name", After, Replace[{{"Expression", n_} :> #["ExpressionGenerations"][[n]],
+ {"Event", n_} :> #["EventGenerations"][[n]]}]]] & @
+ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
+ GenerateMultihistory[MultisetSubstitutionSystem[{a__} /; Total[{a}] == 5 :> {Total[{a}] - 1, Total[{a}] + 1}],
+ MaxEvents -> 3] @ {1, 2, 3}
+```
+
+
+
+Restricting the number of generations to one will prevent the last two events from occurring. Note, however, that
+another event is created instead:
+
+```wl
+In[] := #["ExpressionsEventsGraph"] & @ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
+ GenerateMultihistory[MultisetSubstitutionSystem[{a__} /; Total[{a}] == 5 :> {Total[{a}] - 1, Total[{a}] + 1}],
+ MaxGeneration -> 1, MaxEvents -> 3] @ {1, 2, 3}
+```
+
+
+
+`MaxGeneration` is an event selection parameter, not a stopping condition. That is, the evolution of the system won't
+stop if a match (tentative event) with a generation greater than the constraint is encountered. Instead, it will ignore
+those matches and instantiate only those that satisfy the generation constraint. This selection of events according to
+their generation will (in most cases) affect the event order, as opposed to using stopping conditions, e.g.,
+`MaxEvents`. For this reason, `MaxGenerations` (like other selection parameters) does not have a corresponding
+termination reason.
+
+```wl
+In[] := #[[2, "TerminationReason"]] & @
+ GenerateMultihistory[MultisetSubstitutionSystem[{a__} /; Total[{a}] == 5 :> {Total[{a}] - 1, Total[{a}] + 1}],
+ MaxGeneration -> 1] @ {1, 2, 3}
+Out[] = "Complete"
+```
diff --git a/Documentation/Generators/MinEventInputs.md b/Documentation/Generators/MinEventInputs.md
new file mode 100644
index 00000000..b8a52d24
--- /dev/null
+++ b/Documentation/Generators/MinEventInputs.md
@@ -0,0 +1,26 @@
+# MinEventInputs
+
+`MinEventInputs` and [`MaxEventInputs`](MaxEventInputs.md) are event-selection parameters that control the min and max
+numbers of input tokens allowed per event. These parameters are useful in systems where rules with variable numbers of
+inputs are possible, such as [`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md).
+Compare, for example, `MinEventInputs -> 0` (default):
+
+```wl
+In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
+ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
+ GenerateMultihistory[
+ MultisetSubstitutionSystem[{a___} :> {Total[{a}]}], MinEventInputs -> 0, MaxEvents -> 10] @ {1, 2, 3}
+```
+
+
+
+and `MinEventInputs -> 2`:
+
+```wl
+In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
+ SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
+ GenerateMultihistory[
+ MultisetSubstitutionSystem[{a___} :> {Total[{a}]}], MinEventInputs -> 2, MaxEvents -> 10] @ {1, 2, 3}
+```
+
+
diff --git a/Documentation/Generators/README.md b/Documentation/Generators/README.md
index e64c887d..4a465932 100644
--- a/Documentation/Generators/README.md
+++ b/Documentation/Generators/README.md
@@ -5,59 +5,69 @@ rules for a [computational system](/Documentation/Systems/README.md) and additio
the evaluation.
In *SetReplace*, we split the states of [computational systems](/Documentation/Systems/README.md) into components which
-we call tokens. Rewrites (which we call events) replace some of these tokens with others. Crucially, *SetReplace* can
-evaluate multiple branches of nondeterministic systems simultaneously. That is done by applying different events to the
-same tokens and keeping events and tokens instead of states in
-[`Multihistory`](/Documentation/Types/Multihistory/README.md) objects. We can reconstruct the states from that
-information afterwards.
+we call tokens. Rewrites (which we call events) replace some of these tokens with others. *SetReplace* can evaluate
+multiple branches of nondeterministic systems simultaneously. That is done by applying different events to the same
+tokens and keeping events and tokens instead of states in [`Multihistory`](/Documentation/Types/Multihistory/README.md)
+objects. We can reconstruct the states from that information afterward.
-Most systems, however, cannot be evaluated completely. And there are multiple groups of parameters to control how to
-perform a partial evaluation. One needs to decide [which events to include](EventSelectionParameters.md), in
-[which order](EventOrderingFunctions.md), and [when to terminate the evaluation](StoppingConditionParameters.md).
+There are parameters that control how this evaluation is done. Some of them control which events to select. Other
+parameters might control how to deduplicate identical tokens, etc. Different generators correspond to different settings
+of parameters. Additional parameters can be specified with a syntax similar to options.
-For example, in a [`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md) we can generate
-a single history (no nondeterministic branching):
+Note that if the system does not terminate, it is necessary to specify some parameters, such as
+[`MaxEvents`](MaxEvents.md) or [`MaxGeneration`](MaxGeneration.md).
+
+For example, [`GenerateSingleHistory`](GenerateSingleHistory.md) corresponds to
+[`MaxDestroyerEvents -> 1`](MaxDestroyerEvents.md) and thus does not produce nondeterministic branching:
```wl
In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}],
- "MaxDestroyerEvents" -> 1,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @ {1, 2, 3, 4}
+ GenerateSingleHistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}]] @ {1, 2, 3, 4}
```
-or multiple histories. Note different events (orange) using the same tokens (light blue):
+We can also use a more general [`GenerateMultihistory`](GenerateMultihistory.md) and specify
+[`MaxDestroyerEvents`](MaxDestroyerEvents.md) manually.
```wl
In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}],
- "MaxDestroyerEvents" -> 2,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @ {1, 2, 3, 4}
+ GenerateMultihistory[
+ MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}], MaxDestroyerEvents -> 2] @ {1, 2, 3, 4}
```
The same generators support multiple systems. In addition to
[`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md), other examples include
-`HypergraphSubstitutionSystem`, `StringSubstitutionSystem`, etc. Many of these systems have shared evaluation
-parameters.
+`HypergraphSubstitutionSystem`, `StringSubstitutionSystem`, etc. Many of these systems have shared parameters.
+
+All generators take the form
+
+```wl
+Generator[System[rules], parameters...] @ init
+```
-[`GenerateMultihistory`](GenerateMultihistory.md) is the universal generator. It can generate everything that the
-specialized `GenerateSingleHistory` and `GenerateFullMultihistory` can produce. However, it is more verbose, which can
-make the code harder to read.
+`parameters` can be specified either as a [`Sequence`](https://reference.wolfram.com/language/ref/Sequence.html) of
+[`Rule`](https://reference.wolfram.com/language/ref/Rule.html)s, a
+[`List`](https://reference.wolfram.com/language/ref/List.html) of
+[`Rule`](https://reference.wolfram.com/language/ref/Rule.html)s or an
+[`Association`](https://reference.wolfram.com/language/ref/Association.html). Keys supported
+in such [`Rule`](https://reference.wolfram.com/language/ref/Rule.html)s depend on the system and can be looked up with
+[`SetReplaceSystemParameters`](SetReplaceSystemParameters.md).
* Introspection:
* [`$SetReplaceSystems`]($SetReplaceSystems.md) — yields the list of all implemented systems
+ * [`$SetReplaceGenerators`]($SetReplaceGenerators.md) — yields the list of all generators
+ * [`SetReplaceSystemParameters`](SetReplaceSystemParameters.md) — yields the list of parameters for a system
* Generators:
- * [`GenerateMultihistory`](GenerateMultihistory.md) — the most customizable and explicit generator
+ * [`GenerateMultihistory`](GenerateMultihistory.md) — the universal generator for multihistories
+ * [`GenerateSingleHistory`](GenerateSingleHistory.md) — sets `MaxDestroyerEvents -> 1`
* Parameters:
- * [`EventSelectionParameters`](EventSelectionParameters.md) — determines which events to include
- * [`EventOrderingFunctions`](EventOrderingFunctions.md) — determines the order of events
- * [`StoppingConditionParameters`](StoppingConditionParameters.md) — determines when to stop evaluation
+ * [`MaxDestroyerEvents`](MaxDestroyerEvents.md) — allows one to switch between single and multihistories
+ * [`MinEventInputs`](MinEventInputs.md)
+ * [`MaxEventInputs`](MaxEventInputs.md)
+ * [`MaxEvents`](MaxEvents.md)
+ * [`MaxGeneration`](MaxGeneration.md) — controls the depth of the evaluation
diff --git a/Documentation/Generators/SetReplaceSystemParameters.md b/Documentation/Generators/SetReplaceSystemParameters.md
new file mode 100644
index 00000000..15e17e76
--- /dev/null
+++ b/Documentation/Generators/SetReplaceSystemParameters.md
@@ -0,0 +1,16 @@
+# SetReplaceSystemParameters
+
+**`SetReplaceSystemParameters`** gives the list of parameters that can be used as keys in generators such as
+[`GenerateMultihistory`](GenerateMultihistory.md):
+
+```wl
+In[] := SetReplaceSystemParameters[MultisetSubstitutionSystem]
+Out[] = {MaxGeneration, MaxDestroyerEvents, MinEventInputs, MaxEventInputs, MaxEvents}
+```
+
+The entire system spec including the rules can be passed as well (rules don't affect the result):
+
+```wl
+In[] := SetReplaceSystemParameters[AtomicStateSystem[a_ :> a + 1]]
+Out[] = {MaxGeneration, MaxDestroyerEvents, MaxEvents}
+```
diff --git a/Documentation/Generators/StoppingConditionParameters.md b/Documentation/Generators/StoppingConditionParameters.md
deleted file mode 100644
index 9852ec62..00000000
--- a/Documentation/Generators/StoppingConditionParameters.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Stopping Condition Parameters
-
-Stopping condition controls when the evaluation should be terminated. Unlike the
-[event selection parameters](EventSelectionParameters.md), the termination occurs immediately once one of these
-conditions is reached, even if a further evaluation would be possible with a different
-[event ordering](EventOrderingFunctions.md).
-
-**`StoppingConditionParameters`** allows one to obtain the list of parameters available for a particular system:
-
-```wl
-In[] := StoppingConditionParameters[MultisetSubstitutionSystem]
-Out[] = {"MaxEvents"}
-```
-
-They typically correspond to the values returned by the `TerminationReason` property. These values can be used as keys
-for the corresponding arguments of [generators](README.md).
-
-## MaxEvents
-
-This is the most basic stopping condition. It stops the evaluation once the given number of events is reached
-(regardless of causal dependencies between these events):
-
-```wl
-In[] := #["ExpressionsEventsGraph"] & @
- SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}],
- {},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {"MaxEvents" -> 9}] @ {1, 2, 3, 4}
-```
-
-
-
-Compare to [`"MaxGeneration"`](EventSelectionParameters.md#maxgeneration) which controls the depth of the evaluation
-instead.
diff --git a/Documentation/Images/GenerateSingleHistoryExample.png b/Documentation/Images/GenerateSingleHistoryExample.png
new file mode 100644
index 00000000..3921c412
Binary files /dev/null and b/Documentation/Images/GenerateSingleHistoryExample.png differ
diff --git a/Documentation/Systems/AtomicStateSystem.md b/Documentation/Systems/AtomicStateSystem.md
index eeb1d6af..8b4481be 100644
--- a/Documentation/Systems/AtomicStateSystem.md
+++ b/Documentation/Systems/AtomicStateSystem.md
@@ -6,11 +6,7 @@ left that can match these states, and the arbitrary code on the right that creat
```wl
In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[AtomicStateSystem[{n_ :> n + 1, n_ :> n - 1}],
- {"MaxGeneration" -> 4},
- None,
- EventOrderingFunctions[AtomicStateSystem],
- {}] @ 0
+ GenerateMultihistory[AtomicStateSystem[{n_ :> n + 1, n_ :> n - 1}], MaxGeneration -> 4][0]
```
diff --git a/Documentation/Systems/MultisetSubstitutionSystem.md b/Documentation/Systems/MultisetSubstitutionSystem.md
index a0ac1162..21930a2b 100644
--- a/Documentation/Systems/MultisetSubstitutionSystem.md
+++ b/Documentation/Systems/MultisetSubstitutionSystem.md
@@ -6,9 +6,8 @@ expressions. Events take submultisets of these and replace them with other multi
The left-hand sides of rules are written as Wolfram Language patterns. The first level of these patterns should match to
a [`List`](https://reference.wolfram.com/language/ref/List.html), and is matched to a multiset of tokens, so
-`{n__Integer, s_String}` and `{s_String, n__Integer}` are equivalent aside from their effect on the
-[event ordering](/Documentation/Generators/EventOrderingFunctions.md) and can match, e.g., `{1, 2, "s"}`, `{2, "x", 3}`
-and `{"q", 1}`.
+`{n__Integer, s_String}` and `{s_String, n__Integer}` are equivalent aside from their effect on the event order and can
+match, e.g., `{1, 2, "s"}`, `{2, "x", 3}` and `{"q", 1}`.
The right-hand sides determine the result of the replacement similar to
[`Replace`](https://reference.wolfram.com/language/ref/Replace.html). The top level of the output must be a
@@ -20,11 +19,7 @@ For example, to make a system that adds pairs of numbers:
```wl
In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] & @
SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}],
- "MaxDestroyerEvents" -> 1,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @ {1, 2, 3, 4}
+ GenerateSingleHistory[MultisetSubstitutionSystem[{a_, b_} /; a < b :> {a + b}]] @ {1, 2, 3, 4}
```
@@ -40,10 +35,7 @@ In[] := #["ExpressionsEventsGraph", VertexLabels -> Placed[Automatic, After]] &
SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
GenerateMultihistory[
MultisetSubstitutionSystem[{a__} /; OrderedQ[{a}] && PrimeQ[Plus[a]] :> First /@ FactorInteger[Plus[a]]],
- {"MinEventInputs" -> 2, "MaxEventInputs" -> 4},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @ {1, 2, 3, 4}
+ MinEventInputs -> 2, MaxEventInputs -> 4] @ {1, 2, 3, 4}
```
@@ -52,26 +44,11 @@ Note, however, that the system cannot recognize if the code on the right-hand si
first output will be used for each assignment of pattern variables.
`MultisetSubstitutionSystem` supports
-[`"MaxGeneration"`](/Documentation/Generators/EventSelectionParameters.md#maxgeneration),
-[`"MaxDestroyerEvents"`](/Documentation/Generators/EventSelectionParameters.md#maxdestroyerevents),
-[`"MinEventInputs"` and `"MaxEventInputs"`](/Documentation/Generators/EventSelectionParameters.md#mineventinputs-and-maxeventinputs)
-for [event selection](/Documentation/Generators/EventSelectionParameters.md). It supports
-[`"MaxEvents"`](/Documentation/Generators/StoppingConditionParameters.md#maxevents) as a
-[stopping condition](/Documentation/Generators/StoppingConditionParameters.md). Only a single
-[event ordering](/Documentation/Generators/EventOrderingFunctions.md)
+[`MaxGeneration`](/Documentation/Generators/MaxGeneration.md),
+[`MaxDestroyerEvents`](/Documentation/Generators/MaxDestroyerEvents.md),
+[`MinEventInputs`](/Documentation/Generators/MinEventInputs.md) and
+[`MaxEventInputs`](/Documentation/Generators/MaxEventInputs.md)
+for event selection. It supports
+[`MaxEvents`](/Documentation/Generators/MaxEvents.md) as a stopping condition. Only a single event order
`{"InputCount", "SortedInputTokenIndices", "InputTokenIndices", "RuleIndex", "InstantiationIndex"}` is implemented at
the moment.
-
-`MultisetSubstitutionSystem` produces
-[`{MultisetSubstitutionSystem, 0}`](/Documentation/Types/Multihistory/MultisetSubstitutionSystem0.md) objects.
-
-## Current Limitations
-
-* The current version does no introspection of the rules, so it is slow since it has to enumerate all subsets of tokens
-in the multihistory for every new event.
-[`"MaxEventInputs"`](/Documentation/Generators/EventSelectionParameters.md#mineventinputs-and-maxeventinputs) can be
-used as a workaround.
-* Token deduplication is not implemented. The only value supported is
-[`None`](https://reference.wolfram.com/language/ref/None.html).
-* Only `{"InputCount", "SortedInputTokenIndices", "InputTokenIndices", "RuleIndex", "InstantiationIndex"}` is
-implemented for event ordering.
diff --git a/Documentation/Types/Multihistory/AtomicStateSystem0.md b/Documentation/Types/Multihistory/AtomicStateSystem0.md
index c04c5ddc..9266086b 100644
--- a/Documentation/Types/Multihistory/AtomicStateSystem0.md
+++ b/Documentation/Types/Multihistory/AtomicStateSystem0.md
@@ -5,8 +5,7 @@
[`AtomicStateSystem`](/Documentation/Systems/AtomicStateSystem.md):
```wl
-In[] := GenerateMultihistory[
- AtomicStateSystem[a_ :> a + 1], {}, None, EventOrderingFunctions[AtomicStateSystem], {"MaxEvents" -> 10}] @ 0
+In[] := GenerateMultihistory[AtomicStateSystem[a_ :> a + 1], MaxEvents -> 10][0]
```
diff --git a/Documentation/Types/Multihistory/MultisetSubstitutionSystem0.md b/Documentation/Types/Multihistory/MultisetSubstitutionSystem0.md
index 4b53e132..97761db3 100644
--- a/Documentation/Types/Multihistory/MultisetSubstitutionSystem0.md
+++ b/Documentation/Types/Multihistory/MultisetSubstitutionSystem0.md
@@ -5,11 +5,7 @@
[`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md):
```wl
-In[] := GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- {},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {"MaxEvents" -> 10}] @ {1, 2, 3}
+In[] := GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}], MaxEvents -> 10] @ {1, 2, 3}
```
@@ -21,4 +17,5 @@ separations of expressions and other values used for optimization.
There are no properties implemented for it yet, however, it can be
[converted](/Documentation/TypeSystem/SetReplaceTypeConvert.md) to a
-[WolframModelEvolutionObject](/Documentation/SymbolsAndFunctions/WolframModelAndWolframModelEvolutionObject/WolframModelAndWolframModelEvolutionObject.md).
+[WolframModelEvolutionObject](/Documentation/SymbolsAndFunctions/WolframModelAndWolframModelEvolutionObject/WolframModelAndWolframModelEvolutionObject.md)
+for backwards compatibility.
diff --git a/Documentation/Types/Multihistory/README.md b/Documentation/Types/Multihistory/README.md
index 4da0bb7b..4cb5b7e4 100644
--- a/Documentation/Types/Multihistory/README.md
+++ b/Documentation/Types/Multihistory/README.md
@@ -6,11 +6,7 @@
For example, for a [`MultisetSubstitutionSystem`](/Documentation/Systems/MultisetSubstitutionSystem.md),
```wl
-In[] := GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- {},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {"MaxEvents" -> 10}] @ {1, 2, 3}
+In[] := GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}], MaxEvents -> 10] @ {1, 2, 3}
```
diff --git a/Kernel/A1$GenerateMultihistory.m b/Kernel/A1$GenerateMultihistory.m
deleted file mode 100644
index 4c7f4790..00000000
--- a/Kernel/A1$GenerateMultihistory.m
+++ /dev/null
@@ -1,219 +0,0 @@
-Package["SetReplace`"]
-
-PackageImport["GeneralUtilities`"]
-
-PackageExport["GenerateMultihistory"]
-PackageExport["$SetReplaceSystems"]
-PackageExport["EventSelectionParameters"]
-PackageExport["EventOrderingFunctions"]
-PackageExport["StoppingConditionParameters"]
-
-PackageScope["generateMultihistory"]
-
-PackageScope["declareMultihistoryGenerator"]
-PackageScope["initializeGenerators"]
-
-SetUsage @ "
-GenerateMultihistory[system$, eventSelectionSpec$, tokenDeduplicationSpec$, eventOrderingSpec$, \
-stoppingConditionSpec$][init$] yields a Multihistory object of the evaluation of a specified system$.
-* A list of all supported systems can be obtained with $SetReplaceSystems.
-* eventSelectionSpec$ is an Association defining constraints on the events that will be generated. The keys that can \
-be used depend on the system$, some examples include 'MaxGeneration' and 'MaxDestroyerEvents'. A list for a particular \
-system can be obtained with EventSelectionParameters[Head[system$]].
-* tokenDeduplicationSpec$ can be set to None or All.
-* eventOrderingSpec$ can be set to 'UniformRandom', 'Any', or a list of partial event ordering functions. The list of \
-supported functions can be obtained with EventOrderingFunctions[Head[system$]].
-* stoppingConditionSpec$ is an Association specifying conditions (e.g., 'MaxEvents') that, if satisfied, will cause \
-the evaluation to stop immediately. The list of choices can be obtained with StoppingConditionParameters[Head[system$]].
-";
-
-SyntaxInformation[GenerateMultihistory] = {"ArgumentsPattern" ->
- {system_, eventSelectionSpec_, tokenDeduplicationSpec_, eventOrderingSpec_, stoppingConditionSpec_}};
-
-(* Declaration *)
-
-$implementations = CreateDataStructure["HashTable"];
-$eventSelectionSpecs = CreateDataStructure["HashTable"]; (* generator -> <|key -> {default, constraint}, ...|> *)
-$eventOrderings = CreateDataStructure["HashTable"]; (* generator -> {ordering, ...} *)
-$stoppingConditionSpecs = CreateDataStructure["HashTable"]; (* generator -> <|key -> {default, constraint}, ...|> *)
-
-$possibleConstraints = None | "NonNegativeIntegerOrInfinity";
-
-$constraintsSpecPattern =
- _Association ? (AllTrue[StringQ] @ Keys[#] && MatchQ[Values[#], {{_, $possibleConstraints}...}] &);
-
-(* Every generator needs to call this function in order to be usable through GenerateMultihistory and related functions.
- The metadata about selection, ordering and stopping conditions will be used to automatically check the arguments.
- The implementation function can expect event selection and stopping conditions to be passed as associations with
- all specified keys present and valid according to the constraint (substituted with defaults if missing).
- Event ordering will be passed as a list of strings from the eventOrderings argument. *)
-
-(* For example,
- declareMultihistoryGenerator[
- generateMultisetSubstitutionSystem,
- MultisetSubstitutionSystem,
- <|"MaxGeneration" -> {Infinity, "NonNegativeIntegerOrInfinity"},
- "MinEventInputs" -> {0, "NonNegativeIntegerOrInfinity"}|>,
- {"InputCount", "SortedInputTokenIndices", "InputTokenIndices", "RuleIndex", "InstantiationIndex"},
- <|"MaxEvents" -> {Infinity, "NonNegativeIntegerOrInfinity"}|>] *)
-
-declareMultihistoryGenerator[implementationFunction_,
- systemType_,
- eventSelectionSpec : $constraintsSpecPattern,
- eventOrderings : {___String},
- stoppingConditionSpec : $constraintsSpecPattern] := (
- $implementations["Insert", systemType -> implementationFunction];
- $eventSelectionSpecs["Insert", systemType -> eventSelectionSpec];
- $eventOrderings["Insert", systemType -> eventOrderings];
- $stoppingConditionSpecs["Insert", systemType -> stoppingConditionSpec];
-);
-
-declareMessage[General::invalidGeneratorDeclaration,
- "Internal error. Multihistory generator is declared incorrectly with arguments `args`."];
-declareMultihistoryGenerator[args___] :=
- message[SetReplace, Failure["invalidGeneratorDeclaration", <|"args" -> {args}|>]];
-
-(* Generator call *)
-
-expr : (generator : GenerateMultihistory[args___])[init___] /;
- CheckArguments[generator, 5] && CheckArguments[expr, 1] := ModuleScope[
- result = Catch[generateMultihistory[args, init],
- _ ? FailureQ,
- message[GenerateMultihistory, #, <|"expr" -> HoldForm[expr]|>] &];
- result /; !FailureQ[result]
-];
-
-generateMultihistory[system_ /; $implementations["KeyExistsQ", Head[system]],
- rawEventSelection_,
- rawTokenDeduplication_,
- rawEventOrdering_,
- rawStoppingCondition_,
- init_] := ModuleScope[
- $implementations["Lookup", Head[system]][
- system,
- parseConstraints["invalidEventSelection"][$eventSelectionSpecs["Lookup", Head[system]]][rawEventSelection],
- parseTokenDeduplication[rawTokenDeduplication],
- parseEventOrdering[$eventOrderings["Lookup", Head[system]]][rawEventOrdering],
- parseConstraints["invalidStoppingCondition"][$stoppingConditionSpecs["Lookup", Head[system]]][rawStoppingCondition],
- init
- ]
-];
-
-declareMessage[General::unknownSystem, "System `system` in `expr` is not recognized."];
-generateMultihistory[system_, __] := throw[Failure["unknownSystem", <|"system" -> system|>]];
-
-(* Parsing *)
-(* In addition to associations, lists of rules and single rules are allowed. *)
-
-parseConstraints[errorName_][specs_][listOfRules : {___Rule}] :=
- parseConstraints[errorName][specs][listOfRules, Association[listOfRules]];
-parseConstraints[errorName_][specs_][rule_Rule] := parseConstraints[errorName][specs][rule, Association[rule]];
-parseConstraints[errorName_][specs_][associationOrInvalid_] :=
- parseConstraints[errorName][specs][associationOrInvalid, associationOrInvalid];
-parseConstraints[_][specs_][_, argument_Association] /; SubsetQ[Keys[specs], Keys[argument]] :=
- Association @ KeyValueMap[#1 -> checkParameter[#1, #2[[2]]] @ Lookup[argument, #1, #2[[1]]] &, specs];
-declareMessage[General::invalidEventSelection,
- "Event selection spec `argument` in `expr` should be an Association with keys from `choices`."];
-declareMessage[General::invalidStoppingCondition,
- "Stopping condition spec `argument` in `expr` should be an Association with keys from `choices`."];
-parseConstraints[errorName_][specs_][originalArgument_, _] :=
- throw[Failure[errorName, <|"argument" -> originalArgument, "choices" -> Keys[specs]|>]];
-
-$tokenDeduplicationValues = {None, All};
-parseTokenDeduplication[value : Alternatives @@ $tokenDeduplicationValues] := value;
-declareMessage[
- General::invalidTokenDeduplication, "Token deduplication `value` in `expr` can only be one of `choices`."];
-parseTokenDeduplication[value_] :=
- throw[Failure["invalidTokenDeduplication", <|"value" -> value, "choices" -> $tokenDeduplicationValues|>]];
-
-parseEventOrdering[supportedFunctions_][argument_List] /; SubsetQ[supportedFunctions, argument] := argument;
-declareMessage[
- General::invalidEventOrdering, "Event ordering spec `argument` in `expr` should be a List of values from `choices`."];
-parseEventOrdering[supportedFunctions_][argument_] :=
- throw[Failure["invalidEventOrdering", <|"argument" -> argument, "choices" -> supportedFunctions|>]];
-
-checkParameter[_, None][value_] := value;
-checkParameter[_, "NonNegativeIntegerOrInfinity"][value : (_Integer ? (# >= 0 &)) | Infinity] := value;
-declareMessage[General::notNonNegativeIntegerOrInfinityParameter,
- "Parameter `name` in `expr` is expected to be a non-negative integer or Infinity."];
-checkParameter[name_, "NonNegativeIntegerOrInfinity"][_] :=
- throw[Failure["notNonNegativeIntegerOrInfinityParameter", <|"name" -> name|>]];
-
-(* Initialization *)
-
-(* It would be best to only show autocompletions for specific-system keys, but it does not seem to be possible because
- dependent argument completions are only supported in WL if the main argument is a string. *)
-
-constraintArgumentCompletions[hashTable_] := Replace[Union @ Catenate[Keys /@ hashTable["Values"]], {} -> 0];
-
-SetUsage @ "
-$SetReplaceSystems gives the list of all computational systems that can be used with GenerateMultihistory and related \
-functions.
-";
-
-initializeGenerators[] := (
- $SetReplaceSystems = Sort @ $implementations["Keys"];
- With[{
- selectionKeys = constraintArgumentCompletions[$eventSelectionSpecs],
- orderings = Replace[Union @ Catenate @ $eventOrderings["Values"], {} -> 0],
- stoppingConditionKeys = constraintArgumentCompletions[$stoppingConditionSpecs]},
- FE`Evaluate[FEPrivate`AddSpecialArgCompletion[
- "GenerateMultihistory" -> {0, selectionKeys, 0, orderings, stoppingConditionKeys, 0}]];
- ];
-);
-
-(* Introspection functions *)
-
-SetUsage @ "
-EventSelectionParameters[system$] yields the list of event selection parameters that can be used with system$.
-";
-
-SyntaxInformation[EventSelectionParameters] = {"ArgumentsPattern" -> {system_}};
-
-expr : EventSelectionParameters[args___] /; CheckArguments[expr, 1] := ModuleScope[
- result = Catch[eventSelectionParameters[args],
- _ ? FailureQ,
- message[EventSelectionParameters, #, <|"expr" -> HoldForm[expr]|>] &];
- result /; !FailureQ[result]
-];
-
-eventSelectionParameters[system_Symbol | system_Symbol[___]] /; $eventSelectionSpecs["KeyExistsQ", system] :=
- Keys @ $eventSelectionSpecs["Lookup", system];
-
-eventSelectionParameters[system_] := throw[Failure["unknownSystem", <|"system" -> system|>]];
-
-SetUsage @ "
-EventOrderingFunctions[system$] yields the list of event ordering functions that can be used with system$.
-";
-
-SyntaxInformation[EventOrderingFunctions] = {"ArgumentsPattern" -> {system_}};
-
-expr : EventOrderingFunctions[args___] /; CheckArguments[expr, 1] := ModuleScope[
- result = Catch[eventOrderingFunctions[args],
- _ ? FailureQ,
- message[EventOrderingFunctions, #, <|"expr" -> HoldForm[expr]|>] &];
- result /; !FailureQ[result]
-];
-
-eventOrderingFunctions[system_Symbol | system_Symbol[___]] /; $eventOrderings["KeyExistsQ", system] :=
- $eventOrderings["Lookup", system];
-
-eventOrderingFunctions[system_] := throw[Failure["unknownSystem", <|"system" -> system|>]];
-
-SetUsage @ "
-StoppingConditionParameters[system$] yields the list of stopping condition parameters that can be used with system$.
-";
-
-SyntaxInformation[StoppingConditionParameters] = {"ArgumentsPattern" -> {system_}};
-
-expr : StoppingConditionParameters[args___] /; CheckArguments[expr, 1] := ModuleScope[
- result = Catch[stoppingConditionParameters[args],
- _ ? FailureQ,
- message[StoppingConditionParameters, #, <|"expr" -> HoldForm[expr]|>] &];
- result /; !FailureQ[result]
-];
-
-stoppingConditionParameters[system_Symbol | system_Symbol[___]] /; $stoppingConditionSpecs["KeyExistsQ", system] :=
- Keys @ $stoppingConditionSpecs["Lookup", system];
-
-stoppingConditionParameters[system_] := throw[Failure["unknownSystem", <|"system" -> system|>]];
diff --git a/Kernel/A1$generatorSystem.m b/Kernel/A1$generatorSystem.m
new file mode 100644
index 00000000..4c5a9557
--- /dev/null
+++ b/Kernel/A1$generatorSystem.m
@@ -0,0 +1,281 @@
+Package["SetReplace`"]
+
+PackageImport["GeneralUtilities`"]
+
+PackageExport["$SetReplaceSystems"]
+PackageExport["$SetReplaceGenerators"]
+PackageExport["SetReplaceSystemParameters"]
+
+PackageScope["declareSystemParameter"]
+PackageScope["declareSystem"]
+PackageScope["declareSystemGenerator"]
+PackageScope["initializeSystemGenerators"]
+
+(* Parameter declaration *)
+
+$parameterDefaults = <||>;
+$parameterPatterns = <||>;
+
+(* Both systems and generators use parameters. Systems declare parameters they implement. Generators set fixed values
+ for a subset of parameters. To declare a new parameter, one needs to specify a default value (which usually disables
+ whatever the parameter is doing) and a pattern the parameter value should match. *)
+
+(* declareSystemParameter[MaxGeneration,
+ Infinity,
+ _ ? (GreaterEqualThan[0]),
+ "is a parameter specifying the maximum generations of tokens that will be created."] *)
+
+declareSystemParameter[name_, defaultValue_, pattern_, usage_] := (
+ $parameterDefaults[name] = defaultValue;
+ $parameterPatterns[name] = pattern;
+ SyntaxInformation[name] = {"ArgumentsPattern" -> {}};
+ SetUsage @ Evaluate[ToString[name] <> " " <> usage];
+);
+
+declareMessage[General::invalidSystemParameterDeclaration,
+ "Internal error. Parameter is declared incorrectly with arguments `args`."];
+declareSystemParameter[args___] :=
+ message[SetReplace, Failure["invalidSystemParameterDeclaration", <|"args" -> {args}|>]];
+
+(* System declaration *)
+
+$systemImplementations = <||>; (* system -> implementationFunction *)
+$systemInitPatterns = <||>; (* system -> pattern *)
+$systemParameters = <||>; (* system -> {parameter, ...} *)
+$systemParameterDependencies = <||>; (* system -> logical expression on parameter keys *)
+
+(* Every system implementation needs to call this function in order to be usable through GenerateMultihistory and other
+ generators. *)
+
+(* The third argument is the pattern that the init should satisfy. *)
+
+(* Parameters in the fourth argument should be declared with declareSystemParameter. Their values are guaranteed to
+ match the pattern from that declaration. Further, the logical expression in the last argument will check if all
+ required parameters are specified. Some parameters may require others to be specified, e.g.,
+ Implies[MaxDestroyerEvents || MaxEvents, EventOrder] means that if MaxDestroyerEvents or MaxEvents is specified,
+ EventOrder must be specified as well. The implementation function can expect all specified parameters present
+ (substituted with defaults if missing) and all values will satisfy the constraints (substituted with defaults if
+ missing). *)
+
+(* declareSystem[MultisetSubstitutionSystem,
+ generateMultisetSubstitutionSystem,
+ _List,
+ {MaxGeneration, MinEventInputs, MaxDestroyerEvents, MaxEvents, EventOrder},
+ Implies[MaxDestroyerEvents || MaxEvents, EventOrder]] *)
+
+(* The implementation function is then called as
+ generateMultisetSubstitutionSystem[MultisetSubstitutionSystem[rules], init, <|MaxGeneration -> value, ...|>] *)
+
+declareSystem[systemType_,
+ implementationFunction_,
+ initPattern_,
+ parameters_List,
+ dependencies_ ? SatisfiableQ] := (
+ $systemImplementations[systemType] = implementationFunction;
+ $systemInitPatterns[systemType] = initPattern;
+ $systemParameters[systemType] = parameters;
+ $systemParameterDependencies[systemType] = dependencies;
+);
+
+declareMessage[General::unsatisfiableParameterDependencies,
+ "Internal error. Parameter constraints `constraints` for `system` are not satisfiable."];
+declareSystem[systemType_, _, _, _List, dependencies_] :=
+ message[SetReplace,
+ Failure["unsatisfiableParameterDependencies", <|"constraints" -> dependencies, "system" -> systemType|>]];
+
+declareMessage[General::invalidSystemDeclaration,
+ "Internal error. System is declared incorrectly with arguments `args`."];
+declareSystem[args___] :=
+ message[SetReplace, Failure["invalidSystemDeclaration", <|"args" -> {args}|>]];
+
+(* Generator declaration *)
+
+$generatorPackageScopeSymbols = <||>; (* generator (public symbol) -> package-scope symbol *)
+$generatorParameters = <||>; (* generator -> <|parameter -> value, ...|> *)
+$generatorProperties = <||>; (* generator -> property *)
+
+(* Generators are functions that are called to produce Multihistory objects. They take the form
+ symbol[system, init, params]. They also define a fixed set of parameter values. These parameter values cannot be
+ changed in params. Generators can also compute a property at the end of the evaluation. *)
+
+(* declareSystemGenerator[EvaluateSingleHistory,
+ evaluateSingleHistory,
+ <|MaxDestroyerEvents -> 1|>,
+ FinalState,
+ "yields a single-history object."]
+
+ Note that the constraint in the last argument of declareSystemGenerator still needs to be specified, which means
+ EventOrder is now a required parameter. *)
+
+(* evaluateSingleHistory is a PackageScope symbol that will throw exceptions instead of returning unevaluated.
+ It cannot be used in operator form. *)
+
+$systemUsage = "* A list of all supported systems can be obtained with $SetReplaceSystems.";
+$parametersUsage = "* parameters$ is either a Sequence, a List or an Association of key-value rules. A list of " <>
+ "parameter keys can be obtained with SetReplaceSystemParameters[system$].";
+$initUsage = "* init$ is the initial state, the format of which depends on the system$.";
+
+declareSystemGenerator[publicSymbol_, packageScopeSymbol_, parameterValues_, property_, usage_] := (
+ $generatorPackageScopeSymbols[publicSymbol] = packageScopeSymbol;
+ $generatorParameters[publicSymbol] = parameterValues;
+ $generatorProperties[publicSymbol] = property;
+ SyntaxInformation[publicSymbol] = {"ArgumentsPattern" -> {system_, parameters___}};
+ SetUsage @ Evaluate @ StringRiffle[
+ {ToString[publicSymbol] <> "[system$, parameters$][init$] " <> usage, $systemUsage, $parametersUsage, $initUsage},
+ "\n"];
+);
+
+declareMessage[General::invalidSystemGeneratorDeclaration,
+ "Internal error. Generator is declared incorrectly with arguments `args`."];
+declareSystemGenerator[args___] :=
+ message[SetReplace, Failure["invalidSystemGeneratorDeclaration", <|"args" -> {args}|>]];
+
+(* Initialization *)
+
+SetUsage @ "
+$SetReplaceSystems gives the list of all computational systems that can be used with GenerateMultihistory and related \
+functions.
+";
+
+SetUsage @ "
+$SetReplaceGenerators gives the list of all generators that can be used to evaluate systems such as \
+MultisetSubstitutionSystem.
+";
+
+declareMessage[
+ General::unknownSystemParameters, "Parameters `parameters` are implemented by `system` but not declared."];
+declareMessage[General::unknownGeneratorParameters, "Parameters `parameters` are set by `generator` but not declared."];
+
+initializeSystemGenerators[] := (
+ $SetReplaceSystems = Sort @ Keys @ $systemImplementations;
+ $SetReplaceGenerators = Sort @ Keys @ $generatorParameters;
+ With[{missingParameters = Complement[$systemParameters[#], Keys[$parameterDefaults]]},
+ If[missingParameters =!= {},
+ message[SetReplace, Failure["unknownSystemParameters", <|"parameters" -> missingParameters, "system" -> #|>]];
+ ];
+ ] & /@ $SetReplaceSystems;
+ With[{missingParameters = Complement[Keys @ $generatorParameters[#], Keys[$parameterDefaults]]},
+ If[missingParameters =!= {},
+ message[
+ SetReplace, Failure["unknownGeneratorParameters", <|"parameters" -> missingParameters, "generator" -> #|>]];
+ ];
+ ] & /@ $SetReplaceGenerators;
+ defineGeneratorImplementation /@ Keys @ $generatorParameters;
+);
+
+declareMessage[General::argNotInit, "The init `arg` in `expr` should match `pattern`."];
+declareMessage[General::unknownSystem, "`system` is not a recognized SetReplace system."];
+declareMessage[General::noRules, "Rules need to be specified as `system`[\[Ellipsis]] in `expr`."];
+
+defineGeneratorImplementation[generator_] := With[{packageScopeGenerator = $generatorPackageScopeSymbols[generator]},
+ expr : (operator : generator[args1___])[args2___] /;
+ CheckArguments[operator, {1, Infinity}] && CheckArguments[expr, {1, 1}] := ModuleScope[
+ result = Catch[packageScopeGenerator[args1][args2],
+ _ ? FailureQ,
+ message[generator, #, <|"expr" -> HoldForm[expr]|>] &];
+ result /; !FailureQ[result]
+ ];
+
+ packageScopeGenerator[system_, parameters___][init_] := (
+ If[MissingQ[$systemImplementations[Head[system]]],
+ If[MissingQ[$systemImplementations[system]],
+ throw[Failure["unknownSystem", <|"system" -> system|>]]
+ ,
+ throw[Failure["noRules", <|"system" -> system|>]]
+ ];
+ ];
+ If[!MatchQ[init, Lookup[$systemInitPatterns, Head[system]]],
+ throw[Failure["argNotInit", <|"arg" -> arg, "pattern" -> $systemInitPatterns[Head[system]]|>]]
+ ];
+ checkSystemGeneratorCompatibility[Head[system], generator];
+ $generatorProperties[generator] @
+ $systemImplementations[Head[system]][system, init, parseParameters[generator, Head[system]][parameters]]
+ );
+];
+
+declareMessage[
+ General::incompatibleSystem, "`generator` requires `parameters` parameters, which `system` does not implement."];
+checkSystemGeneratorCompatibility[system_, generator_] := With[{
+ missingParameters = Complement[Keys @ $generatorParameters[generator], $systemParameters[system]]},
+ If[missingParameters =!= {},
+ throw[Failure[
+ "incompatibleSystem", <|"generator" -> generator, "parameters" -> missingParameters, "system" -> system|>]];
+ ];
+];
+
+parseParameters[generator_, system_][parameters___] :=
+ addMissingParameters[generator, system] @
+ checkParameters[generator, system] @ Association[Join @@ collectParameters /@ {parameters}];
+
+collectParameters[key_ -> value_] := <|key -> value|>;
+collectParameters[key_ :> value_] := <|key -> value|>;
+collectParameters[list_List] := Association[Join @@ collectParameters /@ list];
+collectParameters[association_Association] := association;
+declareMessage[General::invalidGeneratorParameterSpec, "Parameter specification `spec` in `expr` should be a Rule."];
+collectParameters[spec_] := throw[Failure["invalidGeneratorParameterSpec", <|"spec" -> spec|>]];
+
+checkParameters[generator_, system_][parameters_] := (
+ KeyValueMap[checkParameter[generator, system][##] &, parameters];
+ checkParameterCompleteness[generator, system][Keys[parameters]];
+ parameters
+);
+checkParameter[generator_, system_][key_, value_] := (
+ checkParameterKeyIsRecognized[system][key];
+ checkParameterKeyIsNotForbidden[generator][key];
+ checkParameterValueMatchesPattern[key, value];
+);
+
+declareMessage[General::unknownParameter, "`system` in `expr` does not support `parameter` parameter."];
+checkParameterKeyIsRecognized[system_][key_] /; !MemberQ[$systemParameters[system], key] :=
+ throw[Failure["unknownParameter", <|"system" -> system, "parameter" -> key|>]];
+
+declareMessage[General::forbiddenParameter, "`parameter` in `expr` cannot be used with `generator`."];
+checkParameterKeyIsNotForbidden[generator_][key_] /; KeyExistsQ[$generatorParameters[generator], key] :=
+ throw[Failure["forbiddenParameter", <|"generator" -> generator, "parameter" -> key|>]];
+
+declareMessage[
+ General::invalidParameter, "`parameter` value `value` in `expr` should match `pattern`."];
+checkParameterValueMatchesPattern[key_, value_] /; !MatchQ[value, $parameterPatterns[key]] :=
+ throw[Failure[
+ "invalidParameter", <|"parameter" -> key, "value" -> value, "pattern" -> $parameterPatterns[key]|>]];
+
+declareMessage[General::incompatibleParameters,
+ "Parameters in `expr` are incompatible. Specified parameters should satisfy `condition`."];
+checkParameterCompleteness[generator_, system_][keys_] /;
+ simplifyParameterCondition[generator, system, keys] === False :=
+ throw[Failure["incompatibleParameters", <|"condition" -> $systemParameterDependencies[system]|>]];
+
+simplifyParameterCondition[generator_, system_, specifiedKeys_] :=
+ FullSimplify[
+ $systemParameterDependencies[system] /.
+ Alternatives @@ Join[specifiedKeys, Keys @ $generatorParameters[generator]] -> True];
+
+declareMessage[General::missingParameters, "`missingParameters` should be explicitly specified in `expr`."];
+checkParameterCompleteness[generator_, system_][keys_] /; !compatibleParametersQ[generator, system, keys] :=
+ throw[Failure["missingParameters", <|"missingParameters" -> simplifyParameterCondition[generator, system, keys]|>]];
+
+compatibleParametersQ[generator_, system_, specifiedKeys_] :=
+ FullSimplify[
+ $systemParameterDependencies[system] /.
+ Alternatives @@ Join[specifiedKeys, Keys @ $generatorParameters[generator]] -> True /.
+ Alternatives @@ $systemParameters[system] -> False];
+compatibleParametersQ[___] := False;
+
+addMissingParameters[generator_, system_][parameters_] :=
+ Join[KeyTake[$parameterDefaults, $systemParameters[system]], parameters, $generatorParameters[generator]];
+
+(* Introspection functions *)
+
+SetUsage @ "SetReplaceSystemParameters[system$] yields the list of parameters supported by the system$.";
+
+SyntaxInformation[SetReplaceSystemParameters] = {"ArgumentsPattern" -> {system_}};
+
+expr : SetReplaceSystemParameters[args___] /; CheckArguments[expr, 1] := ModuleScope[
+ result = Catch[setReplaceSystemParameters[args],
+ _ ? FailureQ,
+ message[SetReplaceSystemParameters, #, <|"expr" -> HoldForm[expr]|>] &];
+ result /; !FailureQ[result]
+];
+
+setReplaceSystemParameters[system_Symbol[___] | system_] :=
+ Lookup[$systemParameters, system, throw[Failure["unknownSystem", <|"system" -> system|>]]];
diff --git a/Kernel/AtomicStateSystem.m b/Kernel/AtomicStateSystem.m
index 006c35af..07e4dfc9 100644
--- a/Kernel/AtomicStateSystem.m
+++ b/Kernel/AtomicStateSystem.m
@@ -12,41 +12,12 @@
SyntaxInformation[AtomicStateSystem] = {"ArgumentsPattern" -> {rules_}};
-declareMultihistoryGenerator[
- generateAtomicStateSystem,
- AtomicStateSystem,
- <|"MaxGeneration" -> {Infinity, "NonNegativeIntegerOrInfinity"},
- "MaxDestroyerEvents" -> {Infinity, "NonNegativeIntegerOrInfinity"}|>,
- {"InputIndex", "RuleIndex"},
- <|"MaxEvents" -> {Infinity, "NonNegativeIntegerOrInfinity"}|>];
-
-declareMessage[General::atomicStateTokenDeduplicationNotImplemented,
- "Token deduplication is not implemented for Atomic State System."];
-
-$supportedEventOrdering = {"InputIndex", "RuleIndex"};
-
-declareMessage[General::atomicStateEventOrderingNotImplemented,
- "Only " <> ToString[$supportedEventOrdering] <> " event ordering is implemented at this time."];
-
-generateAtomicStateSystem[AtomicStateSystem[rules___], (* BlankNullSequence is needed to catch invalid arg counts *)
- eventSelection_,
- tokenDeduplication_,
- eventOrdering_,
- stoppingCondition_,
- init_] := ModuleScope[
- If[tokenDeduplication =!= None, throw[Failure["atomicStateTokenDeduplicationNotImplemented", <||>]]];
- If[eventOrdering =!= {"InputIndex", "RuleIndex"}, throw[Failure["atomicStateEventOrderingNotImplemented", <||>]]];
+declareSystem[AtomicStateSystem, generateAtomicStateSystem, _, {MaxGeneration, MaxDestroyerEvents, MaxEvents}, True];
+generateAtomicStateSystem[AtomicStateSystem[rules___], init_, parameters_] := ModuleScope[
toAtomicStateMultihistory[rules] @ generateMultihistory[
MultisetSubstitutionSystem[toMultisetRules[rules]],
- <|"MaxGeneration" -> eventSelection["MaxGeneration"],
- "MaxDestroyerEvents" -> eventSelection["MaxDestroyerEvents"],
- "MinEventInputs" -> 1,
- "MaxEventInputs" -> 1|>,
- None,
- {"InputCount", "SortedInputTokenIndices", "InputTokenIndices", "RuleIndex", "InstantiationIndex"},
- stoppingCondition,
- {init}]
+ Join[parameters, <|MinEventInputs -> 1, MaxEventInputs -> 1|>]] @ {init}
];
(* Parsing *)
diff --git a/Kernel/MultisetSubstitutionSystem.m b/Kernel/MultisetSubstitutionSystem.m
index c0edccf9..9af7ee8b 100644
--- a/Kernel/MultisetSubstitutionSystem.m
+++ b/Kernel/MultisetSubstitutionSystem.m
@@ -13,34 +13,22 @@
SyntaxInformation[MultisetSubstitutionSystem] = {"ArgumentsPattern" -> {rules_}};
-declareMultihistoryGenerator[
- generateMultisetSubstitutionSystem,
- MultisetSubstitutionSystem,
- <|"MaxGeneration" -> {Infinity, "NonNegativeIntegerOrInfinity"},
- "MaxDestroyerEvents" -> {Infinity, "NonNegativeIntegerOrInfinity"},
- "MinEventInputs" -> {0, "NonNegativeIntegerOrInfinity"},
- "MaxEventInputs" -> {Infinity, "NonNegativeIntegerOrInfinity"}|>,
- {"InputCount", "SortedInputTokenIndices", "InputTokenIndices", "RuleIndex", "InstantiationIndex"},
- <|"MaxEvents" -> {Infinity, "NonNegativeIntegerOrInfinity"}|>];
-
-generateMultisetSubstitutionSystem[MultisetSubstitutionSystem[rawRules___],
- rawEventSelection_,
- rawTokenDeduplication_,
- rawEventOrdering_,
- rawStoppingCondition_,
- rawInit_] := Block[{
+declareSystem[MultisetSubstitutionSystem,
+ generateMultisetSubstitutionSystem,
+ _List,
+ {MaxGeneration, MaxDestroyerEvents, MinEventInputs, MaxEventInputs, MaxEvents},
+ True];
+
+generateMultisetSubstitutionSystem[MultisetSubstitutionSystem[rawRules___], init_, parameters_] := Block[{
expressions, eventRuleIndices, eventInputs, eventOutputs, eventGenerations, expressionCreatorEvents,
expressionDestroyerEventCounts, destroyerChoices, instantiationCounts, instantiations},
- Module[{rules, maxGeneration, maxDestroyerEvents, minEventInputs, maxEventInputs, maxEvents, init, terminationReason},
+ Module[{rules, ruleInputCountRanges, maxGeneration, maxDestroyerEvents, minEventInputs, maxEventInputs, maxEvents,
+ terminationReason},
rules = parseRules[rawRules];
ruleInputCountRanges = inputCountRange /@ rules;
- {maxGeneration, maxDestroyerEvents, minEventInputs, maxEventInputs} = Values @ rawEventSelection;
+ {maxGeneration, maxDestroyerEvents, minEventInputs, maxEventInputs, maxEvents} = Values @ parameters;
minEventInputs = Max[minEventInputs, Min[ruleInputCountRanges[[All, 1]]]];
maxEventInputs = Min[maxEventInputs, Max[ruleInputCountRanges[[All, 2]]]];
- parseTokenDeduplication[rawTokenDeduplication]; (* Token deduplication is not implemented at the moment *)
- parseEventOrdering[rawEventOrdering]; (* Event ordering is not implemented at the moment *)
- {maxEvents} = Values @ rawStoppingCondition;
- init = parseInit[rawInit];
(* "HashTable" is causing memory leaks, so we are using Data`UnorderedAssociation instead. *)
@@ -318,19 +306,3 @@ according to some (but not all) ordering functions. This new data structure will
(* Since we have enumerated all pattern constructs above, this case does not correspond to a pattern.
However, the completeness of checks above needs to be checked for every new WL version. *)
sequencePatternLengthRange[_] := If[$VersionNumber <= 12.3, {1, 1}, {0, Infinity}];
-
-parseTokenDeduplication[None] := None;
-declareMessage[General::multisetTokenDeduplicationNotImplemented,
- "Token deduplication is not implemented for Multiset Substitution System."];
-parseTokenDeduplication[_] := throw[Failure["multisetTokenDeduplicationNotImplemented", <||>]];
-
-$supportedEventOrdering =
- {"InputCount", "SortedInputTokenIndices", "InputTokenIndices", "RuleIndex", "InstantiationIndex"};
-parseEventOrdering[ordering : $supportedEventOrdering] := ordering;
-declareMessage[General::multisetEventOrderingNotImplemented,
- "Only " <> ToString[$supportedEventOrdering] <> " event ordering is implemented at this time."];
-parseEventOrdering[_] := throw[Failure["multisetEventOrderingNotImplemented", <||>]];
-
-parseInit[init_List] := init;
-declareMessage[General::multisetInitNotList, "Multiset Substitution System init `init` should be a List."];
-parseInit[init_] := throw[Failure["multisetInitNotList", <|"init" -> init|>]];
diff --git a/Kernel/init.m b/Kernel/init.m
index 61fdcfee..47094078 100644
--- a/Kernel/init.m
+++ b/Kernel/init.m
@@ -32,7 +32,7 @@
(* Type system and generators should be initialized after all Kernel files are loaded. *)
SetReplace`PackageScope`initializeTypeSystem[];
- SetReplace`PackageScope`initializeGenerators[];
+ SetReplace`PackageScope`initializeSystemGenerators[];
];
End[];
diff --git a/Kernel/systemGenerators.m b/Kernel/systemGenerators.m
new file mode 100644
index 00000000..748f5a17
--- /dev/null
+++ b/Kernel/systemGenerators.m
@@ -0,0 +1,23 @@
+Package["SetReplace`"]
+
+PackageImport["GeneralUtilities`"]
+
+PackageExport["GenerateMultihistory"]
+PackageExport["GenerateSingleHistory"]
+
+PackageScope["generateMultihistory"]
+PackageScope["generateSingleHistory"]
+
+declareSystemGenerator[
+ GenerateMultihistory,
+ generateMultihistory,
+ <||>,
+ Identity,
+ "yields a Multihistory object of the evaluation of a specified system$ starting from an initial state init$."];
+
+declareSystemGenerator[
+ GenerateSingleHistory,
+ generateSingleHistory,
+ <|MaxDestroyerEvents -> 1|>,
+ Identity,
+ "yields a single history of the evaluation of a specified system$ starting from an initial state init$."];
diff --git a/Kernel/systemParameters.m b/Kernel/systemParameters.m
new file mode 100644
index 00000000..c0ed1b53
--- /dev/null
+++ b/Kernel/systemParameters.m
@@ -0,0 +1,31 @@
+Package["SetReplace`"]
+
+(* Event-selection parameters *)
+
+PackageExport["MaxGeneration"]
+PackageExport["MaxDestroyerEvents"]
+PackageExport["MinEventInputs"]
+PackageExport["MaxEventInputs"]
+PackageExport["MaxEvents"]
+
+declareSystemParameter[#, #2, _ ? (GreaterEqualThan[0]), #3] & @@@ {
+ {MaxGeneration,
+ Infinity,
+ "is an event-selection parameter specifying the maximum generation of created tokens.\n" <>
+ "* Tokens in init$ are assumed to have generation 0.\n" <>
+ "* A generation of an event and its output tokens is defined as the largest generation of its inputs plus one."},
+ {MaxDestroyerEvents,
+ Infinity,
+ "is an event-selection parameter specifying the number of events that can use a single token as their input.\n" <>
+ "* Setting this to 1 limits evaluation to a single history."},
+ {MinEventInputs, 0, "is an event-selection parameter specifying the minimum number of input tokens of events."},
+ {MaxEventInputs, Infinity, "is an event-selection parameter specifying the maximum number of input tokens of events."}
+};
+
+(* Stopping-condition parameters *)
+
+declareSystemParameter[
+ MaxEvents,
+ Infinity,
+ _ ? (GreaterEqualThan[0]),
+ "is a stopping-condition parameter that stops evaluation after a specified number of events."];
diff --git a/Tests/AtomicStateSystem.wlt b/Tests/AtomicStateSystem.wlt
index 99f8433a..2121cda5 100644
--- a/Tests/AtomicStateSystem.wlt
+++ b/Tests/AtomicStateSystem.wlt
@@ -9,54 +9,37 @@
allExpressions[Multihistory[_, data_]] := Normal @ data["MultisetMultihistory"][[2]]["Expressions"];
),
"tests" -> {
- With[{anEventOrdering = EventOrderingFunctions[AtomicStateSystem]}, {
- (* Symbol Leak *)
- testSymbolLeak[
- GenerateMultihistory[AtomicStateSystem[a_ :> a + 1], {}, None, anEventOrdering, {"MaxEvents" -> 5}] @ 0],
+ (* Symbol Leak *)
+ testSymbolLeak[GenerateMultihistory[AtomicStateSystem[a_ :> a + 1], MaxEvents -> 5][0]],
- (* Rules *)
- testUnevaluated[GenerateMultihistory[AtomicStateSystem[##2], {}, None, anEventOrdering, {}] @ 1, {#}] & @@@
- {{AtomicStateSystem::argx},
- {AtomicStateSystem::argx, 1, 2},
- {GenerateMultihistory::invalidAtomicStateRules, 1}},
+ (* Rules *)
+ testUnevaluated[GenerateMultihistory[AtomicStateSystem[##2]][1], {#}] & @@@ {
+ {AtomicStateSystem::argx},
+ {AtomicStateSystem::argx, 1, 2},
+ {GenerateMultihistory::invalidAtomicStateRules, 1}},
- (* Ordering not yet supported *)
- testUnevaluated[GenerateMultihistory[AtomicStateSystem[1 -> 2], {}, None, {"RuleIndex"}, {}] @ 1,
- {GenerateMultihistory::atomicStateEventOrderingNotImplemented}],
+ (* Parameters *)
+ testUnevaluated[GenerateMultihistory[AtomicStateSystem[1 -> 2], MaxGeneration -> -1][1],
+ {GenerateMultihistory::invalidParameter}],
- (* Token deduplication not yet supported *)
- testUnevaluated[GenerateMultihistory[AtomicStateSystem[1 -> 2], {}, All, anEventOrdering, {}] @ 1,
- {GenerateMultihistory::atomicStateTokenDeduplicationNotImplemented}],
+ Function[{rules, parameters, init, expectedCreatedExpressions},
+ VerificationTest[allExpressions @ GenerateMultihistory[AtomicStateSystem[rules], parameters] @ init,
+ Join[{init}, expectedCreatedExpressions],
+ SameTest -> MatchQ]] @@@ {
+ {1 -> 1, MaxEvents -> 1, 1, {1}},
+ {1 -> 2, MaxEvents -> 1, 1, {2}},
+ {1 -> 2, MaxGeneration -> 1, 1, {2}},
+ {2 :> 5, MaxGeneration -> 1, 2, {5}},
+ {{2 -> 5, 2 :> 6}, MaxGeneration -> 1, 2, {5, 6}},
+ {{2 :> 3, 3 :> 4}, MaxGeneration -> 2, 2, {3, 4}},
+ {x_ :> x + 1, MaxGeneration -> 2, 2, {3, 4}},
+ {{x_ :> x - 1, x_ :> x + 1}, MaxGeneration -> 2, 0, {-1, 1, -2, 0, 0, 2}},
+ {{x_ ? EvenQ :> x - 1, x_ ? OddQ :> x + 1}, MaxGeneration -> 2, 0, {-1, 0}},
+ (* only valid patterns should be matched *)
+ {x_String :> x <> "x", MaxEvents -> 2, 0, {}}},
- (* Parameters *)
- testUnevaluated[
- GenerateMultihistory[AtomicStateSystem[1 -> 2], {"MaxGeneration" -> -1}, None, anEventOrdering, {}] @ 1,
- {GenerateMultihistory::notNonNegativeIntegerOrInfinityParameter}],
-
- Function[{rules, selection, stopping, init, expectedCreatedExpressions},
- VerificationTest[
- allExpressions @ GenerateMultihistory[
- AtomicStateSystem[rules], selection, None, anEventOrdering, stopping] @ init,
- Join[{init}, expectedCreatedExpressions],
- SameTest -> MatchQ]] @@@
- {{1 -> 1, <||>, <|"MaxEvents" -> 1|>, 1, {1}},
- {1 -> 2, <||>, <|"MaxEvents" -> 1|>, 1, {2}},
- {1 -> 2, <|"MaxGeneration" -> 1|>, <||>, 1, {2}},
- {2 :> 5, <|"MaxGeneration" -> 1|>, <||>, 2, {5}},
- {{2 -> 5, 2 :> 6}, <|"MaxGeneration" -> 1|>, <||>, 2, {5, 6}},
- {{2 :> 3, 3 :> 4}, <|"MaxGeneration" -> 2|>, <||>, 2, {3, 4}},
- {x_ :> x + 1, <|"MaxGeneration" -> 2|>, <||>, 2, {3, 4}},
- {{x_ :> x - 1, x_ :> x + 1}, <|"MaxGeneration" -> 2|>, <||>, 0, {-1, 1, -2, 0, 0, 2}},
- {{x_ ? EvenQ :> x - 1, x_ ? OddQ :> x + 1}, <|"MaxGeneration" -> 2|>, <||>, 0, {-1, 0}},
- (* only valid patterns should be matched *)
- {x_String :> x <> "x", {}, {"MaxEvents" -> 2}, 0, {}}}
- }],
-
- VerificationTest[
- allExpressions @ GenerateMultihistory[
- AtomicStateSystem[{1 -> 3, 2 -> 3, 0 -> 1, 0 -> 2}], {}, None, {"InputIndex", "RuleIndex"}, {}] @ 0,
- {0, 1, 2, 3, 3}
- ]
+ VerificationTest[allExpressions @ GenerateMultihistory[AtomicStateSystem[{1 -> 3, 2 -> 3, 0 -> 1, 0 -> 2}]][0],
+ {0, 1, 2, 3, 3}]
}
|>
|>
diff --git a/Tests/GenerateMultihistory.wlt b/Tests/GenerateMultihistory.wlt
deleted file mode 100644
index 58e40e89..00000000
--- a/Tests/GenerateMultihistory.wlt
+++ /dev/null
@@ -1,120 +0,0 @@
-<|
- "GenerateMultihistory" -> <|
- "init" -> (
- Attributes[Global`testUnevaluated] = {HoldAll};
- Global`testUnevaluated[args___] := SetReplace`PackageScope`testUnevaluated[VerificationTest, args];
- Global`declareMultihistoryGenerator = SetReplace`PackageScope`declareMultihistoryGenerator;
- Global`initializeGenerators = SetReplace`PackageScope`initializeGenerators;
-
- SetReplace`PackageScope`declareMultihistoryGenerator[
- testSystemImplementation,
- TestSystem,
- <|"MaxGeneration" -> {Infinity, "NonNegativeIntegerOrInfinity"},
- "MinEventInputs" -> {0, "NonNegativeIntegerOrInfinity"},
- "EventPattern" -> {_, None}|>,
- {"InputCount", "RuleOrdering"},
- <|"MaxEvents" -> {Infinity, "NonNegativeIntegerOrInfinity"},
- "MinCausalDensityDimension" -> {2., None},
- "TokenEventGraphTest" -> {True &, None}|>];
-
- $originalSetReplaceSystem = $SetReplaceSystems;
- Unprotect[$SetReplaceSystems];
- initializeGenerators[];
- Protect[$SetReplaceSystems];
- ),
- "tests" -> {
- (* It's useful to catch unprocessed declarations early *)
- VerificationTest[declareMultihistoryGenerator[a, b, {}, {}, <||>],
- _,
- {SetReplace::invalidGeneratorDeclaration},
- SameTest -> MatchQ],
-
- (* GenerateMultihistory *)
-
- $defaultEventSelection = <|"MaxGeneration" -> Infinity, "MinEventInputs" -> 0, "EventPattern" -> _|>;
- $defaultStoppingConditions =
- <|"MaxEvents" -> Infinity, "MinCausalDensityDimension" -> 2., "TokenEventGraphTest" -> (True &)|>;
-
- testUnevaluated[GenerateMultihistory[][], GenerateMultihistory::argrx],
- testUnevaluated[GenerateMultihistory[][0], GenerateMultihistory::argrx],
- testUnevaluated[GenerateMultihistory[0][], GenerateMultihistory::argr],
- testUnevaluated[GenerateMultihistory[0, 1, 2, 3, 4, 5][], GenerateMultihistory::argrx],
- testUnevaluated[GenerateMultihistory[0, 1, 2, 3, 4][], GenerateMultihistory::argx],
- testUnevaluated[GenerateMultihistory[0, 1, 2, 3, 4][1, 2], GenerateMultihistory::argx],
-
- testUnevaluated[GenerateMultihistory[#, 1, 2, 3, 4][0], GenerateMultihistory::unknownSystem] & /@
- {UnknownSystem[], TestSystem, TestSystem[][]},
-
- testUnevaluated[GenerateMultihistory[TestSystem[], 1, 2, 3, 4][0], GenerateMultihistory::invalidEventSelection],
- testUnevaluated[GenerateMultihistory[TestSystem[], <|"Unknown" -> 0|>, 2, 3, 4][0],
- GenerateMultihistory::invalidEventSelection],
- testUnevaluated[GenerateMultihistory[TestSystem[], <|"MaxGeneration" -> -1|>, 2, 3, 4][0],
- GenerateMultihistory::notNonNegativeIntegerOrInfinityParameter],
- VerificationTest[
- GenerateMultihistory[TestSystem[], #, None, {}, <||>][0],
- testSystemImplementation[TestSystem[],
- <|"MaxGeneration" -> 2, "MinEventInputs" -> 0, "EventPattern" -> _|>,
- None,
- {},
- $defaultStoppingConditions,
- 0]] & /@ {<|"MaxGeneration" -> 2|>, {"MaxGeneration" -> 2}, "MaxGeneration" -> 2},
- VerificationTest[
- GenerateMultihistory[TestSystem[], <|"EventPattern" -> {__} -> {_}|>, None, {}, <||>][0],
- testSystemImplementation[TestSystem[],
- <|"MaxGeneration" -> Infinity, "MinEventInputs" -> 0, "EventPattern" -> {__} -> {_}|>,
- None,
- {},
- $defaultStoppingConditions,
- 0]],
-
- testUnevaluated[
- GenerateMultihistory[TestSystem[], <||>, 2, 3, 4][0], GenerateMultihistory::invalidTokenDeduplication],
- VerificationTest[
- GenerateMultihistory[TestSystem[], <||>, #, {}, <||>][0],
- testSystemImplementation[TestSystem[], $defaultEventSelection, #, {}, $defaultStoppingConditions, 0]] & /@
- {None, All},
-
- testUnevaluated[
- GenerateMultihistory[TestSystem[], <||>, None, 3, 4][0], GenerateMultihistory::invalidEventOrdering],
- testUnevaluated[
- GenerateMultihistory[TestSystem[], <||>, None, {"Unknown"}, 4][0], GenerateMultihistory::invalidEventOrdering],
- VerificationTest[
- GenerateMultihistory[TestSystem[], <||>, None, {"RuleOrdering", "InputCount"}, <||>][0],
- testSystemImplementation[
- TestSystem[], $defaultEventSelection, None, {"RuleOrdering", "InputCount"}, $defaultStoppingConditions, 0]],
-
- testUnevaluated[
- GenerateMultihistory[TestSystem[], <||>, None, {}, 4][0], GenerateMultihistory::invalidStoppingCondition],
- testUnevaluated[GenerateMultihistory[TestSystem[], <||>, None, {}, <|"Unknown" -> 0|>][0],
- GenerateMultihistory::invalidStoppingCondition],
- testUnevaluated[GenerateMultihistory[TestSystem[], <||>, None, {}, <|"MaxEvents" -> -1|>][0],
- GenerateMultihistory::notNonNegativeIntegerOrInfinityParameter],
- VerificationTest[
- GenerateMultihistory[TestSystem[], <||>, None, {}, #][0],
- testSystemImplementation[
- TestSystem[],
- $defaultEventSelection,
- None,
- {},
- <|"MaxEvents" -> 2, "MinCausalDensityDimension" -> 2., "TokenEventGraphTest" -> (True &)|>,
- 0]] & /@ {<|"MaxEvents" -> 2|>, {"MaxEvents" -> 2}, "MaxEvents" -> 2},
-
- (* Introspection *)
- VerificationTest[$SetReplaceSystems, Sort @ Join[$originalSetReplaceSystem, {TestSystem}]],
-
- Function[{introspectionFunction}, {
- testUnevaluated[introspectionFunction[], introspectionFunction::argx],
- testUnevaluated[introspectionFunction[0, 1], introspectionFunction::argx],
- testUnevaluated[introspectionFunction[#], introspectionFunction::unknownSystem] & /@
- {0, UnknownSystem, TestSystem[][]}
- }] /@ {EventSelectionParameters, EventOrderingFunctions, StoppingConditionParameters},
-
- VerificationTest[EventSelectionParameters[#], {"MaxGeneration", "MinEventInputs", "EventPattern"}] & /@
- {TestSystem, TestSystem[1, 2]},
- VerificationTest[EventOrderingFunctions[#], {"InputCount", "RuleOrdering"}] & /@ {TestSystem, TestSystem[1, 2]},
- VerificationTest[
- StoppingConditionParameters[#], {"MaxEvents", "MinCausalDensityDimension", "TokenEventGraphTest"}] & /@
- {TestSystem, TestSystem[1, 2]}
- }
- |>
-|>
diff --git a/Tests/MultisetSubstitutionSystem.wlt b/Tests/MultisetSubstitutionSystem.wlt
index 2899b098..10f57300 100644
--- a/Tests/MultisetSubstitutionSystem.wlt
+++ b/Tests/MultisetSubstitutionSystem.wlt
@@ -6,47 +6,24 @@
Global`testSymbolLeak[args___] := SetReplace`PackageScope`testSymbolLeak[VerificationTest, args];
),
"tests" -> {
- With[{anEventOrdering = EventOrderingFunctions[MultisetSubstitutionSystem]}, {
- (* Symbol Leak *)
- testSymbolLeak[
- GenerateMultihistory[
- MultisetSubstitutionSystem[{a_, b_} :> {a + b}], {}, None, anEventOrdering, <||>] @ {1, 2, 3}],
+ (* Symbol Leak *)
+ testSymbolLeak[GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}]] @ {1, 2, 3}],
- (* Rules *)
- testUnevaluated[
- GenerateMultihistory[MultisetSubstitutionSystem[##2], <||>, None, anEventOrdering, <||>] @ {1}, {#}] & @@@
- {{MultisetSubstitutionSystem::argx},
- {MultisetSubstitutionSystem::argx, 1, 2},
- {GenerateMultihistory::invalidMultisetRules, 1},
- {GenerateMultihistory::ruleOutputNotList, {1} -> 2}},
+ (* Rules *)
+ testUnevaluated[GenerateMultihistory[MultisetSubstitutionSystem[##2]][{1}], {#}] & @@@ {
+ {MultisetSubstitutionSystem::argx},
+ {MultisetSubstitutionSystem::argx, 1, 2},
+ {GenerateMultihistory::invalidMultisetRules, 1},
+ {GenerateMultihistory::ruleOutputNotList, {1} -> 2}},
- (* Init *)
- testUnevaluated[
- GenerateMultihistory[MultisetSubstitutionSystem[{1} -> {2}], <||>, None, anEventOrdering, <||>] @ 1,
- {GenerateMultihistory::multisetInitNotList}],
+ (* Init *)
+ testUnevaluated[
+ GenerateMultihistory[MultisetSubstitutionSystem[{1} -> {2}]][1], {GenerateMultihistory::argNotInit}],
- (* Ordering not yet supported *)
- testUnevaluated[
- GenerateMultihistory[MultisetSubstitutionSystem[{1} -> {2}], {}, None, {"RuleIndex"}, {}] @ {1},
- {GenerateMultihistory::multisetEventOrderingNotImplemented}],
-
- (* Token deduplication not yet supported *)
- testUnevaluated[
- GenerateMultihistory[MultisetSubstitutionSystem[{1} -> {2}], {}, All, anEventOrdering, {}] @ {1},
- {GenerateMultihistory::multisetTokenDeduplicationNotImplemented}],
-
- (* Parameters *)
- testUnevaluated[
- GenerateMultihistory[MultisetSubstitutionSystem[{1} -> {2}], <|# -> -1|>, None, anEventOrdering, <||>] @
- {1},
- {GenerateMultihistory::notNonNegativeIntegerOrInfinityParameter}] & /@
- {"MaxGeneration", "MaxDestroyerEvents", "MinEventInputs", "MaxEventInputs"},
-
- testUnevaluated[
- GenerateMultihistory[
- MultisetSubstitutionSystem[{1} -> {2}], <||>, None, anEventOrdering, <|"MaxEvents" -> -1|>] @ {1},
- {GenerateMultihistory::notNonNegativeIntegerOrInfinityParameter}]
- }]
+ (* Parameters *)
+ testUnevaluated[GenerateMultihistory[MultisetSubstitutionSystem[{1} -> {2}], # -> -1] @ {1},
+ {GenerateMultihistory::invalidParameter}] & /@
+ {MaxGeneration, MaxDestroyerEvents, MinEventInputs, MaxEventInputs, MaxEvents}
}
|>,
"Multiset System Rules" -> <|
@@ -61,195 +38,155 @@
lastEventGeneration[Multihistory[_, data_]] := data["EventGenerations"]["Part", -1];
),
"tests" -> {
- With[{anEventOrdering = EventOrderingFunctions[MultisetSubstitutionSystem]}, {
- Function[{rules, selection, stopping, init, expectedCreatedExpressions},
- VerificationTest[
- allExpressions @ GenerateMultihistory[
- MultisetSubstitutionSystem[rules], selection, None, anEventOrdering, stopping] @ init,
- Join[init, expectedCreatedExpressions],
- SameTest -> MatchQ]] @@@
- {{{1} -> {1}, <||>, <|"MaxEvents" -> 1|>, {1}, {1}},
- (* 1 does not match any lists, {1} should be used for matching a single 1 as an expression *)
- {1 -> 2, <||>, <|"MaxEvents" -> 1|>, {1, 2, 3}, {}},
- {{1} -> {2}, <|"MaxGeneration" -> 1|>, <||>, {1, 2, 3}, {2}},
- {{2} -> {5}, <|"MaxGeneration" -> 1|>, <||>, {1, 2, 3}, {5}},
- {{2} :> {5}, <|"MaxGeneration" -> 1|>, <||>, {1, 2, 3}, {5}},
- {{{2} :> {5}, {3} :> {6}}, <|"MaxGeneration" -> 1|>, <||>, {1, 2, 3}, {5, 6}},
- {{{2} -> {5}, {3} :> {6}}, <|"MaxGeneration" -> 1|>, <||>, {1, 2, 3}, {5, 6}},
- {{{2} -> {5}, {3} :> {6}}, <|"MaxGeneration" -> 2|>, <||>, {1, 2, 3}, {5, 6}},
- {{3, 2} -> {5}, <|"MaxGeneration" -> 1|>, <||>, {1, 2, 3}, {5}},
- {{4} -> {5}, <|"MaxGeneration" -> 1|>, <||>, {1, 2, 3}, {}},
- {{{1}} :> {}, <|"MaxGeneration" -> 1|>, <||>, {{1}}, {}},
- {{{1}, {2}} :> {{3}}, <|"MaxGeneration" -> 1|>, <||>, {{1}, {2}}, {{3}}},
- {{{1}, {2}} :> {{3}}, <|"MaxGeneration" -> 1|>, <||>, {{2}, {1}}, {{3}}},
- {{x_List ? (Length[#] == 3 &), y_List ? (Length[#] == 6 &)} :> {x, y, Join[x, y]},
- <|"MaxGeneration" -> 2, "MaxEventInputs" -> 6|>,
- <||>,
- {"This" -> "that", {2, 3, 4}, {2, 5}, {1, 2, 3, 4, 5, 6}},
- {{2, 3, 4}, {1, 2, 3, 4, 5, 6}, {2, 3, 4, 1, 2, 3, 4, 5, 6}, {2, 3, 4}, {1, 2, 3, 4, 5, 6},
- {2, 3, 4, 1, 2, 3, 4, 5, 6}}},
- {{x_List /; (Length[x] == 3), y_List /; (Length[y] == 6)} :> {x, y, Join[x, y]},
- <|"MaxGeneration" -> 2, "MaxEventInputs" -> 6|>,
- <||>,
- {"This" -> "that", {2, 3, 4}, {2, 5}, {1, 2, 3, 4, 5, 6}},
- {{2, 3, 4}, {1, 2, 3, 4, 5, 6}, {2, 3, 4, 1, 2, 3, 4, 5, 6}, {2, 3, 4}, {1, 2, 3, 4, 5, 6},
- {2, 3, 4, 1, 2, 3, 4, 5, 6}}},
- {{{a_, b_}, {b_, c_}} :> {{a, c}},
- <|"MaxGeneration" -> 3, "MaxDestroyerEvents" -> 1|>,
- <||>,
- {{1, 2}, {2, 3}, {3, 4}, {4, 5}},
- {{1, 3}, {3, 5}, {1, 5}}},
- {{{a_, b_}, {b_, c_}} :> {{a, c}},
- "MaxGeneration" -> 2,
- <||>,
- {{1, 2}, {2, 3}, {3, 4}, {4, 5}},
- {{1, 3}, {2, 4}, {1, 4}, {3, 5}, {2, 5}, {1, 4}, {2, 5}, {1, 5}}},
- {{{a_, b_}, {b_, c_}} :> {{a, c}},
- "MaxGeneration" -> 1,
- <||>,
- {{1, 2}, {2, 3}, {3, 4}, {4, 5}},
- {{1, 3}, {2, 4}, {3, 5}}},
- {{{_}} -> {}, <||>, <||>, {{1}, {2}, {3}, {4}, {5}}, {}},
- {{{1}} -> {{2}}, <|"MaxGeneration" -> 1|>, <||>, {{1}, {1}, {1}}, {{2}, {2}, {2}}},
- {{{v[a1_], v[a2_]}, {v[a2_], v[a3_]}} :> {{v[a1], v[a3]}},
- <|"MaxGeneration" -> 1|>,
- <||>,
- {{v[1], v[2]}, {v[2], v[3]}},
- {{v[1], v[3]}}},
- (* Nested lists of vertices *)
- {ToPatternRules[{{2, 2, 1}, {2, 2, 2}} -> {{1, 1, 3}, {1, 1, 1}, {2, 1, 2}, {3, 3, 2}}],
- <|"MaxGeneration" -> 2|>,
- <||>,
- {Table[{0, 0, 0}, 3]},
- {}},
- {ToPatternRules[{{2, 2, 1}, {2, 2, 2}} -> {{1, 1, 3}, {1, 1, 1}, {2, 1, 2}, {3, 3, 2}}],
- <|"MaxGeneration" -> 1|>,
- <||>,
- {{{2}, {2}, 1}, {{2}, {2}, {2}}},
- {{1, 1, v2_ ? AtomQ}, {1, 1, 1}, {{2}, 1, {2}}, {v2_ ? AtomQ, v2_ ? AtomQ, {2}}}},
- {ToPatternRules[{{1, 1, 1}} -> {{1, 1, 1, 1}}],
- <|"MaxGeneration" -> 2|>,
- <||>,
- {Table[{0, 0, 0}, 3]},
- {ConstantArray[{0, 0, 0}, 4]}},
- (* Potential variable collision between different rule inputs and outputs *)
- {ToPatternRules[{{{1, 1}, {2, 3}} -> {{2, 1}, {2, 2}, {2, 3}, {4, 2}}, {{1, 2}, {1, 2}} -> {{3, 2}}}],
- <||>,
- <|"MaxEvents" -> 1|>,
- {{1, 0}, {6, 1}, {1, 0}, {1, 1}, {1, 0}, {7, 1}, {3, 0}, {3, 3}, {3, 1}, {8, 3}, {4, 0}, {4, 4}, {4, 0},
- {9, 4}, {2, 2}, {2, 2}, {2, 0}, {10, 2}, {2, 1}, {2, 2}, {2, 0}, {11, 2}, {5, 1}, {5, 5}, {5, 2}, {12, 5}},
- {{_Symbol, 0}}},
- {ToPatternRules[{{1, 2} -> {}, {1} -> {2}}], <||>, <|"MaxEvents" -> 1|>, {{1}}, {_ ? AtomQ}},
- {{0} :> {1, 2}, <||>, <|"MaxEvents" -> 2|>, {0}, {1, 2}},
- (* there is only one created expression because the empty set {} can only be matched once *)
- {{} :> {0}, <||>, <|"MaxEvents" -> 2|>, {}, {0}},
- {{x_, y_} /; OddQ[x + y] :> {x + y}, "MaxDestroyerEvents" -> 1, {}, Range[10], {3, 7, 11, 15, 19}},
- {{x_, y_} /; Mod[x + y, 2] == 0 :> {x + y},
- "MaxDestroyerEvents" -> 1,
- <||>,
- Range[10],
- {4, 6, 12, 14, 14, 18, 28, 46}},
- {{x_, y_} /; x >= 8 :> {x - 8, y + 8},
- <|"MaxGeneration" -> 20, "MaxDestroyerEvents" -> 1|>,
- <||>,
- Range[10],
- {__ ? (Not @* Negative)}},
- {{x___} /; Plus[x] == 5 && OrderedQ[{x}] :> {Length[{x}]},
- <|"MaxGeneration" -> 2|>,
- <||>,
- {1, 2, 3, 5},
- {1, 2, 3, 3}},
- {{x_, y_} /; x < y :> Module[{z = Hash[{x, y}]}, {z}],
- "MaxDestroyerEvents" -> 1,
- <||>,
- {1, 2, 3, 4},
- {Hash[{1, 2}], Hash[{3, 4}], Hash[Sort @ {Hash[{1, 2}], Hash[{3, 4}]}]}},
- {{x__, y__} /; OrderedQ[Catenate[{x, y}]] :> {Catenate[{x, y}]},
- <|"MaxEventInputs" -> 3|>,
- <||>,
- {{1}, {2}, {3}},
- {{1, 2}, {1, 3}, {2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}}},
- {{a__, b__} /; OrderedQ[{a, b}] :> {{a}, {b}},
- <|"MaxGeneration" -> 1, "MinEventInputs" -> 3, "MaxEventInputs" -> 3|>,
- <||>,
- {1, 2, 3},
- {{1}, {2, 3}, {1, 2}, {3}}},
- {{Longest[a__], b__} /; OrderedQ[{a, b}] :> {{a}, {b}},
- <|"MaxGeneration" -> 1, "MinEventInputs" -> 3, "MaxEventInputs" -> 3|>,
- <||>,
- {1, 2, 3},
- {{1, 2}, {3}, {1}, {2, 3}}},
- {{a_Integer, b_Integer} :> {a + b}, "MaxGeneration" -> 1, {}, {1, 2, 3, "x"}, {3, 3, 4, 4, 5, 5}},
- {{PatternSequence[a_, b_], c_} :> {a + b + c}, "MaxGeneration" -> 1, {}, {1, 2, 3}, ConstantArray[6, 6]},
- {{x_ | Repeated[x_, {2}]} :> {2 x}, "MaxGeneration" -> 1, {}, {1, 1, 1}, ConstantArray[2, 9]},
- {{Alternatives[]} :> {}, {}, {}, Range[1000], {}}, (* empty Alternatives does not match to anything *)
- {{x_ ..} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1}, ConstantArray[2, 4]},
- {{Repeated[x_, 2]} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1, 1}, ConstantArray[2, 9]},
- {{Repeated[x_, {2, 3}]} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1, 1}, ConstantArray[2, 12]},
- {{Repeated[x_, {2}]} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1, 1}, ConstantArray[2, 6]},
- {{x_ ...} :> {x + 1}, "MaxGeneration" -> 1, {}, {1}, {1, 2, 2, 2, 2}},
- {{RepeatedNull[x_, 2]} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1}, Join[{1}, ConstantArray[2, 9]]},
- {{RepeatedNull[x_, {2, 3}]} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1, 1}, ConstantArray[2, 12]},
- {{RepeatedNull[x_, {2}]} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1, 1}, ConstantArray[2, 6]},
- {{x : Except[1]} :> {x + 1}, "MaxGeneration" -> 1, {}, Range[10], Range[3, 11]},
- {{x : Except[1, _Integer]} :> {x + 1}, "MaxGeneration" -> 1, {}, {0, 1, "x"}, {1}},
- {{Longest[Repeated[x_, {2}]]} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1, 1}, ConstantArray[2, 6]},
- {{Shortest[Repeated[x_, {2}]]} :> {x + 1}, "MaxGeneration" -> 1, {}, {1, 1, 1}, ConstantArray[2, 6]},
- {{OptionsPattern[]} :> {3}, "MaxGeneration" -> 1, {}, {1, {"test" -> 2}}, {3, 3}},
- {{OptionsPattern[], OptionsPattern[]} :> {3}, "MaxGeneration" -> 1, {}, {1, {"test" -> 2}}, {3, 3, 3}},
- {{PatternSequence[x_, y_]} :> {x + y}, "MaxGeneration" -> 1, {}, {1, 2, 3}, {3, 3, 4, 4, 5, 5}},
- {{Verbatim[_], Verbatim[_]} :> {4}, "MaxGeneration" -> 1, {}, {_, _, 2, 3}, {4, 4}},
- {{HoldPattern[_[x_ + x_]]} :> {0},
- "MaxGeneration" -> 1,
- {},
- {Hold[2 + 2], Hold[2 + 3], Hold[x + x]},
- {0, 0, 0, 0}},
- {{OrderlessPatternSequence[x_, y_]} :> {x + y},
- "MaxGeneration" -> 1,
- {},
- {1, 2, 3},
- Catenate[ConstantArray[#, 4] & /@ {3, 4, 5}]},
- {{KeyValuePattern[{}]} :> {3}, "MaxGeneration" -> 1, {}, {1, {}, {"test" -> 2}}, {3, 3}},
- {{a_ /; OddQ[a], (b_ ? EvenQ)} :> {a + b}, "MaxGeneration" -> 1, {}, {1, 2, 3, 4}, {3, 5, 5, 7}},
- {{a_ : 0} :> {a}, "MaxGeneration" -> 1, {}, {1, 2, 3, 4}, {0, 1, 2, 3, 4, 0}},
- {HoldPattern @ {_[x_ + x_]} :> {0},
- "MaxGeneration" -> 1,
- {},
- {Hold[2 + 2], Hold[2 + 3], Hold[x + x]},
- {0, 0, 0, 0}},
- {{x_} | HoldPattern[{x_, y_}] :> {x + y}, "MaxGeneration" -> 1, {}, {1, 2, 3}, {1, 2, 3, 3, 3, 4, 4, 5, 5}},
- {{a_, b_} /; OddQ[a] && EvenQ[b] :> {a + b}, "MaxGeneration" -> 1, {}, {1, 2, 3, 4}, {3, 5, 5, 7}},
- {x : {_, _} :> {Total[x]}, "MaxGeneration" -> 1, {}, {1, 2, 3, 4}, {3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7}},
- {Except[{1, 2}, {a_, b_}] /; a < b :> {a + b}, "MaxGeneration" -> 1, {}, {1, 2, 3, 4}, {4, 5, 5, 6, 7}},
- {Verbatim[{_, __}] :> {0}, "MaxGeneration" -> 1, {}, {_, __, 1}, {0}}},
+ Function[{rules, parameters, init, expectedCreatedExpressions},
+ VerificationTest[allExpressions @ GenerateMultihistory[MultisetSubstitutionSystem[rules], parameters] @ init,
+ Join[init, expectedCreatedExpressions],
+ SameTest -> MatchQ]] @@@
+ {{{1} -> {1}, MaxEvents -> 1, {1}, {1}},
+ (* 1 does not match any lists, {1} should be used for matching a single 1 as an expression *)
+ {1 -> 2, MaxEvents -> 1, {1, 2, 3}, {}},
+ {{1} -> {2}, MaxGeneration -> 1, {1, 2, 3}, {2}},
+ {{2} -> {5}, MaxGeneration -> 1, {1, 2, 3}, {5}},
+ {{2} :> {5}, MaxGeneration -> 1, {1, 2, 3}, {5}},
+ {{{2} :> {5}, {3} :> {6}}, MaxGeneration -> 1, {1, 2, 3}, {5, 6}},
+ {{{2} -> {5}, {3} :> {6}}, MaxGeneration -> 1, {1, 2, 3}, {5, 6}},
+ {{{2} -> {5}, {3} :> {6}}, MaxGeneration -> 2, {1, 2, 3}, {5, 6}},
+ {{3, 2} -> {5}, MaxGeneration -> 1, {1, 2, 3}, {5}},
+ {{4} -> {5}, MaxGeneration -> 1, {1, 2, 3}, {}},
+ {{{1}} :> {}, MaxGeneration -> 1, {{1}}, {}},
+ {{{1}, {2}} :> {{3}}, MaxGeneration -> 1, {{1}, {2}}, {{3}}},
+ {{{1}, {2}} :> {{3}}, MaxGeneration -> 1, {{2}, {1}}, {{3}}},
+ {{x_List ? (Length[#] == 3 &), y_List ? (Length[#] == 6 &)} :> {x, y, Join[x, y]},
+ {MaxGeneration -> 2, MaxEventInputs -> 6},
+ {"This" -> "that", {2, 3, 4}, {2, 5}, {1, 2, 3, 4, 5, 6}},
+ {{2, 3, 4}, {1, 2, 3, 4, 5, 6}, {2, 3, 4, 1, 2, 3, 4, 5, 6}, {2, 3, 4}, {1, 2, 3, 4, 5, 6},
+ {2, 3, 4, 1, 2, 3, 4, 5, 6}}},
+ {{x_List /; (Length[x] == 3), y_List /; (Length[y] == 6)} :> {x, y, Join[x, y]},
+ {MaxGeneration -> 2, MaxEventInputs -> 6},
+ {"This" -> "that", {2, 3, 4}, {2, 5}, {1, 2, 3, 4, 5, 6}},
+ {{2, 3, 4}, {1, 2, 3, 4, 5, 6}, {2, 3, 4, 1, 2, 3, 4, 5, 6}, {2, 3, 4}, {1, 2, 3, 4, 5, 6},
+ {2, 3, 4, 1, 2, 3, 4, 5, 6}}},
+ {{{a_, b_}, {b_, c_}} :> {{a, c}},
+ {MaxGeneration -> 3, MaxDestroyerEvents -> 1},
+ {{1, 2}, {2, 3}, {3, 4}, {4, 5}},
+ {{1, 3}, {3, 5}, {1, 5}}},
+ {{{a_, b_}, {b_, c_}} :> {{a, c}},
+ MaxGeneration -> 2,
+ {{1, 2}, {2, 3}, {3, 4}, {4, 5}},
+ {{1, 3}, {2, 4}, {1, 4}, {3, 5}, {2, 5}, {1, 4}, {2, 5}, {1, 5}}},
+ {{{a_, b_}, {b_, c_}} :> {{a, c}},
+ MaxGeneration -> 1,
+ {{1, 2}, {2, 3}, {3, 4}, {4, 5}},
+ {{1, 3}, {2, 4}, {3, 5}}},
+ {{{_}} -> {}, {}, {{1}, {2}, {3}, {4}, {5}}, {}},
+ {{{1}} -> {{2}}, MaxGeneration -> 1, {{1}, {1}, {1}}, {{2}, {2}, {2}}},
+ {{{v[a1_], v[a2_]}, {v[a2_], v[a3_]}} :> {{v[a1], v[a3]}},
+ MaxGeneration -> 1,
+ {{v[1], v[2]}, {v[2], v[3]}},
+ {{v[1], v[3]}}},
+ (* Nested lists of vertices *)
+ {ToPatternRules[{{2, 2, 1}, {2, 2, 2}} -> {{1, 1, 3}, {1, 1, 1}, {2, 1, 2}, {3, 3, 2}}],
+ MaxGeneration -> 2,
+ {Table[{0, 0, 0}, 3]},
+ {}},
+ {ToPatternRules[{{2, 2, 1}, {2, 2, 2}} -> {{1, 1, 3}, {1, 1, 1}, {2, 1, 2}, {3, 3, 2}}],
+ MaxGeneration -> 1,
+ {{{2}, {2}, 1}, {{2}, {2}, {2}}},
+ {{1, 1, v2_ ? AtomQ}, {1, 1, 1}, {{2}, 1, {2}}, {v2_ ? AtomQ, v2_ ? AtomQ, {2}}}},
+ {ToPatternRules[{{1, 1, 1}} -> {{1, 1, 1, 1}}],
+ MaxGeneration -> 2,
+ {Table[{0, 0, 0}, 3]},
+ {ConstantArray[{0, 0, 0}, 4]}},
+ (* Potential variable collision between different rule inputs and outputs *)
+ {ToPatternRules[{{{1, 1}, {2, 3}} -> {{2, 1}, {2, 2}, {2, 3}, {4, 2}}, {{1, 2}, {1, 2}} -> {{3, 2}}}],
+ MaxEvents -> 1,
+ {{1, 0}, {6, 1}, {1, 0}, {1, 1}, {1, 0}, {7, 1}, {3, 0}, {3, 3}, {3, 1}, {8, 3}, {4, 0}, {4, 4}, {4, 0},
+ {9, 4}, {2, 2}, {2, 2}, {2, 0}, {10, 2}, {2, 1}, {2, 2}, {2, 0}, {11, 2}, {5, 1}, {5, 5}, {5, 2}, {12, 5}},
+ {{_Symbol, 0}}},
+ {ToPatternRules[{{1, 2} -> {}, {1} -> {2}}], MaxEvents -> 1, {{1}}, {_ ? AtomQ}},
+ {{0} :> {1, 2}, MaxEvents -> 2, {0}, {1, 2}},
+ (* there is only one created expression because the empty set {} can only be matched once *)
+ {{} :> {0}, MaxEvents -> 2, {}, {0}},
+ {{x_, y_} /; OddQ[x + y] :> {x + y}, MaxDestroyerEvents -> 1, Range[10], {3, 7, 11, 15, 19}},
+ {{x_, y_} /; Mod[x + y, 2] == 0 :> {x + y},
+ MaxDestroyerEvents -> 1,
+ Range[10],
+ {4, 6, 12, 14, 14, 18, 28, 46}},
+ {{x_, y_} /; x >= 8 :> {x - 8, y + 8},
+ {MaxGeneration -> 20, MaxDestroyerEvents -> 1},
+ Range[10],
+ {__ ? (Not @* Negative)}},
+ {{x___} /; Plus[x] == 5 && OrderedQ[{x}] :> {Length[{x}]}, MaxGeneration -> 2, {1, 2, 3, 5}, {1, 2, 3, 3}},
+ {{x_, y_} /; x < y :> Module[{z = Hash[{x, y}]}, {z}],
+ MaxDestroyerEvents -> 1,
+ {1, 2, 3, 4},
+ {Hash[{1, 2}], Hash[{3, 4}], Hash[Sort @ {Hash[{1, 2}], Hash[{3, 4}]}]}},
+ {{x__, y__} /; OrderedQ[Catenate[{x, y}]] :> {Catenate[{x, y}]},
+ MaxEventInputs -> 3,
+ {{1}, {2}, {3}},
+ {{1, 2}, {1, 3}, {2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}}},
+ {{a__, b__} /; OrderedQ[{a, b}] :> {{a}, {b}},
+ {MaxGeneration -> 1, MinEventInputs -> 3, MaxEventInputs -> 3},
+ {1, 2, 3},
+ {{1}, {2, 3}, {1, 2}, {3}}},
+ {{Longest[a__], b__} /; OrderedQ[{a, b}] :> {{a}, {b}},
+ {MaxGeneration -> 1, MinEventInputs -> 3, MaxEventInputs -> 3},
+ {1, 2, 3},
+ {{1, 2}, {3}, {1}, {2, 3}}},
+ {{a_Integer, b_Integer} :> {a + b}, MaxGeneration -> 1, {1, 2, 3, "x"}, {3, 3, 4, 4, 5, 5}},
+ {{PatternSequence[a_, b_], c_} :> {a + b + c}, MaxGeneration -> 1, {1, 2, 3}, ConstantArray[6, 6]},
+ {{x_ | Repeated[x_, {2}]} :> {2 x}, MaxGeneration -> 1, {1, 1, 1}, ConstantArray[2, 9]},
+ {{Alternatives[]} :> {}, {}, Range[1000], {}}, (* empty Alternatives does not match to anything *)
+ {{x_ ..} :> {x + 1}, MaxGeneration -> 1, {1, 1}, ConstantArray[2, 4]},
+ {{Repeated[x_, 2]} :> {x + 1}, MaxGeneration -> 1, {1, 1, 1}, ConstantArray[2, 9]},
+ {{Repeated[x_, {2, 3}]} :> {x + 1}, MaxGeneration -> 1, {1, 1, 1}, ConstantArray[2, 12]},
+ {{Repeated[x_, {2}]} :> {x + 1}, MaxGeneration -> 1, {1, 1, 1}, ConstantArray[2, 6]},
+ {{x_ ...} :> {x + 1}, MaxGeneration -> 1, {1}, {1, 2, 2, 2, 2}},
+ {{RepeatedNull[x_, 2]} :> {x + 1}, MaxGeneration -> 1, {1, 1}, Join[{1}, ConstantArray[2, 9]]},
+ {{RepeatedNull[x_, {2, 3}]} :> {x + 1}, MaxGeneration -> 1, {1, 1, 1}, ConstantArray[2, 12]},
+ {{RepeatedNull[x_, {2}]} :> {x + 1}, MaxGeneration -> 1, {1, 1, 1}, ConstantArray[2, 6]},
+ {{x : Except[1]} :> {x + 1}, MaxGeneration -> 1, Range[10], Range[3, 11]},
+ {{x : Except[1, _Integer]} :> {x + 1}, MaxGeneration -> 1, {0, 1, "x"}, {1}},
+ {{Longest[Repeated[x_, {2}]]} :> {x + 1}, MaxGeneration -> 1, {1, 1, 1}, ConstantArray[2, 6]},
+ {{Shortest[Repeated[x_, {2}]]} :> {x + 1}, MaxGeneration -> 1, {1, 1, 1}, ConstantArray[2, 6]},
+ {{OptionsPattern[]} :> {3}, MaxGeneration -> 1, {1, {"test" -> 2}}, {3, 3}},
+ {{OptionsPattern[], OptionsPattern[]} :> {3}, MaxGeneration -> 1, {1, {"test" -> 2}}, {3, 3, 3}},
+ {{PatternSequence[x_, y_]} :> {x + y}, MaxGeneration -> 1, {1, 2, 3}, {3, 3, 4, 4, 5, 5}},
+ {{Verbatim[_], Verbatim[_]} :> {4}, MaxGeneration -> 1, {_, _, 2, 3}, {4, 4}},
+ {{HoldPattern[_[x_ + x_]]} :> {0}, MaxGeneration -> 1, {Hold[2 + 2], Hold[2 + 3], Hold[x + x]}, {0, 0, 0, 0}},
+ {{OrderlessPatternSequence[x_, y_]} :> {x + y},
+ MaxGeneration -> 1,
+ {1, 2, 3},
+ Catenate[ConstantArray[#, 4] & /@ {3, 4, 5}]},
+ {{KeyValuePattern[{}]} :> {3}, MaxGeneration -> 1, {1, {}, {"test" -> 2}}, {3, 3}},
+ {{a_ /; OddQ[a], (b_ ? EvenQ)} :> {a + b}, MaxGeneration -> 1, {1, 2, 3, 4}, {3, 5, 5, 7}},
+ {{a_ : 0} :> {a}, MaxGeneration -> 1, {1, 2, 3, 4}, {0, 1, 2, 3, 4, 0}},
+ {HoldPattern @ {_[x_ + x_]} :> {0}, MaxGeneration -> 1, {Hold[2 + 2], Hold[2 + 3], Hold[x + x]}, {0, 0, 0, 0}},
+ {{x_} | HoldPattern[{x_, y_}] :> {x + y}, MaxGeneration -> 1, {1, 2, 3}, {1, 2, 3, 3, 3, 4, 4, 5, 5}},
+ {{a_, b_} /; OddQ[a] && EvenQ[b] :> {a + b}, MaxGeneration -> 1, {1, 2, 3, 4}, {3, 5, 5, 7}},
+ {x : {_, _} :> {Total[x]}, MaxGeneration -> 1, {1, 2, 3, 4}, {3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7}},
+ {Except[{1, 2}, {a_, b_}] /; a < b :> {a + b}, MaxGeneration -> 1, {1, 2, 3, 4}, {4, 5, 5, 6, 7}},
+ {Verbatim[{_, __}] :> {0}, MaxGeneration -> 1, {_, __, 1}, {0}}},
- VerificationTest[
- eventCount @ GenerateMultihistory[
- MultisetSubstitutionSystem[{{1}} :> {}], <|"MaxGeneration" -> 1|>, None, anEventOrdering, <||>] @ {{1}},
- 1],
+ VerificationTest[
+ eventCount @ GenerateMultihistory[MultisetSubstitutionSystem[{{1}} :> {}], MaxGeneration -> 1] @ {{1}}, 1],
- Function[{rules, selection, init, expectedMaxGeneration, expectedEventCount},
- VerificationTest[
- Through[{lastEventGeneration, eventCount} @ GenerateMultihistory[
- MultisetSubstitutionSystem[rules], selection, None, anEventOrdering, <||>] @ init],
- {expectedMaxGeneration, expectedEventCount}]] @@@
- {{{{a_, b_}} :> Module[{c}, {{a, c}, {c, b}}], <|"MaxGeneration" -> 7|>, {{1, 2}}, 7, 2^7 - 1},
- {{{_}} :> {}, <||>, {{1}, {2}, {3}, {4}, {5}}, 1, 5},
- {{{x_}} :> {{x}}, <|"MaxGeneration" -> 0|>, {{1}, {2}, {3}, {4}, {5}}, 0, 0},
- {{{{_}} :> {}, {{x_, _}} :> {{x}}}, <|"MaxGeneration" -> 2|>, {{1, 2}, {2}, {3}, {4}, {5}}, 2, 6}},
+ Function[{rules, parameters, init, expectedMaxGeneration, expectedEventCount},
+ VerificationTest[
+ Through[{lastEventGeneration, eventCount} @
+ GenerateMultihistory[MultisetSubstitutionSystem[rules], parameters] @ init],
+ {expectedMaxGeneration, expectedEventCount}]] @@@
+ {{{{a_, b_}} :> Module[{c}, {{a, c}, {c, b}}], MaxGeneration -> 7, {{1, 2}}, 7, 2^7 - 1},
+ {{{_}} :> {}, {}, {{1}, {2}, {3}, {4}, {5}}, 1, 5},
+ {{{x_}} :> {{x}}, MaxGeneration -> 0, {{1}, {2}, {3}, {4}, {5}}, 0, 0},
+ {{{{_}} :> {}, {{x_, _}} :> {{x}}}, MaxGeneration -> 2, {{1, 2}, {2}, {3}, {4}, {5}}, 2, 6}},
- (* Test invalid patterns *)
- testUnevaluated[
- eventCount @ GenerateMultihistory[
- MultisetSubstitutionSystem[#],
- <|"MaxGeneration" -> 1|>,
- None,
- anEventOrdering,
- <||>] @ {{1}},
- {Pattern::patvar, GenerateMultihistory::ruleInstantiationMessage}] & /@
- {{{{Pattern[1, _], v2_}} :> {}, {{Pattern[2, _], v1_}} :> Module[{v2}, {v2}]},
- {{{Pattern[Pattern[a, _], _], v2_}} :> {}, {{Pattern[2, _], v1_}} :> Module[{v2}, {v2}]}}
- }]
+ (* Test invalid patterns *)
+ testUnevaluated[
+ eventCount @ GenerateMultihistory[MultisetSubstitutionSystem[#], MaxGeneration -> 1] @ {{1}},
+ {Pattern::patvar, GenerateMultihistory::ruleInstantiationMessage}] & /@
+ {{{{Pattern[1, _], v2_}} :> {}, {{Pattern[2, _], v1_}} :> Module[{v2}, {v2}]},
+ {{{Pattern[Pattern[a, _], _], v2_}} :> {}, {{Pattern[2, _], v1_}} :> Module[{v2}, {v2}]}}
}
|>,
"Multiset System Matching" -> <|
@@ -262,80 +199,68 @@
allExpressions[Multihistory[_, data_]] := Normal @ data["Expressions"];
),
"tests" -> {
- With[{anEventOrdering = EventOrderingFunctions[MultisetSubstitutionSystem]}, {
- Function[{rule, selection, init, expectedCreatedExpressions},
- VerificationTest[
- allExpressions @ GenerateMultihistory[
- MultisetSubstitutionSystem[rule],
- selection,
- None,
- anEventOrdering,
- <||>] @ init,
- Join[init, expectedCreatedExpressions]]
- ] @@@ {
- (* multihistory branching *)
- {{{1} -> {2}, {1} -> {3}}, <||>, {1}, {2, 3}},
- {{{1} -> {2}, {1} -> {3}}, <|"MaxDestroyerEvents" -> 1|>, {1}, {2}},
- (* no branchlike-expressions matching *)
- {{{1} -> {2}, {1} -> {3}, {2, 3} -> {4}}, <||>, {1}, {2, 3}},
- (* no timelike-expressions matching *)
- {{{1} -> {2}, {2} -> {3}, {2, 3} -> {4}}, <||>, {1}, {2, 3}},
- (* spacelike-expressions matching *)
- {{{1} -> {2, 3}, {2, 3} -> {4}}, <||>, {1}, {2, 3, 4}},
- (* no instantiating the same match multiple times *)
- {{1} -> {2}, <||>, {1}, {2}},
- (* no matching rules that don't match *)
- {{a_, a_} :> {0}, <||>, {1, 2}, {}},
- (* no mixed spacelike/branchlike matching *)
- {{{{v, i}} -> {{v, 1}, {v, 2}},
- {{v, 1}} -> {{v, 1, 1}, {v, 1, 2}},
- {{v, 1, 1}, {v, 2}} -> {{v, f, 1}},
- {{v, 1, 2}, {v, 2}} -> {{v, f, 2}},
- {{v, f, 1}, {v, f, 2}} -> {{f}}},
- <||>,
- {{v, i}},
- {{v, 1}, {v, 2}, {v, 1, 1}, {v, 1, 2}, {v, f, 1}, {v, f, 2}}},
- (* single-history spacelike merging *)
- {{{{a_}, {a_, b_}} :> {{a, b}, {b}}, {{a_}, {a_}} :> {{a, a, a}}},
- <|"MaxDestroyerEvents" -> 1|>,
- {{a1}, {a1, a2}, {a2, a3}, {a3, m1}, {b1}, {b1, b2}, {b2, m1}, {m1, m2}},
- {{a1, a2}, {a2}, {a2, a3}, {a3}, {a3, m1}, {m1}, {b1, b2}, {b2}, {b2, m1}, {m1}, {m1, m2}, {m2}, {m1, m2},
- {m2}, {m2, m2, m2}}},
- (* multihistory spacelike merging *)
- {{{{a_}, {a_, b_}} :> {{b}}, {{a_}, {a_}} :> {{a, a, a}}},
- <||>,
- {{a1}, {a1, a2}, {a2, a3}, {a3, m1}, {b1}, {b1, b2}, {b2, m1}, {m1, m2}},
- {{a2}, {a3}, {m1}, {b2}, {m1}, {m2}, {m2}, {m1, m1, m1}, {m1, m1, m1}}},
- (* no single-history branchlike merging *)
- {{{{a_}, {a_, b_}} :> {{a, b}, {b}}, {{a_}, {a_}} :> {{a, a, a}}},
- <|"MaxDestroyerEvents" -> 1|>,
- {{o1}, {o1, a1}, {o1, b1}, {a1, a2}, {a2, a3}, {a3, m1}, {b1, b2}, {b2, m1}, {m1, m2}},
- {{o1, a1}, {a1}, {a1, a2}, {a2}, {a2, a3}, {a3}, {a3, m1}, {m1}, {m1, m2}, {m2}}},
- (* no multihistory branchlike merging *)
- {{{{a_}, {a_, b_}} :> {{b}}, {{a_}, {a_}} :> {{a, a, a}}},
- <||>,
- {{o1}, {o1, a1}, {o1, b1}, {a1, a2}, {a2, a3}, {a3, m1}, {b1, b2}, {b2, m1}, {m1, m2}},
- {{a1}, {b1}, {a2}, {a3}, {m1}, {b2}, {m1}, {m2}, {m2}}}},
+ Function[{rule, parameters, init, expectedCreatedExpressions},
+ VerificationTest[allExpressions @ GenerateMultihistory[MultisetSubstitutionSystem[rule], parameters] @ init,
+ Join[init, expectedCreatedExpressions]]
+ ] @@@ {
+ (* multihistory branching *)
+ {{{1} -> {2}, {1} -> {3}}, {}, {1}, {2, 3}},
+ {{{1} -> {2}, {1} -> {3}}, MaxDestroyerEvents -> 1, {1}, {2}},
+ (* no branchlike-expressions matching *)
+ {{{1} -> {2}, {1} -> {3}, {2, 3} -> {4}}, {}, {1}, {2, 3}},
+ (* no timelike-expressions matching *)
+ {{{1} -> {2}, {2} -> {3}, {2, 3} -> {4}}, {}, {1}, {2, 3}},
+ (* spacelike-expressions matching *)
+ {{{1} -> {2, 3}, {2, 3} -> {4}}, {}, {1}, {2, 3, 4}},
+ (* no instantiating the same match multiple times *)
+ {{1} -> {2}, {}, {1}, {2}},
+ (* no matching rules that don't match *)
+ {{a_, a_} :> {0}, {}, {1, 2}, {}},
+ (* no mixed spacelike/branchlike matching *)
+ {{{{v, i}} -> {{v, 1}, {v, 2}},
+ {{v, 1}} -> {{v, 1, 1}, {v, 1, 2}},
+ {{v, 1, 1}, {v, 2}} -> {{v, f, 1}},
+ {{v, 1, 2}, {v, 2}} -> {{v, f, 2}},
+ {{v, f, 1}, {v, f, 2}} -> {{f}}},
+ {},
+ {{v, i}},
+ {{v, 1}, {v, 2}, {v, 1, 1}, {v, 1, 2}, {v, f, 1}, {v, f, 2}}},
+ (* single-history spacelike merging *)
+ {{{{a_}, {a_, b_}} :> {{a, b}, {b}}, {{a_}, {a_}} :> {{a, a, a}}},
+ MaxDestroyerEvents -> 1,
+ {{a1}, {a1, a2}, {a2, a3}, {a3, m1}, {b1}, {b1, b2}, {b2, m1}, {m1, m2}},
+ {{a1, a2}, {a2}, {a2, a3}, {a3}, {a3, m1}, {m1}, {b1, b2}, {b2}, {b2, m1}, {m1}, {m1, m2}, {m2}, {m1, m2},
+ {m2}, {m2, m2, m2}}},
+ (* multihistory spacelike merging *)
+ {{{{a_}, {a_, b_}} :> {{b}}, {{a_}, {a_}} :> {{a, a, a}}},
+ {},
+ {{a1}, {a1, a2}, {a2, a3}, {a3, m1}, {b1}, {b1, b2}, {b2, m1}, {m1, m2}},
+ {{a2}, {a3}, {m1}, {b2}, {m1}, {m2}, {m2}, {m1, m1, m1}, {m1, m1, m1}}},
+ (* no single-history branchlike merging *)
+ {{{{a_}, {a_, b_}} :> {{a, b}, {b}}, {{a_}, {a_}} :> {{a, a, a}}},
+ MaxDestroyerEvents -> 1,
+ {{o1}, {o1, a1}, {o1, b1}, {a1, a2}, {a2, a3}, {a3, m1}, {b1, b2}, {b2, m1}, {m1, m2}},
+ {{o1, a1}, {a1}, {a1, a2}, {a2}, {a2, a3}, {a3}, {a3, m1}, {m1}, {m1, m2}, {m2}}},
+ (* no multihistory branchlike merging *)
+ {{{{a_}, {a_, b_}} :> {{b}}, {{a_}, {a_}} :> {{a, a, a}}},
+ {},
+ {{o1}, {o1, a1}, {o1, b1}, {a1, a2}, {a2, a3}, {a3, m1}, {b1, b2}, {b2, m1}, {m1, m2}},
+ {{a1}, {b1}, {a2}, {a3}, {m1}, {b2}, {m1}, {m2}, {m2}}}},
- (* non-overlapping systems produce the same behavior *)
- (* "InstantiationCounts" stores the sequences of expressions that were tried but do not match.
- "MaxDestroyerEvents" prevents some of these sequences to be tried in the first place, thus changing
- "InstantiationCounts" *)
- VerificationTest[
- With[{
- serializeMultihistory = (Normal /@ # &) /@ Normal /@ KeyDrop[Last @ #, "InstantiationCounts"] &,
- multihistories = GenerateMultihistory[
- MultisetSubstitutionSystem[
- {{v1_, v2_}, {v2_, v3_, v4_}} :>
- Module[{v5 = Hash[{{v1, v2}, {v2, v3, v4}}]}, {{v2, v3}, {v3, v4, v5}, {v1, v2, v3, v4}}]],
- <|"MaxDestroyerEvents" -> #|>,
- None,
- anEventOrdering,
- <|"MaxEvents" -> 30|>] @
- {{1, 2}, {2, 3, 4}} & /@ {1, Infinity}},
- SameQ @@ serializeMultihistory /@ multihistories]
- ]
- }]
+ (* non-overlapping systems produce the same behavior *)
+ (* "InstantiationCounts" stores the sequences of expressions that were tried but do not match.
+ "MaxDestroyerEvents" prevents some of these sequences to be tried in the first place, thus changing
+ "InstantiationCounts" *)
+ VerificationTest[
+ With[{
+ serializeMultihistory = (Normal /@ # &) /@ Normal /@ KeyDrop[Last @ #, "InstantiationCounts"] &,
+ multihistories = GenerateMultihistory[
+ MultisetSubstitutionSystem[
+ {{v1_, v2_}, {v2_, v3_, v4_}} :>
+ Module[{v5 = Hash[{{v1, v2}, {v2, v3, v4}}]}, {{v2, v3}, {v3, v4, v5}, {v1, v2, v3, v4}}]],
+ MaxEvents -> 30, MaxDestroyerEvents -> #] @ {{1, 2}, {2, 3, 4}} & /@ {1, Infinity}},
+ SameQ @@ serializeMultihistory /@ multihistories]
+ ]
}
|>,
"Multiset System Ordering" -> <|
@@ -350,13 +275,8 @@
),
"tests" -> {
Function[{rule, init, lastEventInputsOutput},
- VerificationTest[
- lastEventInputs @ GenerateMultihistory[MultisetSubstitutionSystem[rule],
- <||>,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- <|"MaxEvents" -> 1|>] @ init,
- lastEventInputsOutput]
+ VerificationTest[lastEventInputs @ GenerateMultihistory[MultisetSubstitutionSystem[rule], MaxEvents -> 1][init],
+ lastEventInputsOutput]
] @@@ {
{{{2, 3, 4} -> {X}, {3} -> {X}}, {1, 2, 3, 4, 5}, {3}},
{{{b_, _}, {_, b_}} :> {}, {{1, 2}, {3, 4}, {4, 5}, {2, 3}, {a, b}, {b, c}, {5, 6}}, {4, 1}},
@@ -366,11 +286,7 @@
Function[{rule, inits, lastExpressions},
VerificationTest[
- lastExpression @* GenerateMultihistory[MultisetSubstitutionSystem[rule],
- <||>,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- <|"MaxEvents" -> 1|>] /@ inits,
+ lastExpression @* GenerateMultihistory[MultisetSubstitutionSystem[rule], MaxEvents -> 1] /@ inits,
lastExpressions]
] @@@ {
{{{{1, 2}, {2, 3}} -> {1}, {{4, 5}, {5, 6}} -> {2}},
@@ -398,82 +314,51 @@
destroyerEventCounts[Multihistory[_, data_]] := Normal @ data["ExpressionDestroyerEventCounts"];
),
"tests" -> {
- With[{anEventOrdering = EventOrderingFunctions[MultisetSubstitutionSystem]}, {
- Function[{
- rule, selection, stopping, init, expectedMaxEventGeneration, expectedEventCount, expectedTerminationReason},
- VerificationTest[
- Through[{maxEventGeneration, eventCount, terminationReason} @
- GenerateMultihistory[
- MultisetSubstitutionSystem[rule], selection, None, anEventOrdering, stopping] @ init],
- {expectedMaxEventGeneration, expectedEventCount, expectedTerminationReason}]
- ] @@@ {
- (* Complete is returned if MaxGeneration is reached because MaxGeneration is not a stopping condition *)
- {{_} :> {Unique[]}, <|"MaxGeneration" -> 2|>, <||>, {1}, 2, 2, "Complete"},
- {{_} :> {Unique[]}, <|"MaxGeneration" -> 0|>, <||>, {1}, 0, 0, "Complete"},
- {{_} :> {Unique[]}, <|"MaxGeneration" -> 3|>, <|"MaxEvents" -> 2|>, {1}, 2, 2, "MaxEvents"},
- {{_} :> {Unique[]}, <||>, <|"MaxEvents" -> 0|>, {1}, 0, 0, "MaxEvents"},
- {ToPatternRules[{{0, 1}} -> {{0, 2}, {2, 1}}], <|"MaxGeneration" -> 3|>, <||>, {{0, 1}}, 3, 7, "Complete"},
- {ToPatternRules[{{0, 1}} -> {{0, 2}, {2, 1}}], <||>, <|"MaxEvents" -> 6|>, {{0, 1}}, 3, 6, "MaxEvents"},
- {ToPatternRules[{{0, 1}} -> {{0, 2}, {2, 1}}],
- <|"MaxGeneration" -> 3|>,
- <|"MaxEvents" -> 6|>,
- {{0, 1}},
- 3, 6, "MaxEvents"},
- {ToPatternRules[{{0, 1}} -> {{0, 2}, {2, 1}}],
- <|"MaxGeneration" -> 2|>,
- <|"MaxEvents" -> 6|>,
- {{0, 1}},
- 2, 3, "Complete"},
- {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}],
- <|"MaxDestroyerEvents" -> 1|>,
- <||>,
- {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
- 2, 3, "Complete"},
- {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}],
- <|"MaxDestroyerEvents" -> 2|>,
- <||>,
- {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
- 2, 6, "Complete"},
- {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}],
- <|"MaxDestroyerEvents" -> 3|>,
- <||>,
- {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
- 3, 10, "Complete"},
- {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}],
- <||>,
- <||>,
- {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
- 3, 12, "Complete"},
- {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}],
- <|"MaxDestroyerEvents" -> 0|>,
- <||>,
- {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
- 0, 0, "Complete"}},
-
- With[{init = {1, 2, 3, 4, 5, 2/3, 5/3, 7/3}},
- VerificationTest[
- allExpressions @ GenerateMultihistory[
- MultisetSubstitutionSystem[{n___} /; Plus[n] == 5 && OrderedQ[{n}] :> {{n}}],
- <|"MinEventInputs" -> #1, "MaxEventInputs" -> #2, "MaxGeneration" -> 1|>,
- None,
- anEventOrdering,
- <||>] @ init,
- Join[init, #3],
- SameTest -> MatchQ]
- ] & @@@
- {{1, 2, {{5}, {1, 4}, {2, 3}}}, {2, 3, {{1, 4}, {2, 3}, {1, 5/3, 7/3}, {2/3, 2, 7/3}}}, {2, 0, {}}},
+ Function[{
+ rule, parameters, init, expectedMaxEventGeneration, expectedEventCount, expectedTerminationReason},
+ VerificationTest[
+ Through[{maxEventGeneration, eventCount, terminationReason} @
+ GenerateMultihistory[MultisetSubstitutionSystem[rule], parameters] @ init],
+ {expectedMaxEventGeneration, expectedEventCount, expectedTerminationReason}]
+ ] @@@ {
+ (* Complete is returned if MaxGeneration is reached because MaxGeneration is not a stopping condition *)
+ {{_} :> {Unique[]}, MaxGeneration -> 2, {1}, 2, 2, "Complete"},
+ {{_} :> {Unique[]}, MaxGeneration -> 0, {1}, 0, 0, "Complete"},
+ {{_} :> {Unique[]}, {MaxGeneration -> 3, MaxEvents -> 2}, {1}, 2, 2, "MaxEvents"},
+ {{_} :> {Unique[]}, MaxEvents -> 0, {1}, 0, 0, "MaxEvents"},
+ {ToPatternRules[{{0, 1}} -> {{0, 2}, {2, 1}}], MaxGeneration -> 3, {{0, 1}}, 3, 7, "Complete"},
+ {ToPatternRules[{{0, 1}} -> {{0, 2}, {2, 1}}], MaxEvents -> 6, {{0, 1}}, 3, 6, "MaxEvents"},
+ {ToPatternRules[{{0, 1}} -> {{0, 2}, {2, 1}}], {MaxGeneration -> 3, MaxEvents -> 6}, {{0, 1}},
+ 3, 6, "MaxEvents"},
+ {ToPatternRules[{{0, 1}} -> {{0, 2}, {2, 1}}], {MaxGeneration -> 2, MaxEvents -> 6}, {{0, 1}},
+ 2, 3, "Complete"},
+ {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}], MaxDestroyerEvents -> 1, {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
+ 2, 3, "Complete"},
+ {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}], MaxDestroyerEvents -> 2, {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
+ 2, 6, "Complete"},
+ {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}], MaxDestroyerEvents -> 3, {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
+ 3, 10, "Complete"},
+ {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}], {}, {{0, 1}, {1, 2}, {2, 3}, {3, 4}}, 3, 12, "Complete"},
+ {ToPatternRules[{{0, 1}, {1, 2}} -> {{0, 2}}], MaxDestroyerEvents -> 0, {{0, 1}, {1, 2}, {2, 3}, {3, 4}},
+ 0, 0, "Complete"}},
+ With[{init = {1, 2, 3, 4, 5, 2/3, 5/3, 7/3}},
VerificationTest[
- destroyerEventCounts @ GenerateMultihistory[
- MultisetSubstitutionSystem[ToPatternRules[{{1, 2}, {2, 3}} -> {{2, 3}, {2, 4}, {3, 4}, {2, 1}}]],
- <|"MaxDestroyerEvents" -> #|>,
- None,
- anEventOrdering,
- <|"MaxEvents" -> 5|>] @ {{1, 1}, {1, 1}},
- #2,
- SameTest -> MatchQ] & @@@
- {{2, {2, 2, 2, 2, 1, 1, 0..}}, {3, {2, 2, 3, 1, 1, 1, 0..}}}
- }]
+ allExpressions @ GenerateMultihistory[
+ MultisetSubstitutionSystem[{n___} /; Plus[n] == 5 && OrderedQ[{n}] :> {{n}}],
+ MinEventInputs -> #1, MaxEventInputs -> #2, MaxGeneration -> 1] @ init,
+ Join[init, #3],
+ SameTest -> MatchQ]
+ ] & @@@
+ {{1, 2, {{5}, {1, 4}, {2, 3}}}, {2, 3, {{1, 4}, {2, 3}, {1, 5/3, 7/3}, {2/3, 2, 7/3}}}, {2, 0, {}}},
+
+ VerificationTest[
+ destroyerEventCounts @ GenerateMultihistory[
+ MultisetSubstitutionSystem[ToPatternRules[{{1, 2}, {2, 3}} -> {{2, 3}, {2, 4}, {3, 4}, {2, 1}}]],
+ MaxDestroyerEvents -> #, MaxEvents -> 5] @ {{1, 1}, {1, 1}},
+ #2,
+ SameTest -> MatchQ] & @@@
+ {{2, {2, 2, 2, 2, 1, 1, 0..}}, {3, {2, 2, 3, 1, 1, 1, 0..}}}
}
|>
|>
diff --git a/Tests/MultisetToWolframModelEvolutionObject.wlt b/Tests/MultisetToWolframModelEvolutionObject.wlt
index 46e43eee..0ce416fe 100644
--- a/Tests/MultisetToWolframModelEvolutionObject.wlt
+++ b/Tests/MultisetToWolframModelEvolutionObject.wlt
@@ -9,23 +9,13 @@
VerificationTest[
(VertexCount @ #["ExpressionsEventsGraph"] &) @
SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- {"MaxGeneration" -> 1},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}] @
- {1, 2, 3},
+ GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}], MaxGeneration -> 1] @ {1, 2, 3},
15],
VerificationTest[
(#["EventsCount"] &) @
SetReplaceTypeConvert[{WolframModelEvolutionObject, 2}] @
- GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- {},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {"MaxEvents" -> 10}] @
- {1, 2, 3},
+ GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}], MaxEvents -> 10] @ {1, 2, 3},
10]
}
|>
diff --git a/Tests/generatorSystem.wlt b/Tests/generatorSystem.wlt
new file mode 100644
index 00000000..b3ab4f97
--- /dev/null
+++ b/Tests/generatorSystem.wlt
@@ -0,0 +1,144 @@
+<|
+ "generatorSystem" -> <|
+ "init" -> (
+ Attributes[Global`testUnevaluated] = Attributes[Global`testSymbolLeak] = {HoldAll};
+ Global`testUnevaluated[args___] := SetReplace`PackageScope`testUnevaluated[VerificationTest, args];
+ Global`declareSystem = SetReplace`PackageScope`declareSystem;
+ Global`declareSystemGenerator = SetReplace`PackageScope`declareSystemGenerator;
+ Global`declareSystemParameter = SetReplace`PackageScope`declareSystemParameter;
+ Global`initializeSystemGenerators = SetReplace`PackageScope`initializeSystemGenerators;
+
+ declareSystemParameter[maxEventSize, Infinity, _ ? (GreaterEqualThan[0]), "is a max parameter for a test value."];
+ declareSystemParameter[minEventSize, 0, _ ? (GreaterEqualThan[0]), "is a min parameter for a test value."];
+ declareSystemParameter[eventType, None, None | 0 | 1 | 2, "is a choice parameter between None, 0, 1, and 2."];
+
+ declareSystem[genericSystem, List, _Integer, {maxEventSize, eventType}, True];
+ declareSystem[systemWithParameterDependencies,
+ List,
+ _Integer,
+ {minEventSize, maxEventSize, eventType},
+ Implies[eventType, minEventSize || maxEventSize]];
+ declareSystem[minXorMaxSystem, List, _Integer, {minEventSize, maxEventSize}, Xor[minEventSize, maxEventSize]];
+ declareSystem[listStateSystem, List, _List, {minEventSize, maxEventSize, eventType}, True];
+ declareSystem[realParameterSystem, List, _, {MaxDestroyerEvents, MaxEvents, MaxGeneration}, True];
+
+ declareSystemGenerator[genericGenerator, internalGenericGenerator, <||>, Identity, "does nothing."];
+ declareSystemGenerator[typeTwoGenerator, internalTypeTwoGenerator, <|eventType -> 2|>, property, "picks 2."];
+
+ Unprotect[$SetReplaceSystems, $SetReplaceGenerators, GenerateSingleHistory, GenerateMultihistory];
+ initializeSystemGenerators[];
+ Protect[$SetReplaceSystems, $SetReplaceGenerators, GenerateSingleHistory, GenerateMultihistory];
+ ),
+ "tests" -> {
+ (* Declaration errors *)
+ VerificationTest[declareSystem[invalidSystem, List, _, {minEventSize}, minEventSize && !minEventSize],
+ _,
+ {SetReplace::unsatisfiableParameterDependencies},
+ SameTest -> MatchQ],
+ VerificationTest[declareSystem[invalidSystem, List, _, {minEventSize}],
+ _,
+ {SetReplace::invalidSystemDeclaration},
+ SameTest -> MatchQ],
+ VerificationTest[declareSystemGenerator[invalidGenerator, internalInvalidGenerator, <||>, Identity],
+ _,
+ {SetReplace::invalidSystemGeneratorDeclaration},
+ SameTest -> MatchQ],
+ VerificationTest[declareSystemParameter[invalidParameter, 0, _],
+ _,
+ {SetReplace::invalidSystemParameterDeclaration},
+ SameTest -> MatchQ],
+
+ (* Zero args *)
+ testUnevaluated[genericGenerator[], {}], (* nothing is evaluated until the init is given *)
+
+ (* One arg *)
+ testUnevaluated[genericGenerator[0], {}],
+ testUnevaluated[genericGenerator[genericSystem[]], {}],
+
+ (* Two args *)
+ testUnevaluated[genericGenerator[0, 0][0], {genericGenerator::unknownSystem}],
+ testUnevaluated[genericGenerator[genericSystem, 0][0], {genericGenerator::noRules}],
+ testUnevaluated[genericGenerator[genericSystem[], "test"][0], {genericGenerator::invalidGeneratorParameterSpec}],
+ VerificationTest[
+ genericGenerator[genericSystem[]][0], {genericSystem[], 0, <|maxEventSize -> Infinity, eventType -> None|>}],
+
+ (* Operator args *)
+ testUnevaluated[genericGenerator[genericSystem[]][##], {genericGenerator::argx}] & @@@ {{0, 1}, {}},
+
+ (* Parameters spec *)
+ testUnevaluated[genericGenerator[genericSystem[], abc][0], {genericGenerator::invalidGeneratorParameterSpec}],
+ testUnevaluated[genericGenerator[genericSystem[], {abc}][0], {genericGenerator::invalidGeneratorParameterSpec}],
+ testUnevaluated[genericGenerator[genericSystem[], abc -> 4][0], {genericGenerator::unknownParameter}],
+ testUnevaluated[genericGenerator[genericSystem[], maxEventSize -> 4], {}],
+ VerificationTest[genericGenerator[genericSystem[], maxEventSize -> 4][0],
+ {genericSystem[], 0, <|maxEventSize -> 4, eventType -> None|>}],
+ testUnevaluated[genericGenerator[genericSystem[], maxEventSize -> -1][0], {genericGenerator::invalidParameter}],
+ testUnevaluated[genericGenerator[genericSystem[], minEventSize -> 4][0], {genericGenerator::unknownParameter}],
+
+ VerificationTest[
+ genericGenerator[genericSystem[], ##][0], {genericSystem[], 0, <|maxEventSize -> 4, eventType -> 0|>}] & @@@ {
+ {maxEventSize -> 4, eventType -> 0},
+ {{maxEventSize -> 4}, eventType -> 0},
+ {maxEventSize -> 4, {eventType -> 0}},
+ {<|maxEventSize -> 4|>, {{eventType -> 0}}}
+ },
+
+ VerificationTest[genericGenerator[genericSystem[], eventType -> 0, eventType -> 1][0],
+ {genericSystem[], 0, <|maxEventSize -> Infinity, eventType -> 1|>}],
+ testUnevaluated[genericGenerator[genericSystem[], eventType -> 0, eventType -> 1, 3][0],
+ {genericGenerator::invalidGeneratorParameterSpec}],
+
+ (* Generator with predefined parameters *)
+ VerificationTest[typeTwoGenerator[genericSystem[]][0],
+ property[{genericSystem[], 0, <|maxEventSize -> Infinity, eventType -> 2|>}]],
+ testUnevaluated[typeTwoGenerator[genericSystem[], eventType -> 1][0], {typeTwoGenerator::forbiddenParameter}],
+ VerificationTest[typeTwoGenerator[genericSystem[], maxEventSize -> 2][0],
+ property[{genericSystem[], 0, <|maxEventSize -> 2, eventType -> 2|>}]],
+
+ (* Parameter dependencies *)
+ VerificationTest[
+ genericGenerator[systemWithParameterDependencies[]][0],
+ {systemWithParameterDependencies[], 0, <|minEventSize -> 0, maxEventSize -> Infinity, eventType -> None|>}],
+ testUnevaluated[
+ genericGenerator[systemWithParameterDependencies[], eventType -> 2][0], {genericGenerator::missingParameters}],
+ VerificationTest[
+ genericGenerator[systemWithParameterDependencies[], eventType -> 2, minEventSize -> 2][0],
+ {systemWithParameterDependencies[], 0, <|minEventSize -> 2, maxEventSize -> Infinity, eventType -> 2|>}],
+ VerificationTest[
+ genericGenerator[systemWithParameterDependencies[], eventType -> 2, maxEventSize -> 2][0],
+ {systemWithParameterDependencies[], 0, <|minEventSize -> 0, maxEventSize -> 2, eventType -> 2|>}],
+
+ testUnevaluated[typeTwoGenerator[systemWithParameterDependencies[]][0], {typeTwoGenerator::missingParameters}],
+ VerificationTest[
+ typeTwoGenerator[systemWithParameterDependencies[], minEventSize -> 2][0],
+ property[
+ {systemWithParameterDependencies[], 0, <|minEventSize -> 2, maxEventSize -> Infinity, eventType -> 2|>}]],
+
+ testUnevaluated[typeTwoGenerator[minXorMaxSystem[]][0], {typeTwoGenerator::incompatibleSystem}],
+ testUnevaluated[typeTwoGenerator[minXorMaxSystem[], eventType -> 2][0], {typeTwoGenerator::incompatibleSystem}],
+ testUnevaluated[genericGenerator[minXorMaxSystem[]][0], {genericGenerator::missingParameters}],
+ VerificationTest[genericGenerator[minXorMaxSystem[], minEventSize -> 2][0],
+ {minXorMaxSystem[], 0, <|minEventSize -> 2, maxEventSize -> Infinity|>}],
+ testUnevaluated[genericGenerator[minXorMaxSystem[], minEventSize -> 2, maxEventSize -> 3][0],
+ {genericGenerator::incompatibleParameters}],
+
+ (* Existing generators *)
+ VerificationTest[
+ GenerateMultihistory[realParameterSystem[]][0],
+ {realParameterSystem[],
+ 0,
+ <|MaxDestroyerEvents -> Infinity, MaxEvents -> Infinity, MaxGeneration -> Infinity|>}],
+ VerificationTest[
+ GenerateSingleHistory[realParameterSystem[]][0],
+ {realParameterSystem[], 0, <|MaxDestroyerEvents -> 1, MaxEvents -> Infinity, MaxGeneration -> Infinity|>}],
+
+ (* Introspection *)
+ VerificationTest[
+ SubsetQ[
+ $SetReplaceSystems,
+ {genericSystem, systemWithParameterDependencies, minXorMaxSystem, listStateSystem, realParameterSystem}]],
+ VerificationTest[SubsetQ[$SetReplaceGenerators, {genericGenerator, typeTwoGenerator}]],
+ VerificationTest[SetReplaceSystemParameters[listStateSystem], {minEventSize, maxEventSize, eventType}]
+ }
+ |>
+|>
diff --git a/Tests/hypergraphMatching.wlt b/Tests/hypergraphMatching.wlt
index 1bb28a27..23c8a6dc 100644
--- a/Tests/hypergraphMatching.wlt
+++ b/Tests/hypergraphMatching.wlt
@@ -22,11 +22,7 @@
matchingFunction[MultisetSubstitutionSystem] =
(#["EventRuleIndices"]["Length"] > 1 &) @
- Last @ GenerateMultihistory[MultisetSubstitutionSystem[ToPatternRules[#HypergraphRule]],
- <||>,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- <||>] @ #Init &;
+ Last @ GenerateMultihistory[MultisetSubstitutionSystem[ToPatternRules[#HypergraphRule]]][#Init] &;
graphFromHyperedges[edges_] := Graph[UndirectedEdge @@@ Flatten[Partition[#, 2, 1] & /@ edges, 1]];
@@ -120,13 +116,8 @@
] & /@ {"LowLevel", "Symbolic"},
VerificationTest[
- Normal @
- (#["Expressions"] &) @
- Last @ GenerateMultihistory[MultisetSubstitutionSystem[ToPatternRules[{{1, 2}, {2, 3}} -> {{1, 3}}]],
- <||>,
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- <|"MaxEvents" -> 1|>][{{1, 2}, {2, 1}}],
+ Normal @ (#["Expressions"] &) @ Last @ GenerateMultihistory[
+ MultisetSubstitutionSystem[ToPatternRules[{{1, 2}, {2, 3}} -> {{1, 3}}]], MaxEvents -> 1] @ {{1, 2}, {2, 1}},
{{1, 2}, {2, 1}, {1, 1}}
],
diff --git a/performanceTest.wls b/performanceTest.wls
index a1f6271b..3178747e 100755
--- a/performanceTest.wls
+++ b/performanceTest.wls
@@ -65,11 +65,9 @@ Check[
{{3, 3, 3, 6}, {3, 3, 7, 3}, {3, 5, 5}, {3, 3, 1}, {3, 2, 3}, {3, 3, 3, 3, 3, 3}, {4, 6, 6}, {4, 4, 4, 4, 4, 4},
{8, 7, 7}, {8, 8, 8, 8, 8, 8}, {1, 4, 1}, {1, 1, 1, 1, 1}, {2, 2, 8}, {2, 2}},
64],
- "Multiset addition" -> GenerateMultihistory[MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
- {"MaxGeneration" -> 2, "MinEventInputs" -> 2, "MaxEventInputs" -> 2},
- None,
- EventOrderingFunctions[MultisetSubstitutionSystem],
- {}][{1, 2, 3, 4}]
+ "Multiset addition" -> GenerateMultihistory[
+ MultisetSubstitutionSystem[{a_, b_} :> {a + b}],
+ MaxGeneration -> 2, MinEventInputs -> 2, MaxEventInputs -> 2] @ {1, 2, 3, 4}
|>;
$defaultMeasurementsCount = 5;
@@ -84,10 +82,11 @@ Check[
Exit[1];
];
- Attributes[meanAroundTiming] = {HoldAll};
- meanAroundTiming[expr_] := MeanAround @ Table[First @ AbsoluteTiming[expr], $measurementsCount];
+ Attributes[meanAroundTiming] = {HoldFirst};
+ meanAroundTiming[expr_, resultsFunction_] :=
+ MeanAround @ Table[First @ AbsoluteTiming[resultsFunction @ expr], $measurementsCount];
- runTests[repo_, sha_, tests_] := ModuleScope[
+ runTests[repo_, sha_, tests_, resultsFunction_] := ModuleScope[
Print["Testing ", sha];
GitCheckoutReference[repo, sha];
CloseKernels[];
@@ -95,7 +94,8 @@ Check[
result = ParallelEvaluate[
Get[FileNameJoin[{$setReplaceRoot, "Kernel", "init.m"}]];
Check[
- meanAroundTiming @@@ KeyMap[ReleaseHold, ReleaseHold @ Map[Hold, tests, {3}]]
+ Function[{test}, meanAroundTiming[test, resultsFunction], HoldFirst] @@@
+ KeyMap[ReleaseHold, ReleaseHold @ Map[Hold, tests, {3}]]
,
$Failed
]
@@ -134,7 +134,8 @@ Check[
$symbolicRef = RunProcess[{"git", "symbolic-ref", "--short", "HEAD"}];
$originalRef = If[$symbolicRef["ExitCode"] === 0, StringExtract[$symbolicRef["StandardOutput"], 1], $currentSHA];
- {$oldResults, $newResults} = runTests[$gitRepo, #, tests] & /@ {$oldSHA, $newSHA};
+ $oldResults = runTests[$gitRepo, $oldSHA, tests, Quiet];
+ $newResults = runTests[$gitRepo, $newSHA, tests, Identity];
RunProcess[{"git", "checkout", "-q", $originalRef}];
(* We need to return to the original branch before exiting. *)