-
Notifications
You must be signed in to change notification settings - Fork 2
/
test_procedures.py
408 lines (338 loc) · 15.8 KB
/
test_procedures.py
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
from copy import deepcopy
import logging
import pytest
from nettlesome.terms import ContextRegister, Explanation, TermSequence
from nettlesome.entities import Entity
from nettlesome.groups import FactorGroup
from nettlesome.predicates import Predicate
from nettlesome.quantities import Comparison, Q_
from authorityspoke.facts import Fact
from authorityspoke.procedures import Procedure
class TestProcedures:
def test_exception_for_wrong_type_for_procedure(self, make_predicate):
with pytest.raises(TypeError):
Procedure(inputs=make_predicate["p1"], outputs=make_predicate["p2"])
def test_exception_for_wrong_type_in_tuple_for_procedure(self, make_predicate):
with pytest.raises(TypeError):
Procedure(inputs=(make_predicate["p1"]), outputs=(make_predicate["p2"]))
def test_get_terms(self, make_procedure):
# motel, watt
assert len(make_procedure["c1"].generic_terms()) == 2
# trees, motel
assert len(make_procedure["c2"].generic_terms()) == 2
def test_procedure_length(self, make_procedure):
"""Consider deleting Procedure.__len__() and this test."""
assert len(make_procedure["c2"]) == 2
assert len(make_procedure["c1"]) == 2
def test_procedure_string_with_entities(self, make_procedure):
assert "<Craig> performed at <circus>" in str(
make_procedure["c2_irrelevant_inputs"]
)
assert "<Dan> performed at <circus>" in str(
make_procedure["c2_irrelevant_inputs"]
)
def test_cannot_add_nonfactor_as_input(self, make_factor):
with pytest.raises(TypeError):
Procedure(inputs="factor name", outputs=make_factor["f_shooting"])
def test_cannot_add_term_as_input(self, make_factor):
with pytest.raises(TypeError):
Procedure(inputs=Entity("Al"), outputs=make_factor["f_shooting"])
def test_generic_terms(self, make_entity, make_procedure, make_evidence):
"""
Finds that for factor f["f7"], it would be consistent with the
other group of factors for f["f7"]'s two slots to be assigned
(0, 1) or (1, 0).
"""
e = make_entity
factors = make_procedure["c3"].generic_terms()
for factor in (
e["motel"],
e["tree_search"],
e["trees"],
e["watt"],
make_evidence["crime_absent"],
):
assert factor in factors
def test_type_of_terms(self, make_procedure):
assert isinstance(make_procedure["c3"].terms, TermSequence)
def test_repr(self, make_procedure):
rep = repr(make_procedure["c3"])
assert "to_effect=Fact(predicate=Predicate(template='$person committed" in rep
def test_entities_of_inputs_for_identical_procedure(
self, watt_factor, make_procedure, watt_mentioned
):
f = watt_factor
c1 = make_procedure["c1"]
c1_again = make_procedure["c1_again"]
assert f["f1"] in c1.inputs
assert f["f1"] in c1_again.inputs
assert f["f1"].terms == (watt_mentioned[0],)
assert f["f2"] in c1.inputs
assert f["f2"] in c1_again.inputs
assert f["f2"].terms == (watt_mentioned[1], watt_mentioned[0])
class TestProcedureSameMeaning:
def test_procedure_equality(self, make_procedure):
assert make_procedure["c1"].means(make_procedure["c1_again"])
def test_procedure_equality_entity_order(self, make_procedure):
assert make_procedure["c1"].means(make_procedure["c1_entity_order"])
def test_still_equal_after_swapping_reciprocal_entities(
self, make_procedure, caplog
):
caplog.set_level(logging.DEBUG)
assert make_procedure["c2"].means(make_procedure["c2_reciprocal_swap"])
def test_unequal_after_swapping_nonreciprocal_entities(self, make_procedure):
assert not make_procedure["c2"].means(make_procedure["c2_nonreciprocal_swap"])
def test_same_meaning_no_context(self, make_procedure):
assert make_procedure["c_no_context"].means(make_procedure["c_no_context"])
def test_explain_same_meaning(self, make_procedure):
left = make_procedure["c1_factor_and_entity_order"]
right = make_procedure["c1"]
explanation = left.explain_same_meaning(right)
assert "<Wattenburg> is like <Hideaway Lodge>" in str(explanation)
assert "<Hideaway Lodge> is like <Wattenburg>" in str(explanation)
class TestProcedureImplication:
def test_entities_of_implied_inputs_for_implied_procedure(
self, watt_factor, make_procedure
):
f = watt_factor
c1_easy = make_procedure["c1_easy"]
c1_order = make_procedure["c1_entity_order"]
assert f["f2"] in c1_easy.inputs
assert f["f1"] not in c1_easy.inputs
def test_factor_implication_with_exact_quantity(self, watt_factor, make_procedure):
"""This test is mostly to demonstrate the relationships
between the Factors in the Procedures that will be
tested below."""
f = watt_factor
assert f["f7"] in make_procedure["c2"].inputs
assert f["f7"] not in make_procedure["c2_exact_quantity"].inputs
assert f["f8_exact"] in make_procedure["c2_exact_quantity"].inputs
assert f["f8_exact"] > f["f7"]
assert not f["f7"] >= f["f8_exact"]
def test_procedure_implication_with_exact_quantity(self, make_procedure):
"""This is meant to show that the function finds the "distance is
exactly 25" factor in c2_exact, and recognizes that factor can imply
the "distance is more than 20" factor in c2 if they have the same entities.
"""
c2 = make_procedure["c2"]
c2_exact_quantity = make_procedure["c2_exact_quantity"]
assert c2_exact_quantity.implies(c2)
def test_procedure_general_quantity_does_not_imply_exact(
self, watt_factor, make_procedure
):
c2 = make_procedure["c2"]
c2_exact_quantity = make_procedure["c2_exact_quantity"]
assert not c2_exact_quantity <= c2
def test_implied_procedure_with_reciprocal_entities(self, make_procedure):
"""
Because both procedures have a form of "the distance between $place1 and $place2 was"
factor and those factors are reciprocal, the entities of one of them in reversed
order can be used as the entities of the other, and one will still imply the other.
(But if there had been more than two entities, only the first two would have been
reversed.)
"""
c2 = make_procedure["c2"]
c2_reciprocal_swap = make_procedure["c2_reciprocal_swap"]
assert c2.means(c2_reciprocal_swap)
assert c2 >= c2_reciprocal_swap
def test_entities_of_implied_quantity_outputs_for_implied_procedure(
self, make_procedure
):
"""
If c2_narrow was "self" and c2_broad was "other", the output of
c2_broad (with f["f8_int"]) would be implied by the output of
c2_narrow (with f["f8_higher_int"]).
"""
c2_broad = make_procedure["c2_broad_output"]
c2_narrow = make_procedure["c2_narrow_output"]
assert c2_narrow > c2_broad
def test_procedure_implies_identical_procedure(self, make_procedure):
assert make_procedure["c1"] >= make_procedure["c1_again"]
assert make_procedure["c1"].means(make_procedure["c1_again"])
def test_procedure_implies_same_procedure_fewer_inputs(
self, make_procedure, caplog
):
caplog.set_level(logging.DEBUG)
assert make_procedure["c1_easy"] < make_procedure["c1"]
assert make_procedure["c1_easy"] <= make_procedure["c1"]
assert make_procedure["c1_easy"] != make_procedure["c1"]
def test_procedure_implies_reordered_entities_fewer_inputs(self, make_procedure):
assert make_procedure["c1_entity_order"] > make_procedure["c1_easy"]
assert make_procedure["c1_easy"] < make_procedure["c1_entity_order"]
assert make_procedure["c1_easy"] != make_procedure["c1_entity_order"]
def test_procedure_exact_quantity_in_despite_implication(self, make_procedure):
assert make_procedure["c2_exact_in_despite"] > make_procedure["c2"]
def test_procedure_implication_despite_irrelevant_factors(self, make_procedure):
assert make_procedure["c2"] < make_procedure["c2_irrelevant_inputs"]
def test_implies_all_to_some(self, make_procedure):
assert make_procedure["c2"].implies_all_to_some(
make_procedure["c2_irrelevant_despite"]
)
assert not make_procedure["c2"] > make_procedure["c2_irrelevant_despite"]
def test_all_to_some_implies_input_same_as_despite_of_other(self, make_procedure):
"""
Every input of c2_exact_in_despite is equal to or implied by
some input of c2, and an input of c2 implies the despite of c2_exact_in_despite.
"""
p = make_procedure
assert p["c2_exact_in_despite"].implies_all_to_some(p["c2"])
def test_no_all_to_some_implies_input_contradicts_despite(self, make_procedure):
"""
c2_higher_quantity has the right inputs, but it also has an
input that contradicts the despite factor of c2_exact_in_despite.
"""
p = make_procedure
assert not p["c2_higher_quantity"].implies_all_to_some(p["c2_exact_in_despite"])
def test_all_to_some_implication_added_despite_factors(self, make_procedure):
assert not make_procedure["c2"].implies_all_to_some(
make_procedure["c2_absent_despite"]
)
def test_implication_with_more_outputs_than_inputs(self, make_procedure):
p = make_procedure
assert p["c2_irrelevant_outputs"].implies_all_to_all(p["c2"])
def test_fewer_inputs_implies_all_to_all(self, make_procedure):
c = make_procedure
assert c["c3_fewer_inputs"].implies_all_to_all(c["c3"])
def test_all_to_all_implies_reciprocal(self, make_procedure, caplog):
"""
These are the same Procedures below in
test_implication_all_to_all_reciprocal
"""
caplog.set_level(logging.DEBUG)
assert make_procedure["c2_exact_in_despite_entity_order"].implies_all_to_all(
make_procedure["c2"]
)
def test_no_implication_of_other_factor(self, make_procedure, watt_factor):
assert not make_procedure["c1"].implies_all_to_all(watt_factor["f1"])
assert not make_procedure["c1"].implies_all_to_some(watt_factor["f1"])
class TestProcedureContradiction:
def test_no_contradict_between_procedures(self, make_procedure):
"""
I don't think some-to-some contradiction is possible for Procedures
"""
p = make_procedure
with pytest.raises(NotImplementedError):
assert p["c2_higher_quantity"].contradicts(p["c2_exact_in_despite"])
def test_no_contradiction_of_other_type(self, make_procedure, watt_factor):
assert not make_procedure["c1"].contradicts_some_to_all(watt_factor["f1"])
def test_contradiction_some_to_all(self, watt_factor):
within_curtilage = Procedure(
inputs=(watt_factor["f9"],),
outputs=watt_factor["f10"],
)
not_within_curtilage = Procedure(
inputs=(watt_factor["f9"],),
outputs=watt_factor["f10_false"],
)
assert not_within_curtilage.contradicts_some_to_all(within_curtilage)
class TestProcedureUnion:
def test_simple_union(self, make_opinion_with_holding):
feist = make_opinion_with_holding["feist_majority"]
procedure_from_union = feist.holdings[0].procedure | feist.holdings[2].procedure
procedure_from_adding = (
feist.holdings[0].procedure + feist.holdings[2].procedure.inputs[0]
)
assert procedure_from_union.means(procedure_from_adding)
p_small_weight = Comparison(
"the amount of gold $person possessed was", sign="<", expression=Q_("1 gram")
)
p_large_weight = Comparison(
"the amount of gold $person possessed was",
sign=">=",
expression=Q_("100 kilograms"),
)
alice = Entity("Alice")
bob = Entity("Bob")
craig = Entity("Craig")
dan = Entity("Dan")
alice_rich = Fact(p_large_weight, terms=alice)
bob_poor = Fact(p_small_weight, terms=bob)
craig_rich = Fact(p_large_weight, terms=craig)
dan_poor = Fact(p_small_weight, terms=dan)
class TestFactorGroups:
def test_consistent_factor_groups(self):
"""
Verifies that the factor groups are considered "consistent"
even though it would be possible to make an analogy that would
make the statements contradict.
"""
assert FactorGroup([alice_rich, bob_poor]).consistent_with(
FactorGroup([dan_poor, craig_rich])
)
def test_consistent_factor_groups_with_context(self):
alice_like_craig = ContextRegister()
alice_like_craig.insert_pair(alice, craig)
assert FactorGroup([alice_rich, bob_poor]).consistent_with(
FactorGroup([dan_poor, craig_rich]),
context=alice_like_craig,
)
def test_context_indicates_factors_match(self):
alice_like_craig = ContextRegister()
alice_like_craig.insert_pair(alice, craig)
assert alice_rich.means(craig_rich, context=alice_like_craig)
def test_inconsistent_factor_groups(self):
"""
If Alice is considered analagous to Dan the two sets of
statements would be inconsistent, but not if
Alice is considered analagous to Craig.
"""
alice_like_dan = ContextRegister()
alice_like_dan.insert_pair(alice, dan)
assert not FactorGroup([alice_rich, bob_poor]).consistent_with(
FactorGroup([dan_poor, craig_rich]), context=alice_like_dan
)
def test_contradictory_factor_groups(self):
"""
Verifies that the factor groups are considered "consistent"
even though it would be possible to make an analogy that would
make the statements contradict.
If Alice is considered analagous to Dan the two sets of
statements would be contradictory, but not if
Alice is considered analagous to Craig.
"""
assert FactorGroup([alice_rich, bob_poor]).contradicts(
FactorGroup([craig_rich, dan_poor])
)
def test_not_contradictory_factor_groups(self):
"""
Because the ContextRegister matches up the two contexts
consistently, it's impossible to reach a contradiction.
"""
alice_like_craig = ContextRegister()
alice_like_craig.insert_pair(alice, craig)
assert not FactorGroup((alice_rich, bob_poor)).contradicts(
FactorGroup((dan_poor, craig_rich)),
context=alice_like_craig,
)
def test_input_or_despite_factors(self):
"""Test part of the process of checking contradiction."""
rural = Entity(name="Rural's telephone directory")
compilation = Predicate(
"${rural_s_telephone_directory} was a compilation of facts"
)
idea = Predicate(content="${rural_s_telephone_directory} was an idea")
copyrightable = Fact(
Predicate(content="${rural_s_telephone_directory} was copyrightable"),
terms=rural,
)
left = Procedure(
outputs=(copyrightable),
inputs=(Fact(compilation, terms=rural)),
)
right = Procedure(
outputs=(copyrightable),
inputs=(Fact(idea, terms=rural)),
)
context = ContextRegister()
explanation = Explanation.from_context(context)
gen = left._has_input_or_despite_factors_implied_by_all_inputs_of(
right, explanation
)
with pytest.raises(StopIteration):
next(gen)
class TestEvolve:
def test_evolve_context_to_absent(self, make_procedure):
procedure = make_procedure["c1"]
evolved = deepcopy(procedure)
evolved.outputs[0].absent = True
assert procedure.outputs[0].contradicts(evolved.outputs[0])