/** * This program (working title: MAS Prover) is an automated tableaux prover * for epistemic logic (S5n). * Copyright (C) 2007 Elske van der Vaart and Gert van Valkenhoef * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package nl.rug.ai.mas.oops.tableau; import java.util.*; import nl.rug.ai.mas.oops.formula.*; /** * A (SAT-checking) modal tableau. * Supports listeners through the Observer pattern. * @see nl.rug.ai.mas.oops.tableau.TableauObserver * @see nl.rug.ai.mas.oops.tableau.Rule * @see nl.rug.ai.mas.oops.tableau.ModalRuleFactory * @see nl.rug.ai.mas.oops.tableau.PropositionalRuleFactory */ public class Tableau { public enum BranchState { OPEN, CLOSED, ERROR; } private Vector<Rule> d_rules; private String d_error; private Vector<TableauObserver> d_observers; private static final String s_errorInvalidRules = "Default case reached in tableau: invalid rules"; private static final String s_errorNoMatch = " is not simple, and does not match any rules"; /** * Constructor. * @param rules A list of tableau rules for construction. */ public Tableau(Vector<Rule> rules) { d_rules = rules; d_error = null; d_observers = new Vector<TableauObserver>(); } /** * Construct the tableau for the formula given. * @param f A Formula * @return BranchState.OPEN iff the construction was successful and the * tableau contains an open branch. BranchState.CLOSED iff the construction * was successful and the tableau does not contain an open branch. * BranchState.ERROR otherwise. */ public BranchState tableau(Formula f) { d_error = null; notify(new TableauStartedEvent()); BranchState result = (new Worker(f)).tableau(); notify(new TableauFinishedEvent(result)); return result; } /** * Attach a TableauObserver to this instance. */ public void attachObserver(TableauObserver o) { d_observers.add(o); } /** * Detach a TableauObserver from this instance. */ public void detachObserver(TableauObserver o) { d_observers.remove(o); } /** * In case tableau() returned TableauState.ERROR, this function should * return a more detailed error message. */ public String getError() { return d_error; } /** * Notify all attached TableauObserver objects of an event. */ private void notify(TableauEvent e) { for (TableauObserver o : d_observers) { o.update(this, e); } } /** * Implements the actual tableau construction. Copies itself upon any split * of the Branch. */ private class Worker { /** * Node that generates this branch. */ private Node d_node; /** * The Branch we are currently working on. */ private Branch d_branch; /** * The Match that resulted in the creation of this branch. */ private Match d_origin; /** * The list of Necessities. */ private Necessities d_necessities; /** * The Queue we are working from. */ private PriorityQueue<Match> d_queue; /** * The result obtained. */ private BranchState d_result; public Worker(Formula f) { d_node = new Node( createTopLabel(), f); d_branch = new Branch(null); d_origin = null; d_necessities = new Necessities(null); d_queue = new PriorityQueue<Match>(); d_result = null; } public Worker(Worker w, Node n, Match o) { d_node = n; d_origin = o; d_branch = new Branch(w.d_branch); d_necessities = new Necessities(w.d_necessities); d_queue = new PriorityQueue<Match>(w.d_queue); d_result = null; } public BranchState tableau() { if (d_result != null) { return d_result; } Tableau.this.notify(new BranchAddedEvent(d_branch)); handleNode(d_node, d_origin); if (d_result != null) { Tableau.this.notify(new BranchDoneEvent(d_branch)); return d_result; } // handle all elements on the queue while (!d_queue.isEmpty()) { Match match = d_queue.poll(); switch (match.getType()) { case SPLIT: handleSplit(match); break; case LINEAR: handleLinear(match); break; case CREATE: handleCreate(match); break; case ACCESS: handleLinear(match); break; default: d_error = s_errorInvalidRules; return BranchState.ERROR; } if (d_result != null) { Tableau.this.notify(new BranchDoneEvent(d_branch)); return d_result; } } Tableau.this.notify(new BranchOpenEvent(d_branch)); Tableau.this.notify(new BranchDoneEvent(d_branch)); return BranchState.OPEN; } private void handleSplit(Match match) { for (Node n : match.getNodes()) { Worker worker = new Worker(this, n, match); BranchState result = worker.tableau(); if (result != BranchState.CLOSED) { d_result = result; return; } } d_result = BranchState.CLOSED; } private void handleLinear(Match match) { for (Node n : match.getNodes()) { if (!d_branch.contains(n)) { handleNode(n, match); if (d_result != null) return; } } } private void handleCreate(Match match) { for (Node n : match.getNodes()) { if (!d_branch.contains(n)) { handleNode(n, match); if (d_result != null) return; d_queue.addAll(d_necessities.apply(n.getLabel())); } } } private void handleNode(Node n, Match m) { Node neg = new Node(n.getLabel(), n.getFormula().opposite()); if (d_branch.contains(neg)) { put(n, m); Tableau.this.notify( new BranchClosedEvent(d_branch, n, neg)); d_result = BranchState.CLOSED; } else { matchPut(n, m); } } private void put(Node n, Match m) { d_branch.add(n); Tableau.this.notify(new NodeAddedEvent(d_branch, n, m)); } private void matchPut(Node n, Match m) { put(n, m); Vector<Match> v = match(n); if (!v.isEmpty()) { for (Match match : v) { // treat necessities specially if (match.getType() == Rule.Type.ACCESS) { d_necessities.add(match); d_queue.addAll(d_branch.apply(match)); } else { d_queue.add(match); } } } } private Vector<Match> match(Node f) { Vector<Match> result = new Vector<Match>(); for (Rule r : d_rules) { Match m = r.match(f); if (m != null) result.add(m); } return result; } } public void clearObservers() { d_observers.clear(); } public static Label createTopLabel() { return new LabelInstance( new NullLabel(), new WorldInstance(null), new NullAgent()); } }