Skip to content

Commit 919805f

Browse files
committed
Soundness of natural deduction for first-order logic
1 parent f14cf4b commit 919805f

File tree

8 files changed

+309
-82
lines changed

8 files changed

+309
-82
lines changed

src/notebook/math/logic/deduction/proof_tree.py

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -201,22 +201,63 @@ def __str__(self) -> str:
201201
return self.build_renderer().render()
202202

203203

204-
def apply( # noqa: C901,PLR0915
204+
def _infer_application_instantiation(
205205
rule: NaturalDeductionRule,
206-
*args: ProofTree | RuleApplicationPremiseConfig,
206+
application_premises: Sequence[RuleApplicationPremiseConfig],
207207
instantiation: AtomicLogicSchemaInstantiation | None = None,
208208
implicit: Mapping[FormulaPlaceholder, Formula] | None = None,
209209
conclusion_config: FormulaWithSubstitution | None = None
210-
) -> RuleApplicationTree:
211-
if len(args) != len(rule.premises):
212-
raise RuleApplicationError(f'The rule {rule.name} has {len(rule.premises)} premises, but the application has {len(args)}')
213-
210+
) -> AtomicLogicSchemaInstantiation:
214211
if instantiation is None:
215212
instantiation = AtomicLogicSchemaInstantiation(formula_mapping=implicit)
216213
elif implicit:
217214
raise RuleApplicationError('Cannot provide both an instantiation and implicit assumptions')
218215

219-
marker_map = dict[Marker, Formula]()
216+
# Infer instantiation
217+
for rule_premise, application_premise in zip(rule.premises, application_premises, strict=True):
218+
for attachment_schema, attachment in zip(rule_premise.attachments, application_premise.attachments, strict=True):
219+
if attachment:
220+
instantiation |= infer_instantiation_from_formula_substitution_spec(
221+
attachment_schema,
222+
attachment.payload
223+
)
224+
225+
if application_premise.main:
226+
if rule_premise.main.sub is None:
227+
instantiation |= infer_instantiation_from_formula(
228+
rule_premise.main.formula,
229+
evaluate_substitution(application_premise.main),
230+
)
231+
else:
232+
instantiation |= infer_instantiation_from_formula_substitution_spec(
233+
rule_premise.main,
234+
application_premise.main,
235+
)
236+
237+
if rule.conclusion.main.sub:
238+
if conclusion_config is None:
239+
raise RuleApplicationError(f'The rule {rule.name} requires a conclusion with an explicit substitution')
240+
241+
instantiation |= infer_instantiation_from_formula_substitution_spec(rule.conclusion.main, conclusion_config)
242+
243+
for attachment_schema in rule.conclusion.attachments:
244+
try:
245+
instantiate_substitution(attachment_schema, instantiation)
246+
except SchemaInstantiationError as err:
247+
raise RuleApplicationError(f'Cannot instantiate the implicit premise {attachment_schema} of the rule {rule.name}') from err
248+
249+
return instantiation
250+
251+
252+
def apply( # noqa: C901
253+
rule: NaturalDeductionRule,
254+
*args: ProofTree | RuleApplicationPremiseConfig,
255+
instantiation: AtomicLogicSchemaInstantiation | None = None,
256+
implicit: Mapping[FormulaPlaceholder, Formula] | None = None,
257+
conclusion_config: FormulaWithSubstitution | None = None
258+
) -> RuleApplicationTree:
259+
if len(args) != len(rule.premises):
260+
raise RuleApplicationError(f'The rule {rule.name} has {len(rule.premises)} premises, but the application has {len(args)}')
220261

