-
Notifications
You must be signed in to change notification settings - Fork 9
Add "undecidable" truth value #235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 11 commits
ca95f9d
a51f0aa
0cc5315
21b8a65
93c0afb
ec92e84
7951e70
fe48b02
2fbf904
b85e7d5
6a28cf2
2edfb57
bde80be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,16 +3,18 @@ import { parse as _parse } from './Formula/Grammar.js' | |
|
|
||
| import { union } from './Util.js' | ||
|
|
||
| export type TruthValue = boolean | 'undecidable' | ||
|
|
||
| type F<P> = | ||
| | { kind: 'atom'; property: P; value: boolean } | ||
| | { kind: 'atom'; property: P; value: TruthValue } | ||
| | { kind: 'or'; subs: F<P>[] } | ||
| | { kind: 'and'; subs: F<P>[] } | ||
|
|
||
| function atomSchema<P>(p: z.ZodSchema<P>): z.ZodSchema<F<P>> { | ||
| return z.object({ | ||
| kind: z.literal('atom'), | ||
| property: p, | ||
| value: z.boolean(), | ||
| value: z.union([z.boolean(), z.literal('undecidable')]), | ||
| }) as any | ||
| } | ||
|
|
||
|
|
@@ -34,7 +36,7 @@ export const formulaSchema = <P>(p: z.ZodSchema<P>): z.ZodSchema<F<P>> => | |
| export interface Atom<P, X = never> { | ||
| kind: 'atom' | ||
| property: P | ||
| value: boolean | X | ||
| value: TruthValue | X | ||
| } | ||
|
|
||
| export interface And<P, X = never> { | ||
|
|
@@ -59,7 +61,7 @@ export function or<P, X = never>(...subs: Formula<P, X>[]): Or<P, X> { | |
|
|
||
| export function atom<P, X = never>( | ||
| property: P, | ||
| value: boolean | X = true, | ||
| value: TruthValue | X = true, | ||
| ): Atom<P, X> { | ||
| return { kind: 'atom', property, value } | ||
| } | ||
|
|
@@ -78,6 +80,7 @@ export function render<T>(f: Formula<T>, term: (t: T) => string): string { | |
| case 'atom': | ||
| // eslint-disable-next-line no-case-declarations | ||
| const name = term(f.property) | ||
| if (f.value === 'undecidable') return '⊬' + name | ||
| return f.value ? name : '¬' + name | ||
| case 'and': | ||
| return '(' + f.subs.map(sf => render(sf, term)).join(' ∧ ') + ')' | ||
|
|
@@ -86,9 +89,21 @@ export function render<T>(f: Formula<T>, term: (t: T) => string): string { | |
| } | ||
| } | ||
|
|
||
| export function hasUndecidable<P, X = never>(f: Formula<P, X>): boolean { | ||
| switch (f.kind) { | ||
| case 'atom': | ||
| return f.value === 'undecidable' | ||
| default: | ||
| return f.subs.some(hasUndecidable) | ||
| } | ||
| } | ||
|
|
||
| export function negate<P>(formula: Formula<P>): Formula<P> { | ||
| switch (formula.kind) { | ||
| case 'atom': | ||
| if (formula.value === 'undecidable') { | ||
| throw new Error('Cannot negate an undecidable atom') | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think anything else in the deduction engine throws errors...changing this may be a good idea? then again there are things that should never happen and this is one of them. |
||
| } | ||
| return atom(formula.property, !formula.value) | ||
| case 'and': | ||
| return or(...formula.subs.map(negate)) | ||
|
|
@@ -130,7 +145,7 @@ export function compact<P, X>( | |
|
|
||
| export function evaluate<T, V extends boolean | null = boolean>( | ||
| f: Formula<T, V>, | ||
| traits: Map<T, boolean>, | ||
| traits: Map<T, TruthValue>, | ||
| ): boolean | undefined { | ||
| let result: boolean | undefined | ||
|
|
||
|
|
@@ -192,8 +207,8 @@ type Serialized<X = never> = | |
| | { and: Serialized[] } | ||
| | { or: Serialized[] } | ||
| | { inner: Serialized } | ||
| | { property: string; value: boolean | X } | ||
| | Record<string, boolean | X> | ||
| | { property: string; value: TruthValue | X } | ||
| | Record<string, TruthValue | X> | ||
|
|
||
| export function fromJSON(json: Serialized): Formula<string, null> { | ||
| if ('and' in json && typeof json.and === 'object') { | ||
|
|
@@ -211,7 +226,7 @@ export function fromJSON(json: Serialized): Formula<string, null> { | |
| throw `cannot cast object with ${entries.length} keys to atom` | ||
| } | ||
|
|
||
| if (typeof entries[0][1] !== 'boolean') { | ||
| if (typeof entries[0][1] !== 'boolean' && entries[0][1] !== 'undecidable') { | ||
| throw `cannot cast object with non-boolean value` | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| import { | ||
| type TruthValue, | ||
| And, | ||
| Atom, | ||
| Formula, | ||
| Or, | ||
| evaluate, | ||
| hasUndecidable, | ||
| negate, | ||
| properties, | ||
| } from '../Formula.js' | ||
|
|
@@ -35,24 +37,28 @@ export default class Prover< | |
| PropertyId | ||
| >, | ||
| > { | ||
| private traits: Map<PropertyId, boolean> | ||
| private traits: Map<PropertyId, TruthValue> | ||
| private derivations: Derivations<TheoremId, PropertyId> | ||
|
|
||
| private queue: Queue<TheoremId, PropertyId, Theorem> | ||
|
|
||
| constructor( | ||
| implications: ImplicationIndex<TheoremId, PropertyId, Theorem>, | ||
| traits = new Map<PropertyId, boolean>(), | ||
| traits = new Map<PropertyId, TruthValue>(), | ||
| ) { | ||
| this.traits = traits | ||
| this.derivations = new Derivations([...traits.keys()]) | ||
| this.queue = new Queue(implications) | ||
|
|
||
| traits.forEach((_: boolean, id: PropertyId) => { | ||
| traits.forEach((_: TruthValue, id: PropertyId) => { | ||
| this.queue.mark(id) | ||
| }) | ||
| } | ||
|
|
||
| enqueue(theorems: Theorem[]): void { | ||
| this.queue.addAll(theorems) | ||
| } | ||
|
|
||
| run(): Result<TheoremId, PropertyId> { | ||
| let theorem | ||
| while ((theorem = this.queue.shift())) { | ||
|
|
@@ -88,14 +94,14 @@ export default class Prover< | |
| const av = evaluate(a, this.traits) | ||
| const cv = evaluate(c, this.traits) | ||
|
|
||
| if (av === true && cv === false) { | ||
| if (av === true && cv === false && !hasUndecidable(c)) { | ||
| return this.contradiction(implication.id, [ | ||
| ...properties(a), | ||
| ...properties(c), | ||
| ]) | ||
| } else if (av === true) { | ||
| return this.force(implication.id, c, [...properties(a)]) | ||
| } else if (cv === false) { | ||
| } else if (cv === false && !hasUndecidable(a) && !hasUndecidable(c)) { | ||
| return this.force(implication.id, negate(a), [...properties(c)]) | ||
| } | ||
| } | ||
|
|
@@ -114,16 +120,39 @@ export default class Prover< | |
| ): Contradiction<TheoremId, PropertyId> | undefined { | ||
| const property = formula.property | ||
|
|
||
| if (this.traits.has(property)) { | ||
| if (this.traits.get(property) !== formula.value) { | ||
| return this.contradiction(theorem, [...support, property]) | ||
| } else { | ||
| const existing = this.traits.get(property) | ||
|
|
||
| if (existing !== undefined) { | ||
| if (existing === formula.value) { | ||
| return | ||
| } | ||
| // undecidable has lower priority: true/false overrides it | ||
| if (existing === 'undecidable' && formula.value !== 'undecidable') { | ||
| this.traits.set(property, formula.value as TruthValue) | ||
| this.derivations.overrideGiven(property) | ||
| this.derivations.addEvidence( | ||
| property, | ||
| formula.value as TruthValue, | ||
| theorem, | ||
| support, | ||
| ) | ||
| this.queue.mark(property) | ||
| return | ||
| } | ||
| // don't let undecidable override a definite value | ||
| if (formula.value === 'undecidable') { | ||
| return | ||
| } | ||
| return this.contradiction(theorem, [...support, property]) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now this looks sketchy. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or maybe not actually? |
||
| } | ||
|
|
||
| this.traits.set(property, formula.value) | ||
| this.derivations.addEvidence(property, formula.value, theorem, support) | ||
| this.traits.set(property, formula.value as TruthValue) | ||
| this.derivations.addEvidence( | ||
| property, | ||
| formula.value as TruthValue, | ||
| theorem, | ||
| support, | ||
| ) | ||
| this.queue.mark(property) | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cf. #200 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this fixes the issue if I understand correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I means is, currently the CI/CD of pi-base already has other errors.
(Because currently pi-base/data is at a invalid state, but CI/CD still builds)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relatedly, the fact that CI/CD builds at all for the
undecidable-databranch is amazing, considering it has values that the current pi base compiler considers invalid. So if you go to the pi base website and loadunprovable-data, it just ignores Felix's changes that it considers invalid, but otherwise works normally.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One possible reason is that pi-base CI/CD use a docker that is not up to date.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, I can confirm that the change of #171 is not applied at all, because description in pi-base now is not trimmed.