Skip to content

Commit

Permalink
EventDeduplication -> SameInputSetIsomorphicOutputs (#404)
Browse files Browse the repository at this point in the history
## Changes
* Closes #341.
* Adds `"EventDeduplication"` option, which, if set to `"SameInputSetIsomorphicOutputs"`, allows one to treat multiple matches to the same set of inputs producing isomorphic outputs as one.

## Review questions
* Can you think of a better name than `"SameInputSetIsomorphicOutputs"`?

Keep in mind that another possibility for event identification would be, for example, a rule `{{0}, {0}} -> {}` starting with an input `{{0}, {0}, {0}}`. In this case, there are three events possible, but they are still the same in the sense that the states after instantiating any of these events would be identical. So, we might want to add an option for this case as well. Maybe, as an element in a list `"EventDeduplication" -> {"SameInputSetIsomorphicOutputs", ...}`. And if all possible identification are needed, `"EventDeduplication" -> All`.

## Examples
* By default, matches to every possible order of the input subsets will be turned into separate events:
```wl
In[] := WolframModel[{{a, b}, {a, c}} -> {{b, d}, {c, d}}, {{1, 2}, {1, 3}}, 
  "EventSelectionFunction" -> 
   "MultiwaySpacelike"]["ExpressionsEventsGraph", 
 VertexLabels -> Automatic]
```
<img width="399" alt="image" src="https://user-images.githubusercontent.com/1479325/93140017-bdaa8480-f6af-11ea-9339-96599bcf36b7.png">

* However, with `"EventDeduplication" -> "SameInputSetIsomorphicOutputs"` they will be identified if the outputs are the same up to the renaming of new atoms:
```wl
In[] := WolframModel[{{a, b}, {a, c}} -> {{b, d}, {c, d}}, {{1, 2}, {1, 3}}, 
  "EventSelectionFunction" -> "MultiwaySpacelike", 
  "EventDeduplication" -> "SameInputSetIsomorphicOutputs"]["ExpressionsEventsGraph", 
 VertexLabels -> Automatic]
```
<img width="259" alt="image" src="https://user-images.githubusercontent.com/1479325/93140076-d5820880-f6af-11ea-8336-e47edb8a176a.png">

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/maxitg/setreplace/404)
<!-- Reviewable:end -->
  • Loading branch information
maxitg authored Sep 23, 2020
1 parent b43297a commit f7715de
Show file tree
Hide file tree
Showing 16 changed files with 506 additions and 96 deletions.
3 changes: 2 additions & 1 deletion Kernel/WolframModel.m
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@
Method -> overridenOptionValue[Method],
TimeConstraint -> overridenOptionValue[TimeConstraint],
"EventOrderingFunction" -> overridenOptionValue["EventOrderingFunction"],
"EventSelectionFunction" -> overridenOptionValue["EventSelectionFunction"]],
"EventSelectionFunction" -> overridenOptionValue["EventSelectionFunction"],
"EventDeduplication" -> overridenOptionValue["EventDeduplication"]],
$Failed],
$Failed];
If[evolution === $Aborted, Return[$Aborted]];
Expand Down
16 changes: 13 additions & 3 deletions Kernel/setSubstitutionSystem$cpp.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
{Integer, 1}, (* initial set *)
Integer, (* event selection function *)
{Integer, 1}, (* ordering function index, forward / reverse, function, forward / reverse, ... *)
Integer, (* event deduplication *)
Integer}, (* random seed *)
Integer], (* set ptr *)
$Failed];
Expand Down Expand Up @@ -241,8 +242,15 @@
|>;


$eventDeduplicationCodes = <|
None -> 0,
$sameInputSetIsomorphicOutputs -> 1
|>;


