-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathA1$GenerateMultihistory.m
More file actions
232 lines (188 loc) · 11.9 KB
/
Copy pathA1$GenerateMultihistory.m
File metadata and controls
232 lines (188 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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}, ...|> *)
$tokenDeduplications = CreateDataStructure["HashTable"]; (* generator -> tokenDeduplication |>*)
$possibleConstraints = None | "NonNegativeIntegerOrInfinity" | "PositiveNumberOrInfinity" | _List;
$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 | -_String) ...},
stoppingConditionSpec : $constraintsSpecPattern,
tokenDeduplication : (None | _List)] := (
$implementations["Insert", systemType -> implementationFunction];
$eventSelectionSpecs["Insert", systemType -> eventSelectionSpec];
$eventOrderings["Insert", systemType -> eventOrderings];
$stoppingConditionSpecs["Insert", systemType -> stoppingConditionSpec];
$tokenDeduplications["Insert", systemType -> tokenDeduplication];
);
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[$tokenDeduplications["Lookup", Head[system]]][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]|>]];
parseTokenDeduplication[None][None] := None;
parseTokenDeduplication[supportedDeduplications_][argument_] /; MemberQ[supportedDeduplications, argument] := argument;
declareMessage[
General::invalidTokenDeduplication, "Token deduplication spec `argument` in `expr` can only be one of `choices`."];
parseTokenDeduplication[supportedDeduplications_][argument_] :=
throw[Failure["invalidTokenDeduplication", <|"argument" -> argument, "choices" -> supportedDeduplications|>]];
parseEventOrdering[supportedOrderings_][argument_List] /; SubsetQ[supportedOrderings, argument] := argument;
declareMessage[
General::invalidEventOrdering, "Event ordering spec `argument` in `expr` should be a List of values from `choices`."];
parseEventOrdering[supportedOrderings_][argument_] :=
throw[Failure["invalidEventOrdering", <|"argument" -> argument, "choices" -> supportedOrderings|>]];
checkParameter[_, None][value_] := value;
checkParameter[_, choices_List][value_] /; MemberQ[choices, value] := value;
declareMessage[General::invalidChoiceParameter,
"Parameter `name` in `expr` can only be one of `choices`."];
checkParameter[name_, choices_List][_] :=
throw[Failure["invalidChoiceParameter", <|"name" -> name, "choices" -> choices|>]];
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|>]];
checkParameter[_, "PositiveNumberOrInfinity"][value : _ ? (# > 0 &)] := value;
declareMessage[General::notPositiveNumberOrInfinityParameter,
"Parameter `name` in `expr` is expected to be a positive machine-sized number or Infinity."];
checkParameter[name_, "PositiveNumberOrInfinity"][_] :=
throw[Failure["notPositiveNumberOrInfinityParameter", <|"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|>]];