221262
application_premises = [
222263
premise_config(tree=premise_arg, attachments=[None] * len(rule_premise.attachments))
@@ -225,6 +266,17 @@ def apply( # noqa: C901,PLR0915
225266
for rule_premise, premise_arg in zip(rule.premises, args, strict=True)
226267
]
227268

269+
instantiation = _infer_application_instantiation(
270+
rule, application_premises, instantiation, implicit, conclusion_config
271+
)
272+
273+
marker_map = dict[Marker, Formula]()
274+
275+
if conclusion_config is None:
276+
conclusion_config = instantiate_substitution(rule.conclusion.main, instantiation)
277+
278+
conclusion = evaluate_substitution(conclusion_config)
279+
228280
for i, (rule_premise, application_premise) in enumerate(zip(rule.premises, application_premises, strict=True), start=1):
229281
for assumption in application_premise.tree.get_cumulative_assumptions():
230282
if assumption.marker is None:
@@ -245,6 +297,9 @@ def apply( # noqa: C901,PLR0915
245297
if not application_premise.main.sub.is_noop() and eigen in get_formula_free_variables(application_premise.main.formula):
246298
raise RuleApplicationError(f'The eigenvariable {eigen} of the unsubstituted premise conclusion {application_premise.main.formula} cannot be free in it')
247299

300+
if eigen in get_formula_free_variables(conclusion):
301+
raise RuleApplicationError(f'The attached formula eigenvariable {eigen} cannot be free in the conclusion {conclusion} of the rule {rule.name}')
302+
248303
if eigen in application_premise.tree.get_open_variables():
249304
raise RuleApplicationError(f'The eigenvariable {eigen} cannot be free in the derivation of {application_premise.tree.conclusion}')
250305

@@ -274,44 +329,11 @@ def apply( # noqa: C901,PLR0915
274329
if eigen in application_premise.tree.get_open_variables(discharged={attachment.marker}):
275330
raise RuleApplicationError(f'The eigenvariable {eigen} cannot be free in the derivation of {app_conclusion}, except possibly in {attachment.eval()}')
276331

277-
# Update instantiation
278-
instantiation |= infer_instantiation_from_formula_substitution_spec(
279-
attachment_schema,
280-
attachment.payload
281-
)
282-
283-
if application_premise.main:
284-
if rule_premise.main.sub is None:
285-
instantiation |= infer_instantiation_from_formula(
286-
rule_premise.main.formula,
287-
evaluate_substitution(application_premise.main),
288-
)
289-
else:
290-
instantiation |= infer_instantiation_from_formula_substitution_spec(
291-
rule_premise.main,
292-
application_premise.main,
293-
)
294-
295-
if rule.conclusion.main.sub:
296-
if conclusion_config is None:
297-
raise RuleApplicationError(f'The rule {rule.name} requires a conclusion with an explicit substitution')
298-
299-
instantiation |= infer_instantiation_from_formula_substitution_spec(rule.conclusion.main, conclusion_config)
300-
301-
for attachment_schema in rule.conclusion.attachments:
302-
try:
303-
instantiate_substitution(attachment_schema, instantiation)
304-
except SchemaInstantiationError as err:
305-
raise RuleApplicationError(f'Cannot instantiate the implicit premise {attachment_schema} of the rule {rule.name}') from err
306-
307-
if conclusion_config is None:
308-
conclusion_config = instantiate_substitution(rule.conclusion.main, instantiation)
309-
310332
return RuleApplicationTree(
311333
rule,
312334
instantiation,
313335
[premise.eval() for premise in application_premises],
314-
evaluate_substitution(conclusion_config),
336+
conclusion,
315337
get_term_variables(conclusion_config.sub.dest) if conclusion_config.sub else set()
316338
)
317339

text/first_order_logic.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,7 @@ \section{First-order logic}\label{sec:first_order_logic}
10021002
\end{definition}
10031003

10041004
\begin{definition}\label{def:fol_universal_validity}\mcite[105]{Hinman2005Logic}
1005-
If, for some \hyperref[def:fol_formula]{first-order formula} \( \varphi \) and some \hyperref[def:fol_structure]{first-order structure} \( \mscrX \), the \hyperref[alg:fol_formula_denotation]{denotation} \( \Bracks{ \varphi }_\mscrX^v \) equals \( \semtop \) for any variable assignment \( v \) in \( \mscrX \), we say that \( \varphi \) is \term[ru=общезначимая (формула) (\cite[def. 2.4.1]{Герасимов2014Вычислимость})]{universally valid} in \( \mscrX \).
1005+
If, in some \hyperref[def:fol_structure]{first-order structure} \( \mscrX \), every \hyperref[def:fol_variable_assignment]{variable assignment} \( v \) \hyperref[def:fol_semantics/satisfaction]{satisfies} the formula \( \varphi \), we say that \( \varphi \) is \term[ru=общезначимая (формула) (\cite[def. 2.4.1]{Герасимов2014Вычислимость})]{universally valid} in \( \mscrX \).
10061006
\end{definition}
10071007
\begin{comments}
10081008
\item \hyperref[def:closed_fol_formula]{Closed formulas} are universally valid, but the notion is more general --- see \cref{rem:implicit_quantification_and_deduction}.

text/first_order_models.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ \section{First-order models}\label{sec:first_order_models}
252252
\begin{proposition}\label{thm:functions_over_model_form_model}
253253
Let \( \Gamma \) to be a set of closed \hyperref[def:positive_formula]{positive formulas} \hi{without existential quantifiers}. Let \( \mscrX = (X, I) \) be a model of \( \Gamma \) and let \( A \) be a nonempty \hyperref[def:set]{plain set}, possibly unrelated to \( \mscrX \). Consider the set \( Y \coloneqq \fun(A, \mscrX) \) of \hyperref[def:function]{all set functions} from \( A \) to \( X \).
254254

255-
Define \( \iota: X \mapsto Y \) by sending each \( x \in X \) to the corresponding constant function in \( Y \).
255+
Define \( \iota: X \to Y \) by sending each \( x \in X \) to the corresponding constant function in \( Y \).
256256

257257
Define the interpretation \( J \) as follows:
258258
\begin{itemize}

0 commit comments

Comments
 (0)