diff --git a/DATABASE.md b/DATABASE.md index cbefe983b..5d991586c 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -33,7 +33,6 @@ Further tables are: - `special_morphisms` - `related_category_properties` - `category_comments` -- `category_property_comments` For functors there are similar tables, such as: diff --git a/databases/catdat/data/categories/FreeAb.yaml b/databases/catdat/data/categories/FreeAb.yaml index fdb0bd425..984e6e75f 100644 --- a/databases/catdat/data/categories/FreeAb.yaml +++ b/databases/catdat/data/categories/FreeAb.yaml @@ -57,9 +57,9 @@ unsatisfied_properties: - property: ℵ₁-filtered colimits reason: It is shown in MO/511426 that the $\aleph_1$-filtered diagram of countable subgroups of $\IZ^{\IN}$ does not have a colimit in $\FreeAb$. -category_property_comments: +undecidable_properties: - property: accessible - comment: The question if this category is accessible is undecidable in ZFC. See MSE/720885. + reason: The question if this category is accessible is undecidable in ZFC. See MSE/720885. special_objects: initial object: diff --git a/databases/catdat/data/categories/M-Set.yaml b/databases/catdat/data/categories/M-Set.yaml index 5a9185843..0ad479010 100644 --- a/databases/catdat/data/categories/M-Set.yaml +++ b/databases/catdat/data/categories/M-Set.yaml @@ -31,9 +31,9 @@ unsatisfied_properties: - property: trivial reason: This is trivial. -category_property_comments: +undecidable_properties: - property: semi-strongly connected - comment: If this category is semi-strongly connected depends on the choice of $M$. For $M = 1$ it is, for $M = \IZ$ it is not. In general, if $G$ is a group, then $G{-}\Set$ is semi-strongly connected if and only if for all subgroups $H,K \subseteq G$, $H$ is subconjugated to $K$ or $K$ is subconjugated to $H$. If $G$ is abelian, this means that the poset of subgroups is linear, in which case $G$ is either isomorphic to $\IZ/p^n$ or to $\IZ/p^{\infty}$ for a prime $p$. See also MSE/5129804. + reason: If this category is semi-strongly connected depends on the choice of $M$. For $M = 1$ it is, for $M = \IZ$ it is not. In general, if $G$ is a group, then $G{-}\Set$ is semi-strongly connected if and only if for all subgroups $H,K \subseteq G$, $H$ is subconjugated to $K$ or $K$ is subconjugated to $H$. If $G$ is abelian, this means that the poset of subgroups is linear, in which case $G$ is either isomorphic to $\IZ/p^n$ or to $\IZ/p^{\infty}$ for a prime $p$. See also MSE/5129804. special_objects: initial object: diff --git a/databases/catdat/schema/004_category-properties.sql b/databases/catdat/schema/004_category-properties.sql index 7714816da..f5eca8641 100644 --- a/databases/catdat/schema/004_category-properties.sql +++ b/databases/catdat/schema/004_category-properties.sql @@ -23,8 +23,9 @@ CREATE TABLE category_property_assignments ( id INTEGER PRIMARY KEY, category_id TEXT NOT NULL, property_id TEXT NOT NULL, - is_satisfied INTEGER NOT NULL - CHECK (is_satisfied in (TRUE, FALSE)), + is_satisfied INTEGER + -- we use NULL for undecidable properties + CHECK (is_satisfied in (TRUE, FALSE, NULL)), reason TEXT NOT NULL CHECK (length(reason) > 0), is_deduced INTEGER NOT NULL DEFAULT FALSE CHECK (is_deduced in (TRUE, FALSE)), diff --git a/databases/catdat/schema/010_functor-properties.sql b/databases/catdat/schema/010_functor-properties.sql index 256030aac..25aba0200 100644 --- a/databases/catdat/schema/010_functor-properties.sql +++ b/databases/catdat/schema/010_functor-properties.sql @@ -15,8 +15,9 @@ CREATE TABLE functor_property_assignments ( id INTEGER PRIMARY KEY, functor_id TEXT NOT NULL, property_id TEXT NOT NULL, - is_satisfied INTEGER NOT NULL - CHECK (is_satisfied IN (TRUE, FALSE)), + is_satisfied INTEGER + -- we use NULL for undecidable properties + CHECK (is_satisfied in (TRUE, FALSE, NULL)), reason TEXT NOT NULL CHECK (length(reason) > 0), is_deduced INTEGER NOT NULL DEFAULT FALSE CHECK (is_deduced in (TRUE, FALSE)), diff --git a/databases/catdat/schema/013_property_comments.sql b/databases/catdat/schema/013_property_comments.sql deleted file mode 100644 index 2bd07ac49..000000000 --- a/databases/catdat/schema/013_property_comments.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE category_property_comments ( - category_id TEXT NOT NULL, - property_id TEXT NOT NULL, - comment TEXT NOT NULL, - PRIMARY KEY (category_id, property_id), - FOREIGN KEY (category_id) REFERENCES categories (id) ON DELETE CASCADE, - FOREIGN KEY (property_id) REFERENCES category_properties (id) ON DELETE CASCADE -); \ No newline at end of file diff --git a/databases/catdat/scripts/deduce-entity-properties.ts b/databases/catdat/scripts/deduce-entity-properties.ts index 9e8d6c074..55d6e997e 100644 --- a/databases/catdat/scripts/deduce-entity-properties.ts +++ b/databases/catdat/scripts/deduce-entity-properties.ts @@ -299,15 +299,17 @@ function deduce_unsatisfied_properties( /** * Assign dual properties to dual entities: * If C has property P, then C^op has property P^op (if defined). - * Warning: This mutates the sets of satisfied and unsatisfied properties. + * Warning: This mutates the sets of assigned properties. */ function deduce_dual_properties( db: Database, entity: EntityMeta & { dual: string }, satisfied: Set, unsatisfied: Set, + undecidable: Set, dual_satisfied: Set, dual_unsatisfied: Set, + dual_undecidable: Set, properties_dict: Record, type: 'category', ) { @@ -329,14 +331,24 @@ function deduce_dual_properties( unsatisfied.add(p_dual) } + const new_undecidable = new Set() + + for (const p of dual_undecidable) { + const p_dual = properties_dict[p].dual + if (!p_dual || undecidable.has(p_dual)) continue + new_undecidable.add(p_dual) + undecidable.add(p_dual) + } + const property_insert = db.prepare(` INSERT INTO ${type}_property_assignments (${type}_id, property_id, is_satisfied, reason, is_deduced) VALUES (?, ?, ?, ?, TRUE) `) - const reason_satisfied = 'Its dual ${type} satisfies the dual property.' - const reason_unsatisfied = 'Its dual ${type} does not satisfy the dual property.' + const reason_satisfied = `Its dual ${type} satisfies the dual property.` + const reason_unsatisfied = `Its dual ${type} does not satisfy the dual property.` + const reason_undecidable = `The dual property is undecidable for its dual ${type}.` for (const p of new_satisfied) { property_insert.run(entity.id, p, 1, reason_satisfied) @@ -353,6 +365,14 @@ function deduce_dual_properties( console.info( `Deduced ${new_unsatisfied.size} unsatisfied properties by duality for ${entity.id}`, ) + + for (const q of new_undecidable) { + property_insert.run(entity.id, q, null, reason_undecidable) + } + + console.info( + `Deduced ${new_undecidable.size} undecidable properties by duality for ${entity.id}`, + ) } /** @@ -375,19 +395,19 @@ export function deduce_properties_for_entities(type: 'category' | 'functor') { const implications = get_normalized_implications(db, type) const entities = get_entities(db, type) const properties_dict = get_properties_dict(db, type) - const all_decided_properties = get_property_assignments(db, entities, type) + const get_assigned_properties = get_property_assignments(db, entities, type) const deduction = db.transaction(() => { delete_deduced_properties(db, type) for (const entity of entities) { - const decided = all_decided_properties[entity.id] + const assigned = get_assigned_properties[entity.id] deduce_satisfied_properties( db, entity, implications, - decided.satisfied, + assigned.satisfied, properties_dict, type, ) @@ -396,8 +416,8 @@ export function deduce_properties_for_entities(type: 'category' | 'functor') { db, entity, implications, - decided.satisfied, - decided.unsatisfied, + assigned.satisfied, + assigned.unsatisfied, properties_dict, type, ) @@ -413,16 +433,18 @@ export function deduce_properties_for_entities(type: 'category' | 'functor') { for (const entity of entities) { if (!is_dual_entity(entity)) continue - const decided = all_decided_properties[entity.id] - const dual_decided = all_decided_properties[entity.dual] + const assigned = get_assigned_properties[entity.id] + const dual_assigned = get_assigned_properties[entity.dual] deduce_dual_properties( db, entity, - decided.satisfied, - decided.unsatisfied, - dual_decided.satisfied, - dual_decided.unsatisfied, + assigned.satisfied, + assigned.unsatisfied, + assigned.undecidable, + dual_assigned.satisfied, + dual_assigned.unsatisfied, + dual_assigned.undecidable, properties_dict, type, ) @@ -431,7 +453,7 @@ export function deduce_properties_for_entities(type: 'category' | 'functor') { db, entity, implications, - decided.satisfied, + assigned.satisfied, properties_dict, type, ) @@ -440,8 +462,8 @@ export function deduce_properties_for_entities(type: 'category' | 'functor') { db, entity, implications, - decided.satisfied, - decided.unsatisfied, + assigned.satisfied, + assigned.unsatisfied, properties_dict, type, ) diff --git a/databases/catdat/scripts/seed.ts b/databases/catdat/scripts/seed.ts index b2afb9d50..e849af3d4 100644 --- a/databases/catdat/scripts/seed.ts +++ b/databases/catdat/scripts/seed.ts @@ -73,7 +73,6 @@ function clear_all_data() { db.prepare(`DELETE FROM special_objects`).run() db.prepare(`DELETE FROM special_object_types`).run() - db.prepare(`DELETE FROM category_property_comments`).run() db.prepare(`DELETE FROM category_property_assignments`).run() db.prepare(`DELETE FROM category_comments`).run() @@ -187,11 +186,6 @@ function seed_categories() { ) VALUES (?, ?, ?, ?, ?)`, ) - const property_comment_insert = db.prepare(` - INSERT INTO category_property_comments ( - category_id, property_id, comment - ) VALUES (?, ?, ?)`) - function insert_category(category: CategoryYaml) { category_insert.run( category.id, @@ -251,11 +245,13 @@ function seed_categories() { ) } - for (const comment_obj of category.category_property_comments ?? []) { - property_comment_insert.run( + for (const entry of category.undecidable_properties ?? []) { + property_assignment_insert.run( category.id, - comment_obj.property, - comment_obj.comment, + entry.property, + null, + entry.reason, + entry.check_redundancy === false ? 0 : 1, ) } } diff --git a/databases/catdat/scripts/seed.types.ts b/databases/catdat/scripts/seed.types.ts index 82a72bbde..74ab71acd 100644 --- a/databases/catdat/scripts/seed.types.ts +++ b/databases/catdat/scripts/seed.types.ts @@ -28,13 +28,10 @@ export type CategoryYaml = { related_categories: string[] satisfied_properties: PropertyEntry[] unsatisfied_properties: PropertyEntry[] + undecidable_properties?: PropertyEntry[] special_objects: Record special_morphisms: Record comments?: string[] - category_property_comments?: { - property: string - comment: string - }[] } type PropertyEntry = { diff --git a/databases/catdat/scripts/test.ts b/databases/catdat/scripts/test.ts index 8e8434794..3b088d4ef 100644 --- a/databases/catdat/scripts/test.ts +++ b/databases/catdat/scripts/test.ts @@ -150,6 +150,7 @@ function test_decided_entities(entities: string[], type: 'category' | 'functor') * Tests if selected categories or functors behave as expected: * All of their properties in the database have to match those in the * respective JSON files in the subfolder "expected-data". + * We exclude undecidable properties here. */ function test_properties_of_selected_entities( expected: Record>, @@ -157,7 +158,7 @@ function test_properties_of_selected_entities( ) { const property_query = db.prepare( `SELECT property_id, is_satisfied FROM ${type}_property_assignments - WHERE ${type}_id = ?`, + WHERE ${type}_id = ? AND is_satisfied IS NOT NULL`, ) for (const entity_id in expected) { diff --git a/databases/catdat/scripts/utils/deduction.ts b/databases/catdat/scripts/utils/deduction.ts index 01d670a71..80462d6d2 100644 --- a/databases/catdat/scripts/utils/deduction.ts +++ b/databases/catdat/scripts/utils/deduction.ts @@ -74,7 +74,8 @@ export function get_properties_dict(db: Database, type: 'category' | 'functor') /** * Returns a dictionary with all assigned properties of a list of entities - * (categories or functors), grouped by id and value (satisfied / unsatisfied). + * (categories or functors), grouped by id and + * value (satisfied / unsatisfied / undecidable). */ export function get_property_assignments( db: Database, @@ -86,18 +87,32 @@ export function get_property_assignments( `SELECT property_id, ${type}_id as entity_id, is_satisfied FROM ${type}_property_assignments`, ) - .all() as { property_id: string; entity_id: string; is_satisfied: 0 | 1 }[] + .all() as { property_id: string; entity_id: string; is_satisfied: 0 | 1 | null }[] - const grouped: Record; unsatisfied: Set }> = - {} + const grouped: Record< + string, + { satisfied: Set; unsatisfied: Set; undecidable: Set } + > = {} for (const entity of entities) { - grouped[entity.id] = { satisfied: new Set(), unsatisfied: new Set() } + grouped[entity.id] = { + satisfied: new Set(), + unsatisfied: new Set(), + undecidable: new Set(), + } } for (const row of rows) { const { property_id, entity_id, is_satisfied } = row - grouped[entity_id][is_satisfied ? 'satisfied' : 'unsatisfied'].add(property_id) + let group_key: 'satisfied' | 'unsatisfied' | 'undecidable' + if (is_satisfied === 1) { + group_key = 'satisfied' + } else if (is_satisfied === 0) { + group_key = 'unsatisfied' + } else { + group_key = 'undecidable' + } + grouped[entity_id][group_key].add(property_id) } return grouped @@ -106,6 +121,7 @@ export function get_property_assignments( /** * Returns a dictionary with all assigned properties of entities, * grouped by entity, value (satisfied / unsatisfied), and deduced status. + * We exclude undecidable properties here. */ export function get_property_assignments_by_deduction( db: Database, @@ -115,7 +131,7 @@ export function get_property_assignments_by_deduction( const rows = db .prepare( `SELECT property_id, ${type}_id as entity_id, is_satisfied, is_deduced - FROM ${type}_property_assignments`, + FROM ${type}_property_assignments WHERE is_satisfied IS NOT NULL`, ) .all() as { property_id: string diff --git a/src/components/PropertyList.svelte b/src/components/PropertyList.svelte index 00a5d9e53..65da0434c 100644 --- a/src/components/PropertyList.svelte +++ b/src/components/PropertyList.svelte @@ -9,17 +9,16 @@ reason?: string | null }[] type: 'category' | 'functor' - reason_heading?: string } - let { properties, type, reason_heading }: Props = $props() + let { properties, type }: Props = $props() {#if properties.length}
    {#each properties as { id, relation, reason }}
  • - + {relation} {id} diff --git a/src/components/TextWithReason.svelte b/src/components/TextWithReason.svelte index 7ca3f4d06..7a8689406 100644 --- a/src/components/TextWithReason.svelte +++ b/src/components/TextWithReason.svelte @@ -7,10 +7,9 @@ type Props = { children: Snippet reason?: string | null - heading?: string } - let { children, reason, heading = 'Reason' }: Props = $props() + let { children, reason }: Props = $props() const id = $props.id() @@ -18,7 +17,7 @@ e.stopPropagation() show_popup({ id, - heading, + heading: 'Reason', text: reason!, }) } diff --git a/src/lib/server/utils.ts b/src/lib/server/utils.ts index 94318c578..20d4ec3fd 100644 --- a/src/lib/server/utils.ts +++ b/src/lib/server/utils.ts @@ -1,7 +1,11 @@ import type { + CategoryProperty, + CategoryPropertyDB, FunctorImplicationDB, FunctorImplicationDisplay, FunctorProperty, + FunctorPropertyAssignment, + FunctorPropertyAssignmentDB, FunctorPropertyDB, ImplicationDB, ImplicationDisplay, @@ -45,6 +49,17 @@ export function display_property(property: PropertyDB): PropertyDisplay { } } +export function display_category_property_assignment( + property: CategoryPropertyDB, +): CategoryProperty { + return { + id: property.id, + reason: property.reason, + is_deduced: Boolean(property.is_deduced), + relation: property.relation, + } +} + export function display_functor_property(property: FunctorPropertyDB): FunctorProperty { return { id: property.id, @@ -56,6 +71,17 @@ export function display_functor_property(property: FunctorPropertyDB): FunctorPr } } +export function display_functor_property_assignment( + property: FunctorPropertyAssignmentDB, +): FunctorPropertyAssignment { + return { + id: property.id, + reason: property.reason, + is_deduced: Boolean(property.is_deduced), + relation: property.relation, + } +} + export function display_functor_implication( implication: FunctorImplicationDB, ): FunctorImplicationDisplay { diff --git a/src/routes/category-property/[id]/+page.server.ts b/src/routes/category-property/[id]/+page.server.ts index 4d06e2246..86942ba12 100644 --- a/src/routes/category-property/[id]/+page.server.ts +++ b/src/routes/category-property/[id]/+page.server.ts @@ -15,7 +15,7 @@ export const load = async (event) => { PropertyDB, { id: string }, ImplicationDB, - CategoryShort & { is_satisfied: 0 | 1 }, + CategoryShort & { is_satisfied: 0 | 1 | null }, CategoryShort, ] >([ @@ -95,8 +95,9 @@ export const load = async (event) => { const related_properties = related.map(({ id }) => id) - const examples = known_categories.filter((c) => c.is_satisfied) - const counterexamples = known_categories.filter((c) => !c.is_satisfied) + const examples = known_categories.filter((c) => c.is_satisfied === 1) + const counterexamples = known_categories.filter((c) => c.is_satisfied === 0) + const undecidable_categories = known_categories.filter((c) => c.is_satisfied === null) const relevant_implications = relevant_implications_db.map(display_implication) @@ -112,6 +113,7 @@ export const load = async (event) => { examples, counterexamples, unknown_categories, + undecidable_categories, relevant_implications, }) } diff --git a/src/routes/category-property/[id]/+page.svelte b/src/routes/category-property/[id]/+page.svelte index 1362b55c6..8192fb62a 100644 --- a/src/routes/category-property/[id]/+page.svelte +++ b/src/routes/category-property/[id]/+page.svelte @@ -107,4 +107,17 @@ +{#if data.undecidable_categories.length} +

    Undecidable categories

    + +

    + {pluralize(data.undecidable_categories.length, { + one: 'There is {count} category for which it cannot be decided if this property is satisfied or not.', + other: 'There are {count} categories for which it cannot be decided if this property is satisfied or not.', + })} +

    + + +{/if} + diff --git a/src/routes/category/[id]/+page.server.ts b/src/routes/category/[id]/+page.server.ts index bfe6207e8..e5a7c2e3f 100644 --- a/src/routes/category/[id]/+page.server.ts +++ b/src/routes/category/[id]/+page.server.ts @@ -4,7 +4,6 @@ import { batch } from '$lib/server/db.catdat' import sql from 'sql-template-tag' import type { CategoryDisplay, - CategoryProperty, CategoryPropertyDB, CategoryShort, CommentObject, @@ -14,6 +13,7 @@ import type { SpecialObject, TagObject, } from '$lib/commons/types' +import { display_category_property_assignment } from '$lib/server/utils' export const load = async (event) => { const id = event.params.id @@ -23,7 +23,7 @@ export const load = async (event) => { CategoryDisplay, RelatedCategory, TagObject, - CategoryPropertyDB & { is_satisfied: 0 | 1 }, + CategoryPropertyDB & { is_satisfied: 0 | 1 | null }, PropertyShort, SpecialObject, SpecialMorphism, @@ -75,8 +75,8 @@ export const load = async (event) => { cp.reason, cp.is_deduced, CASE - WHEN cp.is_satisfied = TRUE THEN p.relation - ELSE r.negation + WHEN cp.is_satisfied = FALSE THEN r.negation + ELSE p.relation END AS relation FROM category_property_assignments cp INNER JOIN category_properties p ON p.id = cp.property_id @@ -86,13 +86,8 @@ export const load = async (event) => { `, // unknown properties sql` - SELECT - p.id, - p.relation, - c.comment as reason + SELECT p.id, p.relation FROM category_properties p - LEFT JOIN category_property_comments c - ON c.category_id = ${id} AND c.property_id = p.id WHERE NOT EXISTS ( SELECT 1 FROM category_property_assignments WHERE category_id = ${id} AND property_id = p.id @@ -159,23 +154,17 @@ export const load = async (event) => { const category = categories[0] const tags = tag_objects.map(({ tag }) => tag) - const satisfied_properties: CategoryProperty[] = properties_db - .filter((obj) => obj.is_satisfied) - .map((p) => ({ - id: p.id, - reason: p.reason, - is_deduced: Boolean(p.is_deduced), - relation: p.relation, - })) + const satisfied_properties = properties_db + .filter((obj) => obj.is_satisfied === 1) + .map(display_category_property_assignment) + + const unsatisfied_properties = properties_db + .filter((obj) => obj.is_satisfied === 0) + .map(display_category_property_assignment) - const unsatisfied_properties: CategoryProperty[] = properties_db - .filter((obj) => !obj.is_satisfied) - .map((p) => ({ - id: p.id, - reason: p.reason, - is_deduced: Boolean(p.is_deduced), - relation: p.relation, - })) + const undecidable_properties = properties_db + .filter((obj) => obj.is_satisfied === null) + .map(display_category_property_assignment) return render_nested_formulas({ category, @@ -184,6 +173,7 @@ export const load = async (event) => { satisfied_properties, unsatisfied_properties, unknown_properties, + undecidable_properties, special_objects, special_morphisms, undistinguishable_categories, diff --git a/src/routes/category/[id]/+page.svelte b/src/routes/category/[id]/+page.svelte index 84f6e4298..958e0b401 100644 --- a/src/routes/category/[id]/+page.svelte +++ b/src/routes/category/[id]/+page.svelte @@ -157,13 +157,26 @@

    {/if} - + +{#if data.undecidable_properties.length > 0} +
    +

    Undecidable properties

    + + {#if data.undecidable_properties.length > 0} +

    + {pluralize(data.undecidable_properties.length, { + one: 'There is {count} property for which it cannot be decided if it is satisfied or not.', + other: 'There are {count} properties for which it cannot be decided if they are satisfied or not.', + })} +

    + {/if} + + +
    +{/if} +

    Special objects

    diff --git a/src/routes/download/+page.svelte b/src/routes/download/+page.svelte index 238107ff9..36c9b47ef 100644 --- a/src/routes/download/+page.svelte +++ b/src/routes/download/+page.svelte @@ -122,6 +122,12 @@ GROUP BY p.id ORDER BY undecided_categories DESC LIMIT 10; +
    -- Properties which cannot be decided for a given category
    +SELECT category_id, property_id, reason
    +FROM category_property_assignments
    +WHERE is_satisfied IS NULL;
    +
    +