setSubstitutionSystem$cpp[
rules_, set_, stepSpec_, returnOnAbortQ_, timeConstraint_, eventOrderingFunction_, eventSelectionFunction_] /;
rules_, set_, stepSpec_, returnOnAbortQ_, timeConstraint_, eventOrderingFunction_, eventSelectionFunction_,
eventDeduplication_] /;
$cppSetReplaceAvailable := Module[{
canonicalRules,
setAtoms, atomsInRules, globalAtoms, globalIndex,
Expand All @@ -268,6 +276,7 @@
encodeNestedLists[mappedSet],
systemTypeCode[eventSelectionFunction],
Catenate[Replace[eventOrderingFunction, $orderingFunctionCodes, {2}]],
Replace[eventDeduplication, $eventDeduplicationCodes],
RandomInteger[{0, $maxUInt32}]];
TimeConstrained[
CheckAbort[
Expand All @@ -281,8 +290,9 @@
If[!returnOnAbortQ, Return[$Aborted], terminationReason = $timeConstraint]];
numericAtomLists = decodeAtomLists[$cpp$setExpressions[setPtr]];
events = decodeEvents[$cpp$setEvents[setPtr]];
maxCompleteGeneration =
Replace[$cpp$maxCompleteGeneration[setPtr], LibraryFunctionError[___] -> Missing["Unknown", $Aborted]];
maxCompleteGeneration = CheckAbort[
Replace[$cpp$maxCompleteGeneration[setPtr], LibraryFunctionError[___] -> Missing["Unknown", $Aborted]],
If[!returnOnAbortQ, Abort[], terminationReason = $Aborted; Missing["Unknown", $Aborted]]];
terminationReason = Replace[$terminationReasonCodes[$cpp$terminationReason[setPtr]], {
$Aborted -> terminationReason,
$notTerminated -> $timeConstraint}];
Expand Down
47 changes: 31 additions & 16 deletions Kernel/setSubstitutionSystem.m
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
PackageScope["$globalSpacelike"]
PackageScope["$spacelike"]

PackageScope["$sameInputSetIsomorphicOutputs"]


(* ::Text:: *)
(*Termination reason values*)
Expand Down Expand Up @@ -232,7 +234,7 @@


(* ::Subsection:: *)
(*EventSelectionFunction is valid*)
(*String-valued parameter is valid*)


$eventSelectionFunctions = <|
Expand All @@ -242,15 +244,20 @@
|>;


parseEventSelectionFunction[caller_, func_ /; MemberQ[Keys[$eventSelectionFunctions], func]] :=
$eventSelectionFunctions[func]
$eventDeduplications = <|
None -> None,
"SameInputSetIsomorphicOutputs" -> $sameInputSetIsomorphicOutputs
|>;


parseParameterValue[caller_, name_, value_, association_] /; MemberQ[Keys[association], value] := association[value]

General::invalidEventSelection = "EventSelectionFunction `1` should be one of `2`.";

General::invalidParameterValue = "`1` `2` should be one of `3`.";

parseEventSelectionFunction[caller_, func_] := (
Message[caller::invalidEventSelection, func, Keys[$eventSelectionFunctions]];

parseParameterValue[caller_, name_, value_, association_] := (
Message[caller::invalidParameterValue, name, value, Keys[association]];
$Failed
)

Expand Down Expand Up @@ -315,17 +322,19 @@
Method -> Automatic,
TimeConstraint -> Infinity,
"EventOrderingFunction" -> Automatic,
"EventSelectionFunction" -> "GlobalSpacelike"};
"EventSelectionFunction" -> "GlobalSpacelike",
"EventDeduplication" -> None
};


(* ::Text:: *)
(*Switching code between WL and C++ implementations*)


General::symbOrdering = "Custom event ordering and selection functions are not supported for symbolic method.";
General::symbOrdering = "Custom event ordering, selection and deduplication are not supported for symbolic method.";

General::symbNotImplemented =
"Custom event ordering and selection functions are only available for local rules, " <>
"Custom event ordering, selection and deduplication are only available for local rules, " <>
"and only for sets of lists (hypergraphs).";


Expand All @@ -339,12 +348,18 @@
method = OptionValue[Method],
timeConstraint = OptionValue[TimeConstraint],
eventOrderingFunction = parseEventOrderingFunction[caller, OptionValue["EventOrderingFunction"]],
eventSelectionFunction = parseEventSelectionFunction[caller, OptionValue["EventSelectionFunction"]],
eventSelectionFunction = parseParameterValue[
caller, "EventSelectionFunction", OptionValue["EventSelectionFunction"], $eventSelectionFunctions],
eventDeduplication = parseParameterValue[
caller, "EventDeduplication", OptionValue["EventDeduplication"], $eventDeduplications],
symbolicEvaluationSupportedQ = OptionValue["EventOrderingFunction"] === Automatic &&
OptionValue["EventSelectionFunction"] === "GlobalSpacelike" &&
OptionValue["EventDeduplication"] === None,
canonicalRules,
failedQ = False},
If[eventOrderingFunction === $Failed || eventSelectionFunction === $Failed, Return[$Failed]];
If[(OptionValue["EventOrderingFunction"] =!= Automatic ||
OptionValue["EventSelectionFunction"] =!= "GlobalSpacelike") && method === "Symbolic",
If[eventOrderingFunction === $Failed || eventSelectionFunction === $Failed || eventDeduplication === $Failed,
Return[$Failed]];
If[!symbolicEvaluationSupportedQ && method === "Symbolic",
Message[caller::symbOrdering];
Return[$Failed]];
If[(timeConstraint > 0) =!= True, Return[$Failed]];
Expand All @@ -355,16 +370,16 @@
If[$cppSetReplaceAvailable,
Return[
setSubstitutionSystem$cpp[
rules, set, stepSpec, returnOnAbortQ, timeConstraint, eventOrderingFunction, eventSelectionFunction]]]];
rules, set, stepSpec, returnOnAbortQ, timeConstraint, eventOrderingFunction, eventSelectionFunction,
eventDeduplication]]]];
If[MatchQ[method, $cppMethod],
failedQ = True;
If[!$cppSetReplaceAvailable,
makeMessage[caller, "noLowLevel"],
makeMessage[caller, "lowLevelNotImplemented"]]];
If[failedQ || !MatchQ[OptionValue[Method], Alternatives @@ $SetReplaceMethods],
$Failed,
If[OptionValue["EventOrderingFunction"] =!= Automatic ||
OptionValue["EventSelectionFunction"] =!= "GlobalSpacelike",
If[!symbolicEvaluationSupportedQ,
Message[caller::symbNotImplemented];
Return[$Failed]];
setSubstitutionSystem$wl[caller, rules, set, stepSpec, returnOnAbortQ, timeConstraint]]
Expand Down
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1263,7 +1263,7 @@ Objects are automatically converted to the latest version when they are encounte
### Options
["VertexNamingFunction"](#vertexnamingfunction) | ["IncludePartialGenerations"](#includepartialgenerations) | ["IncludeBoundaryEvents"](#includeboundaryevents) | [Method](#method) | [TimeConstraint](#timeconstraint) | ["EventOrderingFunction"](#eventorderingfunction) | ["EventSelectionFunction"](#eventselectionfunction)
["VertexNamingFunction"](#vertexnamingfunction) | ["IncludePartialGenerations"](#includepartialgenerations) | ["IncludeBoundaryEvents"](#includeboundaryevents) | [Method](#method) | [TimeConstraint](#timeconstraint) | ["EventOrderingFunction"](#eventorderingfunction) | ["EventSelectionFunction"](#eventselectionfunction) | ["EventDeduplication"](#eventdeduplication)
#### "VertexNamingFunction"
Expand Down Expand Up @@ -1611,6 +1611,55 @@ In[] := WolframModel[{{{1, 2}, {2, 3}} -> {{1, 2, 3}},
Because of this branchlike and timelike matching, branches in `"EventSelectionFunction" -> None` evolution are not
separated but can "interfere" with one another.
#### "EventDeduplication"
Some rules can match the same set of inputs in different ways.
For example, consider the rule `{{a, b}, {a, c}} -> {{b, c}}` starting with an initial condition `{{1, 2}, {1, 3}}`.
There are two possible ways to match it: `<|a -> 1, b -> 2, c -> 3|>` and `<|a -> 1, b -> 3, c -> 2|>`.
In this case, these matches yield different results, `{2, 3}` and `{3, 2}` respectively:
```wl
In[] := WolframModel[{{a, b}, {a, c}} -> {{b, c}}, {{1, 2}, {1, 3}},
"EventSelectionFunction" ->
"MultiwaySpacelike"]["ExpressionsEventsGraph",
VertexLabels -> Automatic]
```
<img src="READMEImages/TwoMatchOrdersDifferentOutcomes.png" width="310">
In the case above the outputs are different, however sometimes they are the same (more precisely, isomorphic):
```wl
In[] := WolframModel[{{a, b}, {a, c}} -> {{b, c}, {c, b}}, {{1, 2}, {1, 3}},
"EventSelectionFunction" ->
"MultiwaySpacelike"]["ExpressionsEventsGraph",
VertexLabels -> Automatic]
```
<img src="READMEImages/TwoMatchOrdersSameOutcome.png" width="478">
**`EventDeduplication`** option can be used in a case like this to combine these two identical events into one:
```wl
In[] := WolframModel[{{a, b}, {a, c}} -> {{b, c}, {c, b}}, {{1, 2}, {1, 3}},
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> "SameInputSetIsomorphicOutputs"]["ExpressionsEventsGraph",
VertexLabels -> Automatic]
```
<img src="READMEImages/TwoIdentifiedMatchOrders.png" width="310">
The outputs of the rule need not be identical, but should be isomorphic with respect to renaming of new atoms:
```wl
In[] := WolframModel[{{a, b}, {a, c}} -> {{b, d}, {c, d}}, {{1, 2}, {1, 3}},
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> "SameInputSetIsomorphicOutputs"]["ExpressionsEventsGraph",
VertexLabels -> Automatic]
```
<img src="READMEImages/TwoIsomorphicMatchOrders.png" width="310">
## WolframModelPlot
[Edge Type](#edge-type) | [GraphHighlight and GraphHighlightStyle](#graphhighlight-and-graphhighlightstyle) | ["HyperedgeRendering"](#hyperedgerendering) | [VertexCoordinateRules](#vertexcoordinaterules) | [VertexLabels](#vertexlabels) | [VertexSize and "ArrowheadLength"](#vertexsize-and-arrowheadlength) | ["MaxImageSize"](#maximagesize) | [Style Options](#style-options) | [Graphics Options](#graphics-options)
Expand Down
Binary file added READMEImages/TwoIdentifiedMatchOrders.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added READMEImages/TwoIsomorphicMatchOrders.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added READMEImages/TwoMatchOrdersSameOutcome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion Tests/WolframModel.wlt
Original file line number Diff line number Diff line change
Expand Up @@ -1611,7 +1611,8 @@
{message}
]
] @@@ Flatten /@ Tuples[{{{"EventOrderingFunction", WolframModel::invalidEventOrdering},
{"EventSelectionFunction", WolframModel::invalidEventSelection}},
{"EventSelectionFunction", WolframModel::invalidParameterValue},
{"EventDeduplication", WolframModel::invalidParameterValue}},
{"$$$invalid$$$", $$$invalid$$$, 1},
{Automatic, "Symbolic"}}],

Expand Down
125 changes: 125 additions & 0 deletions Tests/eventDeduplication.wlt
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<|
"eventDeduplication" -> <|
"init" -> (
Attributes[Global`testUnevaluated] = {HoldAll};
Global`testUnevaluated[args___] := SetReplace`PackageScope`testUnevaluated[VerificationTest, args];
),
"tests" -> {
(* No deduplication is the default *)
VerificationTest[
Options[WolframModel, "EventDeduplication"],
{"EventDeduplication" -> None}
],

(* Deduplication requires Method -> "LowLevel" *)
testUnevaluated[
WolframModel[{1 -> 2, 1 -> 3}, {1}, "EventDeduplication" -> "SameInputSetIsomorphicOutputs", Method -> #1],
#2
] & @@@ {{Automatic, WolframModel::symbNotImplemented}, {"Symbolic", WolframModel::symbOrdering}},

(* Non-symmetric case *)
VerificationTest[
WolframModel[{{1, 2}, {1, 3}} -> {{2, 3}},
{{1, 2}, {1, 3}},
1,
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> "SameInputSetIsomorphicOutputs"],
WolframModel[{{1, 2}, {1, 3}} -> {{2, 3}}, {{1, 2}, {1, 3}}, 1, "EventSelectionFunction" -> "MultiwaySpacelike"]
],

(* Symmetric case *)
VerificationTest[
WolframModel[{{1, 2}, {1, 3}} -> {{2, 3}, {3, 2}},
{{1, 2}, {1, 3}},
1,
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> "SameInputSetIsomorphicOutputs"]["EventsList"],
{{1, {1, 2} -> {3, 4}}}
],

(* System that becomes non-overlapping *)
VerificationTest[
WolframModel[{{1, 2}, {2, 1}} -> {{1, 3}, {3, 1}, {3, 2}, {2, 3}},
{{1, 2}, {2, 1}},
3,
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> #1]["EventsCount"],
#2
] & @@@ {{None, 42}, {"SameInputSetIsomorphicOutputs", 7}},

VerificationTest[
WolframModel[{{1, 2}, {2, 1}} -> {{1, 3}, {3, 1}, {3, 2}, {2, 3}},
{{1, 2}, {2, 1}},
3,
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> "SameInputSetIsomorphicOutputs"]["FinalState"],
{{1, 6}, {6, 1}, {6, 4}, {4, 6}, {4, 7}, {7, 4}, {7, 3}, {3, 7}, {3, 8}, {8, 3}, {8, 5}, {5, 8}, {5, 9}, {9, 5},
{9, 2}, {2, 9}}
],

(* Neat examples *)
VerificationTest[
WolframModel[{{x, y}, {x, z}} -> {{x, y}, {x, w}, {y, w}, {z, w}},
{{0, 0}, {0, 0}},
3,
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> None]["EventsCount"] >
WolframModel[{{x, y}, {x, z}} -> {{x, y}, {x, w}, {y, w}, {z, w}},
{{0, 0}, {0, 0}},
3,
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> "SameInputSetIsomorphicOutputs"]["EventsCount"]
],

(* Tests for correct isomorphism *)
VerificationTest[
WolframModel[#1,
#2,
#3,
"EventSelectionFunction" -> "MultiwaySpacelike",
"EventDeduplication" -> "SameInputSetIsomorphicOutputs"]["EventsCount"],
#4
] & @@@ {
(* Symmetric case *)
{{{1, 2}, {1, 3}} -> {{2, 3}, {3, 2}}, {{1, 2}, {1, 3}}, 1, 1},
(* Nontrivial isomorphism *)
{{{1, 2}, {1, 3}} -> {{2, 4}, {4, 5}, {5, 3}, {3, 6}, {6, 2}}, {{1, 2}, {1, 3}}, 1, 2},
{{{1, 2}, {1, 3}} -> {{2, 4}, {4, 3}, {3, 5}, {5, 2}}, {{1, 2}, {1, 3}}, 1, 1},
(* Different size subgraph-isomorphic outputs *)
{{{{1}} -> {{1, 2}}, {{1}} -> {{1, 2}, {1, 2}}}, {{1}}, 1, 2},
{{{{1}} -> {{1, 2}, {1, 2}}, {{1}} -> {{1, 2}}}, {{1}}, 1, 2},
(* Empty output *)
{{{1}, {1}} -> {}, {{1}, {1}}, 1, 1},
{{{1}, {1}} -> {}, {{1}, {1}, {1}}, 1, 3},
{{{{1}, {1}} -> {}, {{1}, {1}} -> {}}, {{1}, {1}, {1}}, 1, 3},
{{{{1}, {1}} -> {}, {{1}, {1}, {1}} -> {}}, {{1}, {1}, {1}}, 1, 4},
(* Neat examples *)
{{{1}, {1}} -> {{1}, {1}}, {{1}, {1}, {1}}, 2, 12},
{{{0, 1}, {0, 6}} -> {{1, 4}, {2, 3}, {2, 4}, {3, 4}, {3, 5}, {4, 5}, {6, 4}}, {{0, 1}, {0, 6}}, 1, 1},
{{{0, 1}, {0, 6}} -> {{1, 4}, {2, 3}, {2, 4}, {3, 4}, {3, 5}, {4, 5}, {4, 6}}, {{0, 1}, {0, 6}}, 1, 2},
(* Pattern rules *)
{<|"PatternRules" -> {{0, a_}, {0, b_}} :> Module[{c, d}, {{a, c}, {b, d}}]|>, {{0, 1}, {0, 2}}, 1, 1},
{<|"PatternRules" -> {{0, a_}, {0, b_}} :> {{a, 1}, {b, 2}}|>, {{0, 1}, {0, 2}}, 1, 2},
{<|"PatternRules" -> {{0, a_}, {0, b_}} :> {{a, 3}, {b, 4}}|>, {{0, 1}, {0, 2}}, 1, 2},
{<|"PatternRules" -> {{0, a_}, {0, b_}} :> Module[{c, d}, {{a, 1}, {b, 2}}]|>, {{0, 1}, {0, 2}}, 1, 2}
},

(* Correct weights in random evolution *)
VerificationTest[
Count[Table[First[WolframModel[{{{1}, {1}, {1}, {1}} -> {}, {{1}} -> {}},
{{1}, {1}, {1}, {1}},
<|"MaxEvents" -> 1|>,
"EventOrderingFunction" -> {}]["AllEventsRuleIndices"]], 1000], 2] < 200
],

VerificationTest[
Count[Table[First[WolframModel[{{{1}, {1}, {1}, {1}} -> {}, {{1}} -> {}},
{{1}, {1}, {1}, {1}},
<|"MaxEvents" -> 1|>,
"EventOrderingFunction" -> {},
"EventDeduplication" -> "SameInputSetIsomorphicOutputs"][
"AllEventsRuleIndices"]], 1000], 1] < 300
]
}
|>
|>
Loading

0 comments on commit f7715de

Please sign in to comment.