A Differential Diagnostic System based on a Java Framework by pran342


									     A Differential Diagnostic System based on a Java Framework for
                           Intelligent Systems
                                        by Holger Flier
                                 mail: thesis@holgerflier.de
                              www: http://www.holgerflier.de/thesis

This software is part of my diploma thesis Construction of Differential Diagnostic Systems. It
comprises of
   • a framework for the development of logic-based intelligent systems, providing classes for
       literals, logic formulas, CNF-Systems, etc. (see chapter 6 of my thesis, which is appended
       to this document)
   • an encapsulation of the MINSAT-solver functionality of the lbcc compiler of the Leibniz
       System, including a pre-compiled DLL
   • some smaller examples on how to use the library
   • an implementation, including example data, of a Differential Diagnostic System (DDS) as
       suggested in Klaus Truemper's book Design of Logic-based Intelligent Systems (see
       chapter 7 of my thesis, which is appended to this document)

System Requirements:
   • JDK 5.0 (available at http://java.sun.com/j2se/1.5.0/download.jsp )
   • Windows XP (or compatible)

Legal note:
Except for the DLL, which is distributed under the license terms applying to the Leibniz System,
this software is freely available. The software is distributed “as is”, without any guarantee.

Installation instructions for experienced developers:
Unzip the file dds_1.0.zip to a folder of your choice.
In the „examples“ package, you can find several small classes illustrating the use of the
framework, i.e. the packages “logic” and “leibniz”.
The implementation of the DDS is found in the „diffDiagSys“ package. Run the class

Installation instructions for Java beginners:
Please note that a part of the software (the DLL) will only run on Windows XP (or compatible)!
I recommend you install the following software, which will enable you to try out the library
without running into technical problems:
1. Install the JDK 5.0 (available at http://java.sun.com/j2se/1.5.0/download.jsp )
2. Install BlueJ (available at http://www.bluej.org/download/download.html )
3. Download dds_1.0.zip and extract it, for example, to your Desktop.

After installation, try the following steps:
Start BlueJ. (You might be asked to select a Virtual Machine, which is the JDK 5.0 installed in
step 1. On my computer, this is “c:\Program Files\Java\jdk1.5.0_06”.)
From the menu, select „Project“ and then „Open Non BlueJ“. Select the „dds_1.0“ folder:
In the next screen, you see all subdirectories of „dds_1.0“. Double click on the package
Now, you see all classes contained in the package „diffDiagSys“. Right click on the class
„DifferentialDiagnosticSystem“ and select „void main(String[])“. This is the main routine, starting
the DDS.

BlueJ will prompt you for parameters to pass on to the main function. Simply press „OK“. The
application should now start with the following screen:
You can now try out the DDS by clicking on the button „1. Get Record“ and filling in values.

To try out the library, you may also want to take a look at the examples in the „examples“ package
(execute them by right-clicking and selecting „void main(String[])“ as shown above for the DDS).

To view the source code of a class, simply double click on the class in BlueJ

Further information on the library and the DDS are provided in chapters 6 and 7 of the thesis, which
are appended at the end of this document. You may also download the full thesis at
Chapter 6

A Framework for Logic

The motivation behind the framework presented in this thesis is clearly to fa-
cilitate the implementation of logic-based intelligent systems. With the Leibniz
System, a powerful set of tools is already available. It is programmed in C, a
programming language that allows for efficient use of hardware resources, espe-
cially processor time. The choice of such a programming language seems to be
mandatory if one is interested in developing fast code. However, for fast devel-
oping other tools are more adequate, at least for those parts of the code that are
not critical concerning speed.
While C allows for fast execution of programs, it usually takes considerably more
time to implement applications compared to other programming languages, such
as Java. This is especially the case when dealing with graphical user interfaces
(GUI). For the purposes of both research and practice, being able to rapidly
implement prototypes and applications is of interest.
For example, a student might want to work on some of the programming exer-
cises of the book Design of logic-based intelligent Systems [10] to gain a better
understanding of the algorithms presented there. It would be motivating to get
working results in short time, instead of having to worry about the usual problems
encountered when programming in C. Furthermore, the student might appreciate
to use a library that already provides implementations of essential concepts such
as a CNF system, thus leaving the student more time to work on the exercises.
Or consider the practitioner who realized that one of the applications presented
in [10] is suitable to solve a problem she is confronted with. Although she is an
experienced programmer, her boss does not leave her much time to play with
new ideas. To convince her boss, she would have to create a prototype of the
application quickly. There is no time to get acquainted with detailed technical
manuals. She needs a library that is intuitive to use.

42                                            A Framework for Logic Computation

For all these reasons, it is desirable to develop tools that allow for rapid devel-
opment of logic-based intelligent systems. With the work of this thesis, a step in
this direction has been made. We provide a framework of classes written in the
object-oriented programming language Java. Java is easy to learn, widely spread
and available on virtually all platforms. Through the design of Java, many pitfalls
encountered in C can be avoided. Excellent development tools are available for
free, e. g. the Eclipse software development kit [3], as well as literally millions of
websites providing support. In this spirit, the framework will be freely available.
It is the hope of the author that it will be used and developed further. Users are
strongly encouraged to contribute to this project.
A clear disadvantage of Java as compared to C is the speed of execution. However,
as the time critical parts are already implemented in the Leibniz System, this does
not imply that applications developed with the framework are necessarily slow.
Even if the use of Java is infeasible for a certain application, the framework may
still be of use for rapid prototyping.
We start with an overview of the framework. It comprises of several classes that
reflect the basic concepts of propositional logic, i. e. variables, literals, clauses,
and formulas, as well as further concepts like learned formulas, records, and CNF
systems. The corresponding classes of these concepts are bundled in a package
called logic. A further package, called leibniz, contains a class that provides
an interface to the lbcc compiler of the Leibniz System. With the help of that
class, SAT and MINSAT problems can be solved conveniently.
As will be shown in chapter 7, implementation of logic-based intelligent systems
is straightforward with the use of this framework. Here, we describe the interfaces
of the framework’s classes. We will make use of terms as Collections, Lists
or Sets. By that we denote interfaces of data structures available in the Java
language. The interpretation of List and Set is obvious, whereas a Collection
may stand for any kind of data structure, including the latter ones. For a compre-
hensive introduction to these interfaces, their underlying implementations, and
the Java language in general consult [2].
Since the source code is listed in the appendix, we omit types of both signatures
and return values of methods, as well as getter and setter methods which have
an obvious interpretation.

6.1     Classes Clause, Formula, Literal, and Variable
The purpose of creating classes for basic entities of propositional logic is mainly to
encourage clarity in the design of implementations. The behavior of the methods
is mostly self-explanatory. Since there is a one-to-one correspondence of classes
and concepts, i. e. class Variable and the concept of a “variable”, we will use such
6.1 Classes Clause, Formula, Literal, and Variable                                  43

terms interchangeably.
We start with introducing the methods of class Variable. This class has a
special interface since its constructor is not public. The reason for this is that
each variable must have a unique identifier. Allowing many objects of Variable
with the same identifier could lead to mistakes that are hard to debug. Instead,
the following methods are available:

Variable.createOrGet(name, trueCost, falseCost) With the help of this
     static method, variables are instantiated with name as identifier. At the
     same time, costs for the variable are defined in case that MINSAT instances
     will be solved. If, however, a variable with the same name has already been
     created, this variable is returned without changing its costs.

Variable.exists(name) A static method that checks whether a variable for the
     identifier already exists, but that does not create a new instance.

Variable.getAllVariables() Returns a Collection of all instances of the
     class Variable.

Variable.getByName(name) This static method returns an existing variable for
     the identifier or null if such an instance does not exist. It does not create
     a new variable.

Class Literal consists of a variable and a boolean stating if the literal is a
negated variable or not. For a single variable, arbitrary many literals may exist.

Literal(isPositive, variable) Setting the argument isPositive to true
     means that the literal is a non-negated variable.

getCopy() Returns a copy of the literal.

not() Returns a negated copy of the literal, i. e. if the literal is a negated variable
     the copy is non-negated and vice versa.

Class Clause comprises of a set of literals. A Clause is either in CNF or DNF.
Thus, all literals of a clause are connected by the same operator. To specify
the normal form, the class Formula provides constants NormalForm.CNF and

Clause(normalForm) Constructs an empty CNF or DNF clause.

addLiteral(literal) Adds a literal to the clause. The operator combining the
     literals depends on the normal form.
44                                          A Framework for Logic Computation

containsUnavailableLiteral(record) Returns true if and only if the clause
     contains a literal of a variable that has an Unavailable value in a record of
     the yet to be described class Record.

evaluateRecord(record) Returns the True/False/Undecided value, to which
     the clause evaluates on record. Any open xj will be treated as Absent.
     Constants representing the True/False/Undecided values are defined in the
     static enum member Formula.Value.

getCopy() Creates a copy of the clause and its literals.

getVariables() Returns a list of variables that occur as literals in the clause.

not() Applies the “¬” (not) operator to the clause, thus turning a CNF clause
     into a DNF clause and vice versa, e. g. ¬(x1 ∧ ¬x2 ∧ x3 ) = (¬x1 ∨ x2 ∨ ¬x3 ).

Class Formula consists of a set of clauses. A Formula is either in CNF or DNF.
Thus, all of its clauses must also be in the same normal form.

Formula(normalForm) Constructs a formula in CNF or DNF having no clauses.

addClause(clause) Adds a clause to the formula. If necessary, the clause is
     first transformed into the normal form of the formula.

addClauses(string) Creates a clause from a string. For example, the clause
     (x1 ∧ x2 ) ∨ (¬x3 ) is created with "x1 AND x2 OR NOT x3". This method is
     intended for testing purposes only. Operators have to be given in capital
     letters, parenthesis have to be omitted.

convertedToCNF() Returns a copy of the formula which has been converted to
     CNF via the distributive law.

evaluateRecord(record) Evaluates a record by evaluating the clauses of the
     formula for that record. The True/False/Undecided values which may be
     returned are defined in the static enum Formula.Value.

getUsedVariables() Returns a set of all variables having a literal in any of the
     formula’s clauses.

In the following we give some examples that demonstrate the use of some of the
classes described so far. All examples are part of the package examples. They
can be executed as a simple console application. We start with an example on
how to create variables, literals, and clauses.
01:   package examples;
6.1 Classes Clause, Formula, Literal, and Variable                            45

03:   import   logic.Clause;
04:   import   logic.Formula;
05:   import   logic.Literal;
06:   import   logic.Variable;
08:   public class LogicExample1 {
09:      public static void main(String[] args) {
11:           Variable x1 = Variable.createOrGet("x1");
12:           Variable x2 = Variable.createOrGet("x2");
13:           Literal l1 = new Literal(true, x1);
14:           Literal l2 = new Literal(false, x2);
15:           Clause c1 = new Clause(Formula.NormalForm.DNF);
16:           c1.addLiteral(l1);
17:           c1.addLiteral(l2);
18:           c1.addLiteral(new Literal(true, Variable.createOrGet("x3")));
19:           System.out.println("Clause c1 in DNF:");
20:           System.out.println(c1);
21:           System.out.println();
22:           System.out.println("Negated copy of c1 in CNF:");
23:           System.out.println(c1.not());
24:       }
25:   }

The first six lines are declarations required in Java. The main method of line
9 is automatically called when executing this code. For details about the Java
language, see [2] or search the internet for “java tutorial”. Executing the above
example yields:

Clause c1 in DNF:
x1 AND NOT x2 AND x3

Negated copy of c1 in CNF:
NOT x1 OR x2 OR NOT x3

The following example demonstrates the use of class Formula. For brevity, the
addClauses method is used. In the implementation of the differential diagnostic
system, we will merely use method addClause.
01:   package examples;
03:   import   logic.Clause;
04:   import   logic.Formula;
05:   import   logic.Literal;
06:   import   logic.Variable;
46                                          A Framework for Logic Computation

07:   import logic.Formula.NormalForm;
09:   public class LogicExample2 {
10:      public static void main(String[] args) {
12:           Formula f = new Formula(NormalForm.DNF);
13:           f.addClauses("x1 AND x2 OR NOT x3");
14:           System.out.println("Formula f in DNF, one clause per line:");
15:           System.out.println(f);
17:           Clause c = new Clause(Formula.NormalForm.DNF);
18:           c.addLiteral(new Literal(true, Variable.createOrGet("x4")));
19:           c.addLiteral(new Literal(true, Variable.createOrGet("x5")));
20:           f.addClause(c);
21:           System.out.println("Formula f, with an additional clause:");
22:           System.out.println(f);
24:           Formula g = f.convertedToCNF();
25:           System.out.println("Formula g, a copy of f in CNF:");
26:           System.out.println(g);
27:       }
28:   }

This example leads to the following output:

Formula f in DNF, one clause per line:
x1 AND x2
NOT x3

Formula f, with an additional clause:
x1 AND x2
NOT x3
x4 AND x5

Formula g, a    copy of f in CNF:
x1 OR NOT x3    OR x4
x1 OR NOT x3    OR x5
x2 OR NOT x3    OR x4
x2 OR NOT x3    OR x5

Notice that, e. g. in line 15, the method f.toString is implicitly called. All
classes described so far have such a method. The format produced by these
methods is useful when writing .log files used as input for the lbcc compiler.
We will make use of the classes described so far in the following sections covering
classes Record, LearnedFormula, CNFSystem, and finally Lbcc.
6.2 Class Record                                                                47

6.2     Class Record
A Record is a collection of Record.Values of variables. Possible values are TRUE,
FALSE, ABSENT, UNDECIDED and OPEN. According to previous chapters a variable
may be regarded as open, but “open” was not defined as a value. However, in
order to simplify the implementation, we allow for the value OPEN, too. In case
of evaluation of formulas or reduction of CNF systems, an open variable will be
interpreted as having value Absent. Since both open variables as well as variables
with value Absent do not lead to reduction of CNF systems, the implementation
chosen here is in accordance with definitions of the previous chapters.
Since a record may be used in various contexts, a default value for Variables that
have not been assigned a value yet must be given when instantiating a Record.
Further, a Collection of Variables must be given for which the record stores
values. When using the setter methods on variables not yet in this collection, the
variables will be incorporated into the collection. Note that there may be many
Records storing Values for the same Variable.

Record(defaultValue, variables) Constructs a record containing values for
     a collection of variables. The variables of this collection are initialized to
     the defaultValue.
allVariablesFixed() Returns true if and only if the record contains only
     True/False/Unavailable values.
compareTo(record) Allows for sorting and comparing records. Records are com-
     pared by creating a String for each record, consisting of name/value pairs,
     and then lexicographically comparing these Strings. This method is used
     to count the number of copies of equal records in files. A meaningful inter-
     pretation of the ordering induced by this method is not important.
countCoveredOpenVars(record) Counts the number of True/False values that
     this record has for variables that are open in record.
countOppositeTrueFalse(record) Counts the number of True/False values
     that this record has for variables that have the opposite True/False value
     in record.
countCommonTrueFalse(record) Counts the number of True/False values this
     record has in common with record.
covers(variable) Returns true if the record has a True/False value for the
coversOpenXjOf(record) Returns true if the record has at least one True/False
     value for an open variable of record.
48                                        A Framework for Logic Computation

getAllVariables() Returns the collection of variables for which the record con-
     tains values.

getCopyWithOpenValuesFilledBy(record) Creates a copy of the record with
     values of open variables substituted by values of record.

getVariableValuePairs() Creates an Object[][] used by the GUI of the dif-
     ferential diagnostic system.

All other getter and setter methods are self-explanatory, as can be seen in the
following example:
01:   package examples;
03:   import logic.Record;
04:   import logic.Variable;
06:   public class RecordExample {
07:      public static void main(String[] args) {
09:         // create variables first
10:         Variable.createOrGet("a");
11:         Variable.createOrGet("b");
12:         Variable.createOrGet("c");
13:         Variable.createOrGet("d");
14:         Variable.createOrGet("e");
15:         // define record on these variables
16:         Record r = new Record(Record.Value.OPEN, Variable
17:            .getAllVariables());
18:         r.setTrue(Variable.getByName("a"));
19:         r.setFalse(Variable.getByName("b"));
20:         r.setAbsent(Variable.getByName("c"));
21:         r.setUnavailable(Variable.getByName("d"));
22:         System.out.println("r = " + r);
23:         // define another record on these variables
24:         Record s = new Record(Record.Value.OPEN, Variable
25:            .getAllVariables());
26:         s.setValue(Variable.getByName("a"), Record.Value.TRUE);
27:         s.setValue(Variable.getByName("b"), Record.Value.TRUE);
28:         s.setValue(Variable.getByName("c"), Record.Value.TRUE);
29:         s.setValue(Variable.getByName("d"), Record.Value.TRUE);
30:         s.setValue(Variable.getByName("e"), Record.Value.TRUE);
31:         System.out.println("s = " + s);
32:         // test some properties
33:         System.out.println("");
34:         System.out.print("Common True/False values: ");
6.3 Class LearnedFormula                                                         49

35:           System.out.println(r.countCommonTrueFalse(s));
36:           System.out.print("Open variables of r covered by s: ");
37:           System.out.println(s.countCoveredOpenVars(r));
38:       }
39:   }

This outputs:

r = (a=True     ,b=False ,c=Abs.   ,d=Unav. ,e=Open     )
s = (a=True     ,b=True ,c=True    ,d=True ,e=True      )

Common True/False values: 1
Open variables of r covered by s: 1

6.3       Class LearnedFormula
A LearnedFormula is a direct subclass of class Formula extended by several
properties. First, a learned formula may evaluate to True on either set A or set B,
thus being of type “D” or “E” (according to the notation of section 3.3). Second, a
learned formula consists of clauses with either a minimum or a maximum number
of literals. Third, it has an index 1 ≤ j ≤ 40. A learned formula is always in
DNF, and there is no distinction between optimized and non-optimized formulas.

LearnedFormula(index, trueSet, minOrMax) The constructor takes three ar-
     guments: the index j, the training set ("A" or "B") for whose records the
     formula evaluates to True, and the type of the formula ("min" or "max").

evaluatesTrueOnSetA() Returns true if the learned formula is of type “D”, i. e.
     evaluates to True on records of training set A.

voteOnRecord(record) Transforms the True/False/Undecided value of the in-
     herited evaluateRecord(record method to a {1, 0, −1} vote, depending
     on the type of the learned formula.

It is possible to associate record sets HF g and HP g with the formula via getter
and setter methods. If these sets are provided, low-cost futile file F g and low-cost
proof file P g may be derived by corresponding getter methods.
We include an example in which a hypothetical formula is created.
01:   package examples;
03:   import logic.Clause;
50                                      A Framework for Logic Computation

04:   import   logic.Formula;
05:   import   logic.LearnedFormula;
06:   import   logic.Literal;
07:   import   logic.Record;
08:   import   logic.Variable;
10:   public class LearnedFormulaExample {
11:      public static void main(String[] args) {
12:         // set up a hypothetical formula
13:         LearnedFormula formula = new LearnedFormula(1, "B", "max");
14:         // create clauses
15:         Clause clause1 = new Clause(Formula.NormalForm.DNF);
16:         clause1.addLiteral(new Literal(false, Variable
17:            .createOrGet("x1")));
18:         clause1.addLiteral(new Literal(true, Variable
19:            .createOrGet("x2")));
20:         Clause clause2 = new Clause(Formula.NormalForm.DNF);
21:         clause2.addLiteral(new Literal(false, Variable
22:            .createOrGet("x3")));
23:         // add clauses to formula
24:         formula.addClause(clause1);
25:         formula.addClause(clause2);
26:         System.out.println(formula);
27:         // create a record
28:         Record record = new Record(Record.Value.OPEN,
29:            Variable.getAllVariables());
30:         record.setTrue(Variable.getByName("x1"));
31:         record.setTrue(Variable.getByName("x2"));
32:         record.setFalse(Variable.getByName("x3"));
33:         // let the formula evaluate the record
34:         System.out.println();
35:         System.out.println("record = " + record);
36:         System.out.print("on record, fomula evaluates to ");
37:         System.out.println(formula.evaluateRecord(record));
38:         System.out.print("on record, fomula votes with ");
39:         System.out.println(formula.voteOnRecord(record));
40:         // effect of the Absent value
41:         record.setAbsent(Variable.getByName("x3"));
42:         System.out.println();
43:         System.out.println("record = " + record);
44:         System.out.print("on record, fomula evaluates to ");
45:         System.out.println(formula.evaluateRecord(record));
46:         System.out.print("on record, fomula votes with ");
47:         System.out.println(formula.voteOnRecord(record));
48:      }
6.4 Class CNFSystem                                                            51

49:   }

Note in the output, that since the formula is of type “E”, it evaluates to True on
records of B. Thus, if evaluating to True, the vote is defined to be −1.

LearnedFormula (1), Type E MAX : (NOT x1 AND x2) (NOT x3)

record = (x1=True ,x2=True ,x3=False )
on record, fomula evaluates to TRUE
on record, fomula votes with -1

record = (x1=True ,x2=True ,x3=Abs. )
on record, fomula evaluates to UNDECIDED
on record, fomula votes with 0

6.4       Class CNFSystem
In class CNFSystem we make use of many of the above concepts. It provides meth-
ods for solving SAT and MINSAT problems, where the CNF system itself is the
instance to solve. A CNFSystem has collections of both Clauses and Variables
as fields, thus allowing for the special cases mentioned in section 2.1. Correct
insertion of DNF formulas is assured. Given a certain Record, reduction of CNF
systems is possible.

CNFSystem(variables) The constructor takes a set of variables as arguments.
     The CNF system has no clauses at the beginning.

getReducedCNFSystem(record) This method returns a copy of the CNF system
     which has been reduced according to the definitions of section 2.1 using the
     values of the given record.

addClause(clause) Inserts a single DNF clause into the CNF system. It may
     be used for testing, but is not needed for the differential diagnostic system.

addIfThenClauses(condition, conclusion) For the purposes of the differen-
     tial diagnostic system, we need to insert CNF clauses corresponding to the
     expression d → D, where either the condition d is a literal and D is a for-
     mula or vice versa. Besides a formula, also a single clause may be inserted.
     Several methods with corresponding signatures are provided. In case of d
     being a literal, D has to be reduced according to given Unavailable values
     before insertion. Therefore, the method with the corresponding signature
     demands a Record as a third argument.
52                                          A Framework for Logic Computation

fixVariable(variable, trueFalseValue) Fixing a variable xj in the CNF
     system simply adds another clause (xj ) or (¬xj ) to the system.

getWithVarsDeleted(variables) Returns a copy of the CNF system in which
     all literals of all variables given in the list of variables are removed.

isSatisfiable() Checks satisfiability with use of class Lbcc, i. e. the lbcc com-
     piler. Special cases of trivial satisfiability are checked without using Lbcc.

isUnsatisfiable() This is a shortcut with the obvious interpretation.

solveMINSAT() Returns the objective value of the MINSAT solution solved via
     class Lbcc. The constant defaultObjValOfUnsatisfiableMINSAT is sub-
     stituted for the objective value if the CNF system is not satisfiable. There-
     fore, calling isSatisfiable first is unnecessary.

The following example demonstrates how one can set up CNF systems to prove
certain conclusions. For a given formula, three records are set up on which the
formula evaluates to True/False/Undecided , respectively. Then, for each of these
records, a CNF system is set up that includes a variable d that is constrained
to take on the same True/False value to which the formula evaluates, or is not
constrained if the formula takes on the value Undecided .
001:   package examples;
003:   import   logic.CNFSystem;
004:   import   logic.Clause;
005:   import   logic.Formula;
006:   import   logic.Literal;
007:   import   logic.Record;
008:   import   logic.Variable;
010:   public class CNFSystemExample {
011:      public static void main(String[] args) {
013:         /*
014:       * set up a hypothetical formula
015:       */
016:      Formula formula = new Formula(Formula.NormalForm.DNF);
017:      // create clauses
018:      Clause clause1 = new Clause(Formula.NormalForm.DNF);
019:      clause1.addLiteral(new Literal(true, Variable
020:         .createOrGet("x1")));
021:      clause1.addLiteral(new Literal(true, Variable
022:         .createOrGet("x2")));
023:      Clause clause2 = new Clause(Formula.NormalForm.DNF);
6.4 Class CNFSystem                                                     53

024:    clause2.addLiteral(new Literal(true, Variable
025:       .createOrGet("x3")));
026:    Clause clause3 = new Clause(Formula.NormalForm.DNF);
027:    clause3.addLiteral(new Literal(true, Variable
028:       .createOrGet("x4")));
029:    clause3.addLiteral(new Literal(true, Variable
030:       .createOrGet("x5")));
031:    // add clauses to formula
032:    formula.addClause(clause1);
033:    formula.addClause(clause2);
034:    formula.addClause(clause3);
035:    System.out.println("formula: ");
036:    System.out.println(formula);
037:    // create a literal
038:    Literal d = new Literal(true, Variable.createOrGet("d"));
039:    /*
040:     * create record for which the formula evaluates to True
041:     */
042:    Record record = new Record(Record.Value.OPEN, Variable
043:       .getAllVariables());
044:    record.setTrue(Variable.getByName("x1"));
045:    record.setTrue(Variable.getByName("x2"));
046:    record.setFalse(Variable.getByName("x3"));
047:    record.setTrue(Variable.getByName("x4"));
048:    record.setUnavailable(Variable.getByName("x5"));
049:    // let the formula evaluate the record
050:    System.out.println("record: " + record);
051:    System.out.print("on record, formula evaluates to ");
052:    System.out.println(formula.evaluateRecord(record));
053:    // set up a CNF system
054:    System.out.println("Setting up a new CNF system S.");
055:    CNFSystem S = new CNFSystem(Variable.getAllVariables());
056:    System.out.println(
057:       "S after inserting implication (d->formula):");
058:    S.addIfThenClauses(d, formula, record);
059:    System.out.println(S);
060:    System.out.println(
061:       "S after inserting implication (formula->d):");
062:    S.addIfThenClauses(formula, d);
063:    System.out.println(S);
064:    CNFSystem Sprime = S.getReducedCNFSystem(record);
065:    System.out.println("CNF system reduced according to record");
066:    System.out.println(Sprime);
067:    /*
068:     * change record such that the formula evaluates to False
54                                       A Framework for Logic Computation

069:        */
070:       record.setFalse(Variable.getByName("x1"));
071:       // let the formula evaluate the record
072:       System.out.println("record: " + record);
073:       System.out.print("on record, formula evaluates to ");
074:       System.out.println(formula.evaluateRecord(record));
075:       // set up a new CNF system
076:       System.out.println("Setting up a new CNF system S.");
077:       S = new CNFSystem(Variable.getAllVariables());
078:       System.out.println("Inserting implication (d->formula) and");
079:       S.addIfThenClauses(d, formula, record);
080:       System.out
081:          .println("inserting implication (formula->d) yields");
082:       S.addIfThenClauses(formula, d);
083:       System.out.println(S);
084:       System.out.println("CNF system reduced according to record");
085:       Sprime = S.getReducedCNFSystem(record);
086:       System.out.println(Sprime);
087:       /*
088:        * change record such that the formula evaluates to
089:        * Undecided
090:        */
091:       record.setAbsent(Variable.getByName("x3"));
092:       // let the formula evaluate the record
093:       System.out.println("record: " + record);
094:       System.out.print("on record, formula evaluates to ");
095:       System.out.println(formula.evaluateRecord(record));
096:       // set up a new CNF system
097:       System.out.println("Setting up a new CNF system S.");
098:       S = new CNFSystem(Variable.getAllVariables());
099:       System.out.println("Inserting implication (d->formula) and");
100:       S.addIfThenClauses(d, formula, record);
101:       System.out
102:          .println("inserting implication (formula->d) yields");
103:       S.addIfThenClauses(formula, d);
104:       System.out.println(S);
105:       System.out.println("CNF system reduced according to record");
106:           Sprime = S.getReducedCNFSystem(record);
107:           System.out.println(Sprime);
108:       }
109:   }

In the output, notice that clauses of the DNF formula which are affected by any
Unavailable values are not inserted, according to the rule from section 2.2.
6.4 Class CNFSystem                                                    55

x1 AND x2
x4 AND x5

record: (d=Open ,x1=True ,x2=True ,x3=False ,x4=True     ,x5=Unav. )
on record, formula evaluates to TRUE
Setting up a new CNF system S.
S after inserting implication (d->formula):
(NOT d OR x1 OR x3)
(NOT d OR x2 OR x3)

S after inserting implication (formula->d):
(NOT d OR x1 OR x3)
(NOT d OR x2 OR x3)
(NOT x1 OR NOT x2 OR d)
(NOT x3 OR d)
(NOT x4 OR NOT x5 OR d)

CNF system reduced according to record

record: (d=Open ,x1=False ,x2=True ,x3=False ,x4=True    ,x5=Unav. )
on record, formula evaluates to FALSE
Setting up a new CNF system S.
Inserting implication (d->formula) and
inserting implication (formula->d) yields
(NOT d OR x1 OR x3)
(NOT d OR x2 OR x3)
(NOT x1 OR NOT x2 OR d)
(NOT x3 OR d)
(NOT x4 OR NOT x5 OR d)

CNF system reduced according to record
(NOT d)

record: (d=Open ,x1=False ,x2=True ,x3=Abs.   ,x4=True   ,x5=Unav. )
on record, formula evaluates to UNDECIDED
Setting up a new CNF system S.
Inserting implication (d->formula) and
inserting implication (formula->d) yields
(NOT d OR x1 OR x3)
(NOT d OR x2 OR x3)
(NOT x1 OR NOT x2 OR d)
(NOT x3 OR d)
56                                           A Framework for Logic Computation

(NOT x4 OR NOT x5 OR d)

CNF system reduced according to record
(NOT d OR x3)
(NOT x3 OR d)

A further example of how to use this class is discussed in chapter 7 (p. 59).

6.5     Class Lbcc
The framework is based on the ability to connect to the lbcc compiler of the Leib-
niz System. The class Lbcc encapsulates the lbcc compiler, using the Java Native
Interface (JNI) technology [8]. It provides methods solveSAT and solveMINSAT,
which are given a CNF system S as input parameters. Class Lbcc then handles
all the work necessary to solve the respective problem for S. First, it sets all nec-
essary parameters for the lbcc compiler, including size of memory to allocate,
input directory, various filenames, and writing the leibnizparams.dat file. Sec-
ond, the CNF system S is transformed to the input format of the lbcc compiler
and written to a .log file. Finally, the lbcc compiler is called. The solution can
be obtained via methods isSatisfiable and, in case of a MINSAT problem,
The usage of this class facilitates the use of the lbcc compiler, since the pro-
grammer does not have to deal with the parametrization of the lbcc compiler
anymore, which may be subject to pitfalls for the novice. Furthermore, it en-
hances readability of the code since checking whether a CNF system is satisfiable
can be done with a single call of a method, as described above.
Finally, encapsulating the lbcc compiler has the advantage of decoupling depen-
dencies of applications on the current version of the Leibniz System. Suppose a
future version of the lbcc compiler uses a different interface. The changes made
to the interface only need to be reflected in the class Lbcc. From the viewpoint
of the application, there is no need to change source code since class Lbcc can
still keep the same interface. Since the Lbcc class is encapsulated once more in
class CNFSystem, it is possible to shift functionality between the CNFSystem to
the lbcc compiler without changing other parts of an implementation.
Class Lbcc is located in the leibniz package, together with C code of leibniz.c
that connects to both the Leibniz System and the Lbcc class via JNI. As a naming
convention, all fields and methods that access or are accessed by corresponding
C code through the JNI have the prefix lbcc. A shared library is compiled from
leibniz.c as described in the makefile in the same package. Technical details
on calling procedures of the lbcc compiler can be found in the reference manual
6.5 Class Lbcc                                                               57

of the Leibniz System [11]. A comprehensive introduction to the JNI technology
can be found at [8].
For reference, we list public and private methods that are not self explanatory.

getLogFileName() A private method returning the .log file’s name and rela-
     tive path to the shared library.

lbccInitValues() This method transfers all the “lbcc” parameters defined in
     class Lbcc to the shared library. A change of parameters can be conve-
     niently handled in the Java code. Recompiling the shared library is thereby
     rendered unnecessary.

lbccSolveProblem() Calls the lbcc compiler, which reads parameters from the
     .dat file and the CNF system to be solved from the .log file.

performTest() Solves a test problem and prints detailed information on the
     solution by calling lbccTest().

writeLeibnizParamsDat() Writes the leibnizparams.dat file that contains
     parameters for the lbcc compiler. The parameters of that file are discussed
     in detail in [11].

writeLogFile(CNFSystem, boolean) Writes a CNF system in a specified for-
     mat to a .log file, which the lbcc compiler reads as input.

Since class Lbcc is used internally by class CNFSystem, we omit an example here.
With all these classes at hand we are finally able to implement the differential
diagnostic system.
58   A Framework for Logic Computation
Chapter 7

An Implementation of the
Differential Diagnostic System

This implementation of the differential diagnostic system is deliberately kept
similar to the description of the algorithms in section 4.5. Thereby, not only the
one-to-one correspondence of concepts and classes becomes clear, but also the
alikeness of verbal descriptions and implementation of the algorithms.
The differential diagnostic system is implemented in the package diffDiagSys.
Despite some auxiliary classes that provide the graphical user interface, the classes
Diagnosis, Goal, LowCostAssignmentFile, and Test implement further con-
cepts of the previous chapters. Instead of describing these classes now, we would
first like to delve into the implementation of a core method of the differential
diagnostic system, namely the step of eliminating diagnoses (section 4.5.1).
The following code, taken from class DifferentialDiagnosticSystem makes use
of classes and variables that have not yet been introduced. However, the reader
will have no problem to follow the steps assumed the elimination step has been
01:   private void eliminateImpossibleDiagnoses(Record record) {
03:      // If there are no OPEN variables, we are done.
04:      if (record.noOpenVariables()) {
05:         // Declare pStar to be the only, i.e. final diagnosis.
06:         this.possibleDiagnoses = new TreeSet<Diagnosis>();
07:         this.possibleDiagnoses.add(pStar);
08:         return;
09:      }
10:      myGui.out("Current diagnosis: " + pStar);
11:      // Compute diagnoses that can be eliminated.
12:      Set<Diagnosis> toBeEliminated = new HashSet<Diagnosis>();

60                 An Implementation of the Differential Diagnostic System

13:   for (Diagnosis p : this.possibleDiagnoses) {
14:      if (p == pStar)
15:         continue;
16:      myGui.out("\nCheck whether diagnosis " + p
17:         + " can be eliminated.");
18:      CNFSystem S = new CNFSystem(Diagnosis
19:         .getSymptomVariables());
20:      /*
21:       * Create MINSAT instance. Simplification of formulas
22:       * is done by following the insertion rule (handled in
23:       * CNFSystem) and by reducing S after insertion of the
24:       * formulas
25:       */
26:      int i = 0;
27:      for (LearnedFormula formula : p.getHp()) {
28:          i = formula.getIndex();
29:         if (formula.isTypeD()) {
30:             // add (prop -> D)
31:             S.addIfThenClauses(new Literal(true,
32:                Variable.createOrGet("prop" + i, -1, 1)),
33:                formula, record);
34:         } else {
35:             // add (E -> conp)
36:             S.addIfThenClauses(formula, new Literal(true,
37:                Variable.createOrGet("conp" + i, 1, -1)));
38:         }
39:      }
40:      for (LearnedFormula formula : pStar.getHp()) {
41:          i = formula.getIndex();
42:         if (formula.isTypeD()) {
43:             // add (D* -> prop*)
44:             S.addIfThenClauses(formula, new Literal(true,
45:                Variable.createOrGet("propStar" + i, 1, -1)));
46:         } else {
47:             // add (conp* -> E*)
48:             S.addIfThenClauses(new Literal(true,
49:                Variable.createOrGet("conpStar" + i, -1, 1)),
50:                formula, record);
51:         }
52:      }
53:      S = S.getReducedCNFSystem(record);
54:      /*
55:       * Solve MINSAT instance. S is satisfiable by

56:          * construction.
57:          */
58:         int zdiff = S.solveMINSAT();
59:         zdiff += pStar.getDecisionStrategyValue()
60:            - p.getDecisionStrategyValue();
61:         myGui.out("Result: zdiff=" + zdiff);
62:         if ((zdiff > 0)
63:            || ((zdiff == 0) && (pStar.winsTieBreakOver(p)))) {
64:             myGui.out("Eliminate " + p.getId());
65:             toBeEliminated.add(p);
66:         } else {
67:             myGui.out(p.getId() + " cannot be eliminated.");
68:         }
69:       }
70:       // Eliminate impossible diagnoses for the case at hand.
71:       this.possibleDiagnoses.removeAll(toBeEliminated);
72:   }
At first it is checked whether all values of the current record are fixed to
True/False/Unavailable (line 4). If this is the case, the current tentative di-
agnosis pStar becomes the final diagnosis. The set P of possible diagnoses, here
denoted by possibleDiagnoses, is set to P = {p∗ }, and the method returns to
the main program. For the benefit of the user, messages are shown on the graph-
ical user interface via myGui.out, e. g. on line 10. In the following, we ignore
these messages.
Otherwise, a set of diagnoses that will be eliminated is created (line 12). Then,
for every diagnosis p = p∗ (lines 13-15), a CNF system S is created (line 18). For
each formula of Hp (lines 27-39) and Hp (lines 40-52), implications are inserted
into S as described in section 4.5.1. Correct insertion of the formulas is handled
by methods addIfThenClauses where needed. Notice that different signatures
for method addIfThenClauses are used, one to insert implications of type d → D
(lines 31-33 and 48-50), and another one to insert implications of type D → d
(lines 36-37 and 44-45). The former signature demands the current record to
be given such that the CNFSystem object knows which clauses have to be deleted
due to Unavailable values.
Finally, the whole CNF system is reduced according to the values of the cur-
rent record (line 53). The MINSAT problem defined by S is solved and the
objective value saved in zdiff (line 58). After adding the difference between
the two decision strategy values of p and p∗ to zdiff (lines 59-60), it is decided
whether p can be eliminated or not (lines 62-68). Finally, all diagnoses in the set
toBeEliminated are actually deleted from the possibleDiagnoses (line 71).
In the following, we do not discuss every method in detail. All methods that
62                      An Implementation of the Differential Diagnostic System

implement procedures of the previous chapters have been commented carefully.
Therefore, we only give an overview of the structure of the implementation. For
further details about the classes, please consult the corresponding part of the

7.1     Package diffDiagSys
First, we go through classes that represent concepts of previous chapters, i. e.
classes Diagnosis, Goal, LowCostAssignmentFile, and Test. After that, we
turn to DifferentialDiagnosticSystem, which contains the algorithms of the
differential diagnostic system. We conclude with a brief overview of classes deal-
ing with the graphical user interface.
Class Diagnosis contains any information related to a diagnosis p, i. e. a set of
optimized formulas Hp , optimized and non-optimized record sets Ap , A∗p , B p ,
and B ∗p , a decision strategy value dp , a prior probability PAp , and an identifier
(id). The class is able to automatically read in almost all these values from files
that can be created with the Leibniz System. Exceptions are the values for dp and
PAp , which for the purposes of this implementation of the differential diagnostic
system are set to reasonable dummy values. To the knowledge of the author,
there is no software available yet that computes a decision strategy as described
in section 4.3.3. We therefore simply substitute dp = 0 until a suitable software is
available. To read in the available data, class Diagnosis uses several classes that
can parse the input files. These classes are located in the package fileParser,
which will not be discussed here. They are, however, listed in the appendix.

Diagnosis(id) The constructor of this class asks for an id, which is then used to
     find the necessary input files. The directory structure is defined as follows.
     On the same level as package diffDiagSys, there is a data directory con-
     taining the file data/<id>/dataoptcc.trn of training records Ap and B p .
     The optimized record set is located at data/<id>/dataoptcc.opt. The
     formulas of set Hp are found in files data/<id>/AStar/datalsqcc.sep,
     containing the formulas that evaluate to True on records of set Ap , and
     data/<id>/BStar/datalsqcc.sep, containing the formulas that evaluate
     to True on the records of set B p . This directory structure is useful when
     the Leibniz System is used to create optimized records and formulas as
     described in chapter 5.

initializeSetsHPgandHFg() This method has to be called before the sets HP g
     and HF g are accessed.

winsTieBreakOver(Diagnosis) Here, the tie breaker T from section 4.3 is im-
7.1 Package diffDiagSys                                                          63

We omit obvious getter and setter methods as well as private methods that are
merely used internally.
Class Goal consists of a variable and of low cost assignment files Fg and Pg . A
goal is in one of the following states: FUTILE, IMPOSSIBLE, PROVED, or UNDECIDED.
The class merely consists of trivial getters and setters.
Class LowCostAssignmentFile is constructed from a list of records. If two or
more records of this list share the same values for all variables, only a represen-
tative record is kept along with the number of equal records in the list.
Class Test stores the name, cost, and obtainable values of a test Ti .

Test(name, cost, variables) Constructs a test which can obtain values for a
     Collection of variables.

Test.getAllTests() A static method that returns all tests constructed so far.

Test.reduceAllTestsByAssignedValues(record) If values have already been
     obtained by a test, they may be deleted from the set of obtainable val-
     ues of other sets. This method is used in order to correctly compute the
     effectiveness-score when selecting the next test.

obtainValues(myGui, record) This method implements Algorithm Obtain
     Values of section It creates a dialog where the user is asked to
     supply test values. For technical reasons, the main application myGui has
     to be given.

Finally, we describe class DifferentialDiagnosticSystem. It contains the main
method which is called at program start by default. This method creates a single
instance of class DifferentialDiagnosticSystem.

DifferentialDiagnosticSystem() The constructor is called without any pa-
     rameters. It creates instances for all diagnoses of the differential diagnosis.
     All tests are instantiated as well.

run() This method starts the main window of the application, which then reacts
     to actions performed by the user. Whenever the user clicks a button to
     perform the next step of the algorithm, method performNextStep is called.

performNextStep() Delegates program control to the next procedure according
     to Algorithm Differential Diagnostic System and the current state
     of computation.

eliminateImpossibleDiagnoses(record) This method has already been de-
     scribed at the beginning of this chapter.
64                     An Implementation of the Differential Diagnostic System

getAdditionalSymptomValues(record) Implements the procedure of section
getInitialRecord() Creates a dialog in which the user is asked for initial symp-
     tom values.
getTentativeDiagnosis(record) Evaluates all formulas and decides on a ten-
     tative diagnosis as described in step 2 of Algorithm Differential Diag-
     nostic System.
isFinalDiagnosis() Checks whether the set P of possible diseases has a cardi-
     nality of one.
proveConclusions(S, w, G, H) Implements Algorithm Prove Conclusions
     of section with CNF system S, record w, goal set G, and result set H.
selectTest(tests, S, w, G) This method selects a cost-effective test from a
     given set of tests with regard to a CNF system S, a record w, and a goal
     set G. It implements Algorithm Select Test of section

The classes Gui, and GuiDialogObtainValues implement methods for the main
and dialog windows, respectively. Class Gui is actually in control of the program
flow once it is called by class DifferentialDiagnosticSystem(). It associates
certain actionListeners with elements of the graphical user interface, which call
methods according to the actions of the user. We do not go into the details of
these classes and refer the reader to the appendix and [2]. Instead, we would like
to finish this thesis by showing screenshots of an example run of our differential
diagnostic system.

7.2     An Example Run
For the demonstration to follow we created a small test instance comprising of
fictitious patient records for which three different diagnoses, in the following
denoted by x, y, and z, are possible. The records follow a pattern suggested in
[10, exercise 8.9.2, p. 228]. With help of the Leibniz System, optimized records
and formulas were derived as described in chapter 5.
The program starts with a main screen as shown in figure 7.1. On the top right,
the steps of Algorithm Differential Diagnostic System (section 4.5) are
summarized and the current step is highlighted. On the left, some tabs list
information about the current record, formulas, and tests. Click on button “1.
Get Record” to start with the first step.
As seen in figure 7.2, a dialog appears asking the user for initial values. We do
so by arbitrarily selecting value True for x1 , x2 , and x3 while leaving the other
7.2 An Example Run                                                               65

variables open. For these values, a tentative diagnosis is made. According to the
vote total, the tentative diagnosis is x, see figure 7.3. Notice that on the left,
the “Formulas” tab was selected. For each diagnosis p, the set of formulas Hp
along with the vote total is listed. In order to keep the example concise, only four
formulas were chosen to be computed per disease. Of course, this implementation
also works with 40 formulas per diagnosis.
The next step to be performed is to eliminate diagnoses which are impossible for
the current record. In figure 7.4, the details of this step are shown. Diagnosis y
can already be eliminated since z diff > 0, as described in section 4.5.1. However,
diagnosis z has z diff = 0. It cannot be eliminated, because tie breaker T prefers z
over x because z has the largest “index”. Note that on the left, only the formulas
of the not yet eliminated diagnoses are shown.
In the following step, it is checked whether there is only one possible diagnosis
left, see figure 7.5. Since this is not the case, we need to get additional symptom
values. In figure 7.6, the results of Algorithm Prove Conclusions from section and of Algorithm Select Test from section are displayed. Notice
that goal “dx_01” is proved, although the corresponding formula number 1 of x
evaluates to undecided, as shown on the left. Notice further, on the right, that
“Test_3” got the highest rating and is thus selected to be the next test to be
performed. Arbitrarily, we select the value True to get to the next step.
The algorithms starts another iteration by making a new tentative diagnosis.
Again, it is x as shown in figure 7.7. In the following elimination step, it is
possible to eliminate diagnosis z for the values at hand, as seen in figure 7.8.
Hence, x is the final diagnosis and the algorithms stops, see figure 7.9.
66   An Implementation of the Differential Diagnostic System

      Figure 7.1: The program starts.

     Figure 7.2: Entering initial values.
7.2 An Example Run                                              67

             Figure 7.3: Deciding upon a tentative diagnosis.

                     Figure 7.4: Eliminating diagnosis y.
68                    An Implementation of the Differential Diagnostic System

     Figure 7.5: Checking whether the tentative diagnosis is the final one.

               Figure 7.6: Getting additional symptom values.
7.2 An Example Run                                          69

                 Figure 7.7: Starting another iteration.

                     Figure 7.8: Eliminating diagnosis z.
70         An Implementation of the Differential Diagnostic System

     Figure 7.9: Concluding on the final diagnosis x.
7.3 Summary and Outlook                                                        71

7.3     Summary and Outlook
This is the first implementation of a differential diagnostic system as proposed by
Truemper [10]. It is one of two major components used for construction of differ-
ential diagnostic systems. The first component has already been implemented in
the Leibniz System, except for the parts concerning the computation of a decision
With this thesis, a further step has been taken to reach the aim of an automated
construction of differential diagnostic systems. Both the implementation and
the user interface are designed for clarity. There is still room for improvement
concerning execution speed. For example, it is not necessary to prove conclusions
over and over again in each iteration, provided that no unexpected Unavailable
value occurs. However, it is the hope of the author that this work will be further
Parts of the implementation of the differential diagnostic system are of general
use when implementing logic-based intelligent systems. All source code will be
available at no charge over the internet and may be used and modified as desired.

To top