#!/usr/bin/env python3 """ Implements code for stable revision. When run, will compute the least stable fixedpoint of a knowledge base usage: python aft.py knowledge_bases/simple.hmknf OR usage: python aft.py < knowledge_bases/simple.hmknf """ from sys import stdin, flags, argv from AST import KB, Set, atom, loadProgram from hmknf import objective_knowledge from util import printp Kinterp = tuple[Set[atom], Set[atom]] def add_immediate_XY(kb: KB, X: Set[atom], Y: Set[atom]) -> Set[atom]: """ When X is T and Y is P, will compute the atoms that must be true as a consequence of the rules and the ontology When they are flipped, i.e. X = P, and Y = T, then it computes what is "possibly true" This function is monotone w.r.t. the precision ordering """ _, X, _ = objective_knowledge(kb, X, Set()) for rule in kb.rules: if not X.issuperset(rule.pbody): continue if Y.intersection(rule.nbody): continue X = X.union({rule.head[0]}) return X def extract_OBT_entails_false(kb: KB, T: Set[atom]): """ Returns the set of atoms that are objectively false w.r.t. T Function is antitone w.r.t. subset relation """ _, _, F = objective_knowledge(kb, T, Set()) return F def approximator(kb: KB, T: Set[atom], P: Set[atom]) -> Kinterp: return ( add_immediate_XY(kb, T, P), add_immediate_XY(kb, P, T) - extract_OBT_entails_false(kb, T), ) def fixpoint(op, initial): """ Iteratively apply a function beginning with the value initial until a fixpoint is reached E.g. op(op(op(initation))) """ prev = initial while (current := op(prev)) != prev: prev = current return prev def stable_revision(kb: KB, T: Set[atom], P: Set[atom]): def left(T): return add_immediate_XY(kb, T, P) def right(P): return add_immediate_XY(kb, P, T) - extract_OBT_entails_false(kb, T) return ( fixpoint(left, Set()), fixpoint(right, Set()), ) def stable_revision_extend(kb: KB, initialT: Set[atom], initialP: Set[atom]): def stable_revision_tuple(TP: Kinterp) -> Kinterp: T, P = TP return stable_revision(kb, T, P) return fixpoint(stable_revision_tuple, (initialT, initialP)) def least_stable_fixedpoint(kb: KB): return stable_revision_extend(kb, Set(), kb.katoms) def main(): """""" if len(argv) > 1: in_file = open(argv[1], "rt", encoding="utf8") else: in_file = stdin kb = loadProgram(in_file.read()) T, P = least_stable_fixedpoint(kb) printp(T, P) if __name__ == "__main__" and not flags.interactive: main()