>>>>DOC<<<< PRACTICAL EXERCISE: A SIMPLE EXPERT SYSTEM SHELL, WRITTEN IN PROLOG Peter Ross 1. INTRODUCTION This describes how to use a simple expert system shell, and gives a number of exercises based on it. The shell is modelled loosely on Teknowledge's KS-300 (and so is called ks299, since it's not quite the full shilling). The shell can be found in UNIX: /u4/peter/prolog/ks299/int EMAS: ECMI01.KS299_INT Neither version needs any external utilities. A sample knowledge base can be found in UNIX: /u4/peter/prolog/ks299/wine EMAS: ECMI01.KS299_WINE To load them, start up Prolog, load the files (load the shell first), and then ?- find wine. Answer the questions. Read on to find out what other possibilities there are. 2. CONSTRUCTING A KNOWLEDGE BASE The knowledge base is made up of rules of the general form RULE: if PREMISE then CONCLUSION. where [1] RULE is a Prolog atom (such as 'rule391'), [2] PREMISE is either [2.1] a simple proposition of the form THING = VALUE or THING is known or THING is unknown [2.2] a combination of simple propositions built up using 'and' and 'or', where 'or' binds more tightly than 'and' [3] CONCLUSION is either [3.1] a simple conclusion of the form THING = VALUE or THING = VALUE cf CONFIDENCE [3.2] a combination of simple conclusions built up using 'and' only. THING and VALUE can be any Prolog terms of precedence less than 600. They can be atoms, such as 'colour' or 'red', or they can be Prolog variables (the scope of such a variable being the rule within which it appears), or they can be compound terms. A Prolog operator 'of', with precedence 599 and associativity defined so that 'A of B of C' is regarded as 'A of (B of C)', has already been declared for convenience. This means that THING can take the very handy form 'ATTRIBUTE of OBJECT'. CONFIDENCE must be a number between 0 and 1000 inclusive (on EMAS it must be an integer); it expresses the degree of belief in the conclusion. A CONFIDENCE of 0 means "no confidence at all", and a CONFIDENCE of 1000 means "completely sure". The way in which the system manipulates these confidence factors is explained below. Here are some examples of rules (do not forget the full stop at the end of each rule): whizzo: if today=tuesday then shops_open=no. rule002: if age is unknown and texture of skin = wrinkled then estimated_age = old cf 600. x: if spiral_bound = yes and colour of cover = grey or colour of cover = red then type of document = departmental_paper cf 770 and type of document = private_paper cf 400 and source of document = dept_of_AI cf 910. You must also tell the system which of those THINGs that appear in the PREMISE parts of the rules it is allowed to ask the user about. To do this you must provide assertions of the form QUESTION finds THING. where QUESTION is a question (but without the final question mark), in the form of a Prolog atom. This usually takes the form of a string delimited by single quotes. The commonest mistake is to include an apostrophe, alias single quote, as part of the string. You can do so provided you double it. For example, 'What is the colour of the cover (red/grey/other)' finds colour of cover. 'What is the patient''s age (old/young/middling)' finds age. Note the doubled apostrophe in the second example. 3. HOW IT WORKS The system works by backward chaining. When asked to 'find THING' it looks for rules that conclude about the VALUE of THING and attempts to establish the PREMISE of each such rule by a depth-first recursive application of the rules. It looks at all the non-recursive rules first, taking those that conclude about the VALUE of THING in their order of appearance. Thereafter, it looks at all the recursive rules that conclude about the VALUE of THING. A recursive rule is one in which some THING is referred to both in the PREMISE and in the CONCLUSION. It looks at non-recursive rules first in the hope of establishing something about the THING in question, so that an attempt to use a recursive rule in due course will not trigger a recursive use of the rule. If the system is seeking the VALUE(s) of a THING, and finds that it is allowed to ask the user, then it will ask the user immediately and will never try to use any rule to establish the VALUE(s). If it is not allowed to ask the user it will use the rules in an attempt to find the VALUE(s). A premise of the form 'THING is known' will be satisfied if the system already knows or can deduce or can ask the VALUE(s) of thing. A premise of the form 'THING is unknown' will only be satisfied if the system knows no VALUE, cannot deduce one and cannot ask one. Either of these types of premise will trigger an attempt to find the VALUE(s). Thus 'THING is unknown' will always fail to hold if the user can be asked about it; this type of premise does not mean "THING is currently unknown", it means "THING is unknowable". The system uses the confidence factors in the following way. Given a rule, it determines the confidence factor of the premise of the rule. This is taken to be the confidence factor of each of the conclusions, possibly modified by any confidence factor specified in the conclusion. An example shows how this is worked out. Suppose that the system is looking at the rule rule71: if A then B cf b and C. It works out the cf of A. Suppose it is a. It takes this to be the cf of C, since C appears without any qualifying confidence factor. It also records B, but with a modified cf of a + b - a*b/1000. This odd expression is really 1000*(1 - (1-a/1000)*(1-b/1000)) - if you consider a and b to be probabilities scaled up to lie between 0 and 1000, then this is just the rescaled probability of the disjunction of A and B on the (wholly unreasonable) assumption that they are independent. This dreadful theoretical flaw is nevertheless used because it is simple and it works, provided that the depth of search through the rules is not too great. In attempting to determine a conjunction ('A and B') of premises, the system will work through them left to right. The confidence factor of a conjunction is taken to be the minimum of the confidence factors of the individual parts. In working left to right, however, the system will give up on the whole conjunction as soon as it finds that the confidence factor of the conjunction so far has fallen below 200. For instance, if it is trying to establish 'colour=green and weight=heavy' it will start trying to determine the colour. If it cannot establish that 'colour=green' with a confidence factor of at least 200, it will not go on to trying to determine the weight. In attemtping to determine a disjunction ('A or B') of premises, the system will work through them left to right. The confidence factor of a disjunction is taken to be the maximum of the confidence factors of the individual parts. In working left to right, however, the system will stop as soon as it has done enough to establish that the disjunction will have a confidence factor of 1000. The system assumes that anything it is told by the user has a confidence factor of 1000. One consequence of this is that if it is trying to establish a premise such as 'colour=green or smell=rotten', and the colour can be asked of the user, and the user says it is green, then the system will know that 'colour=green' with confidence factor 1000 and so will not go on immediately to investigate the smell. 4. USER INPUT When the system asks a question, it types it out, followed by a question mark and then a prompt ==>> on a new line. You can answer the question. Your answer must end with a full stop; the system expects your answer to be a valid Prolog term. It is a good idea to include a hint about what the expected answers are in the question (as in the examples above). Instead of answering the question, your reply can be one of: [1] help. to get a short text summarising the other possible responses [2] show THING. to get an account of what the system knows about THING so far. The information is given as a set of assertions of the form THING=VALUE cf CONFIDENCE because LIST_OF_REASONS or THING is unknown [3] show RULE. to have the system print out that rule for you [4] why. to get a short account of why the system is asking you that question, or [5] :- COMMAND. where COMMAND is any Prolog command at all, which will then be run. 5. UTILITIES A Prolog predicate tidy(OldFile,NewFile) is provided. It reads the knowledge base in OldFile and prints it out beautifully into NewFile in a standard format. All the RULE tags get changed into tags of the form 'ruleN' where N is a number counting upward from 1. OldFile and NewFile must be different file names; if not, you will get a message of complaint. Two Prolog predicates are provided to permit you to obtain a simple trace of the operation of the system. The command watch. switches the interpreter's tracing on. The tracing messages are of the form ****** Invoking RULE or +++ added THING=VALUE cf CONFIDENCE because LIST_OF_REASONS or --- deleted THING=VALUE cf CONFIDENCE because LIST_OF_REASONS In general there will be quite a lot of these additions and deletions, because the interpreter modifies its belief about 'THING=VALUE' by a process of deletion followed by addition of the new modified belief. The system does not explicitly tell you if the invocation of a rule was successful. If it was, of course, then you would see the conclusion(s) being added. The command nowatch. turns the interpreter's tracing off. 6. EXPERT SYSTEMS EXERCISES In this order: {a} Make up a simple knowledge base and try it. {b} Show that a recursive rule can trigger a recursive use of the rule (N.B. on UNIX you can interrupt by typing ctrl-C; on EMAS you can interrupt by typing ESC then A). {c} Can the order of the rules affect the final result? Produce an example to show that it can, or try to justify the claim that it cannot. Try to create a knowledge base that you could use to demonstrate that the system's use of confidence factors is unsound. It may help to make use of recursive rules (see the article "Inside an expert system" by Jadzia Cendrowska and Max Bramer, in 'Artificial Intelligence: Tools Techniques and Applications', eds. Tim O'Shea and Marc Eisenstadt, pub. Harper and Row). {d} Make up an elaborate knowledge base. In use, would the sequence of questions make sense to a user who knew nothing of the subject matter? Could you improve matters by judicious choice of the THINGs that the system tries to investigate? {e} What are the deficiencies of the system in use? {f} Design useful features to add to the system. Justify your choices. If they involve extensions to the syntax of the rules, design those extensions. 7. PROLOG EXERCISES Not necessarily in this order: {g} Explain how the code works. {h} It would be nice if the interpreter knew what the valid replies to a question could be, and checked a user's reply accordingly. Make changes in the interpreter and in the syntax of knowledge bases to do this. Test them. {i} It would be nice if the user were allowed to qualify the information he gives in reply to a question with a suitable confidence factor. The system should still assume a confidence of 1000 if the user does not give such a factor. Implement this change. Test it. {j} The system cannot make decent use of values which are numbers. For example, you cannot give a rule of the form ... if age > 40 and age < 65 then ... Design and implement suitable changes. Test them {k} It would be useful to allow premises which involved negation in some way, e.g. ... if colour is not green ... Designing this requires a bit of thought. Some things can only have one value (for example, if you know that a colour is red, then you know that it isn't green), but some things can have more than one value (for example, if a person has Canadian nationality, then you cannot deduce that he does not have British nationality too). Design and implement suitable changes. Test them. {l} Extend the interpreter and the syntax of knowledge bases to allow the system to do some forward chaining whenever the user replies to a question. Give an example or two to show that this can mean that the system asks fewer questions. {m} Extend the interpreter and the syntax of knowledge bases to allow for the provision of explanatory texts, so that a usr can reply 'explain.' to a question. Test it. {n} Extend the interpreter and the syntax of knowledge bases so that an arbitrary Prolog command (e.g. (write(X),nl)) can be included in the conclusion of a rule. Test it. {o} Make any other desirable improvements you can think of, such as wonderful natural language features allowing the user some flexibility in the format of his replies to questions. Test them. >>>>INT<<<< % File: /u4/peter/prolog/ks299/int % Author: Original by Steven Hardy. Reworked by Peter Ross. % Updated: 6 Sep 84 by Peter Ross % Purpose: a simple rule interpreter in the style of KS-300 % Original code courtesy of Teknowledge Inc. - so don't make use of it % without acknowledging them. % The rule base is made up of assertions of the form % RULE: if PREMISE then CONCLUSION. % where RULE is an atom, % PREMISE is a simple proposition of the form % THING = VALUE % or THING is known % or THING is unknown % or is a combination of simple propositions % built up using "and" and "or", where "or" % binds tighter than "and", % CONCLUSION is a simple conclusion of the form % THING = VALUE % or THING = VALUE cf CONFIDENCE % or is a combination of simple conclusions % built up using "and" only. % THING and VALUE can be any Prolog term of precedence less than 600. % An operator 'of' has been defined for convenience; it has precedence 599. % It allows you to have THINGs of the form ATTRIBUTE of OBJECT. % CONFIDENCE should be a number between 0 (no confidence at all) and 1000 % (completely sure) inclusive. % % You must also provide assertions of the form % QUESTION finds THING. % where QUESTION is an atom giving a question to ask the user to get a value % for the attribute. A question mark will be supplied by the system. If the % system can ask the user for a value, he will be asked as soon as the need is % found, and only once. Valid replies are: % why. % asking for a MYCIN-like justification in terms of the goal tree, or % show THING. % asking for what is known about a THING, or % show RULE. % asking to see the rule identified by the given tag, or % :- Command. % asking for some arbitrary Prolog command to be run, or % anything else, which the system will assume to be the value sought, with cf % 1000 because you said so. % % There are three extra useful predicates: % watch switches on printing of the recording of attribute % values % nowatch turns it off % tidy(Old,New) reads file Old and writes file New (not equal) % so that New contains a nicley laid out version of the % rule base in Old. :- op(980, fx, [sought, find, invoke, seek]). :- op(980, xfy, [concludes, uses, refersto]). :- op(975, xfy, :). :- op(950, fx, if). :- op(949, xfy, then). :- op(948, xfy, because). :- op(800, xfy, and). :- op(750, xfy, or). :- op(725, xfy, cf). % cf => certainty factor :- op(600, xfy, finds). :- op(600, fx, show). :- op(599, xfy, of). find THING :- prompt(Old, ' ==>> '), abolish(active,1), abolish(sought,1), abolish(because,2), seek THING, show THING. seek THING :- sought THING, !. seek THING :- QUESTION finds THING, write(QUESTION), write('?'), nl, read(REPLY), ( REPLY = why -> why, seek THING ; REPLY = help -> help, seek THING ; REPLY = show SOMETHING -> show SOMETHING, seek THING ; REPLY = (:- COMMAND) -> do_without_fail(COMMAND), seek THING ; assert(sought THING), note(THING = REPLY cf 1000 because ['you said so']) ), !. seek THING :- assert(sought THING), ( nonrecursive(RULE, THING) ; recursive(RULE, THING) ), notice(RULE), invoke RULE, fail. seek THING. do_without_fail(COMMAND) :- COMMAND, !. do_without_fail(_). invoke RULE :- RULE : if PREMISE then CONCLUSION, PREMISE cf CONFIDENCE, ( CONFIDENCE < 200 ; note(CONCLUSION cf CONFIDENCE because [RULE]) ), !. notice(RULE) :- (watching -> write('****** Invoking '), write(RULE),nl; true), asserta(active(RULE)). notice(RULE) :- retract(active(RULE)), fail. (P1 or P2) cf CONFIDENCE :- P1 cf C1, ( C1 = 1000 -> CONFIDENCE = C1 ; P2 cf C2, ( C1 > C2 -> CONFIDENCE = C1 ; CONFIDENCE = C2 ) ), !. (P1 and P2) cf CONFIDENCE :- P1 cf C1, ( C1 < 200 -> CONFIDENCE = C1 ; P2 cf C2, ( C1 < C2 -> CONFIDENCE = C1 ; CONFIDENCE = C2 ) ), !. THING = VALUE cf CONFIDENCE :- seek THING, ( THING = VALUE cf CONFIDENCE because REASON ; CONFIDENCE = 0 ), !. THING is known cf CONFIDENCE :- seek THING, ( ( THING = VALUE cf C because REASON, C > 200 ) -> CONFIDENCE = 1000 ; CONFIDENCE = 0 ), !. THING is unknown cf CONFIDENCE :- ( THING is known cf 1000 -> CONFIDENCE = 0 ; CONFIDENCE = 1000 ), !. nonrecursive(RULE, THING) :- RULE concludes THING, not(RULE uses THING). recursive(RULE, THING) :- RULE concludes THING, RULE uses THING. RULE concludes THING :- RULE : if PREMISE then CONCLUSION, CONCLUSION refersto THING. RULE uses THING :- RULE : if PREMISE then CONCLUSION, PREMISE refersto CONCLUSION. note((P1 and P2) cf CONFIDENCE because REASON) :- note(P1 cf CONFIDENCE because REASON), note(P2 cf CONFIDENCE because REASON). note(THING = (VALUE1 and VALUE2) cf CONFIDENCE because REASON) :- note(THING = VALUE1 cf CONFIDENCE because REASON), note(THING = VALUE2 cf CONFIDENCE because REASON). note(THING = (VALUE cf CONFIDENCE1) cf CONFIDENCE2 because REASON) :- note((THING = VALUE) cf CONFIDENCE1 cf CONFIDENCE2 because REASON). note((PROPOSITION cf CONFIDENCE1) cf CONFIDENCE2 because REASON) :- note(PROPOSITION cf CONFIDENCE1 cf CONFIDENCE2 because REASON). note(THING = (VALUE cf CONFIDENCE1) cf CONFIDENCE2 because REASON) :- note(THING = VALUE cf CONFIDENCE1 cf CONFIDENCE2 because REASON). note(THING is unknown cf CONFIDENCE because REASON). note(PROPOSITION cf C1 cf C2 because REASON) :- C3 is (C1 * C2)/1000, note(PROPOSITION cf C3 because REASON). note(PROPOSITION cf C1 because [REASON1]) :- remove(PROPOSITION cf C2 because REASON2), !, C3 is C1 + C2 - (C1 * C2)/1000, add(PROPOSITION cf C3 because [REASON1|REASON2]). note(PROPOSITION cf C1 because [REASON1]) :- add(PROPOSITION cf C1 because [REASON1]). remove(Item) :- retract(Item), (watching -> write('--- deleted '), write(Item), nl; true). add(Item) :- assert(Item), (watching -> write('+++ added '), write(Item), nl; true). why :- listof(R,active(R), [CURRENT|OTHERS]), tab(8), write('Your answer to this question will help me determine if the'), nl, tab(16), write('following rule is applicable:'), nl, show CURRENT, ( OTHERS = [] ; nl, tab(8), write('Other relevant rules are: '), write(OTHERS), nl ). help :- tab(8), write('When you get the prompt ==>> vaild replies are:'), nl, tab(16), write('- an answer to the question'), nl, tab(16), write('- why. to get a justification'), nl, tab(16), write('- show RULE. to have that rule printed'), nl, tab(16), write('- show THING. to see what is known about it'), nl, tab(16), write('- (:- COMMAND). to have a Prolog command run'), nl. P1 and P2 refersto THING :- ( P1 refersto THING ; P2 refersto THING ), !. P1 or P2 refersto THING :- ( P1 refersto THING ; P2 refersto THING ), !. PROPOSITION cf CONFIDENCE refersto THING :- PROPOSITION refersto THING, !. THING = VALUE refersto THING :- !. THING is STATUS refersto THING :- !. show RULE :- RULE : if PREMISE then CONCLUSION, !, tab(8), write(RULE), write(':'), nl, tab(10), write('if '), pwrite(PREMISE, 16), nl, tab(10), write('then '), pwrite(CONCLUSION, 16), nl. pwrite(P1 and P2, Indent) :- !, pwrite(P1, Indent), nl, tab(Indent), write('and '), pwrite(P2, Indent). pwrite(P, _) :- write(P). show THING :- G = (THING = VALUE cf CONFIDENCE because R), listof([CONFIDENCE, G], G, GS), !, sort(GS, SGS), tab(8), write('This is what is known about '), write(THING), write(':'), nl, bwrite(SGS). show THING :- sought(THING), !, tab(8), write(THING), write(' is unknown.'), nl. watch :- assert(watching). nowatch :- abolish(watching, 0). bwrite([]). bwrite([[A,B]|C]) :- bwrite(C), tab(16), write(B), nl. tidy(OLD, NEW) :- (OLD == NEW -> write('Files must differ'), nl, fail; true), (exists(OLD) -> true; write('First file does not exist'), nl, fail), assert(rulenumber(1)), see(OLD), tell(NEW), repeat, read(FACT), tidyprocess(FACT), seen, told, abolish(rulenumber, 1). tidyprocess(end_of_file). tidyprocess(FACT) :- output(FACT), nl, nl, !, fail. output(NAME : if PREMISE then CONCLUSION) :- retract(rulenumber(N)), succ(N,N1), assert(rulenumber(N1)), write(rule), write(N), write(':'), nl, tab(8), write('if '), pwrite(PREMISE, 14), nl, tab(8), write('then '), pwrite(CONCLUSION, 14), write('.'), nl. output(QUESTION finds THING) :- write(''''), write(QUESTION), write(''''), nl, tab(4), write('finds '), write(THING), write('.'), nl, nl. output(P) :- write(P), write('.'), nl. % listof/3 behaves very like bagof/3, except that the collection of % answers it comes up with will never be empty. It will fail instead. listof(X,P,Set) :- bagof(X,P,Set), !, X \== []. % sort/2 is a vrsion of Hoare's "Quicksort" algorithm designed to sort % terms of the form THING=VALUE cf CONFIDENCE because LIST into % decreasing order of CONFIDENCE. The only specific reference to this % kind of term occurs within the definition of lesser/2, which succeeds % only if its first argument is 'less' than its second. So, you could % easily adapt sort/2 to many other sorting jobs. sort(L,Sorted) :- sort(L,[],Sorted). sort([X|L],R0,R) :- partition(L,X,L1,L2), sort(L2,R0,R1), sort(L1,[X|R1],R). sort([],R,R). partition([X|L],Y,[X|L1],L2) :- lesser(X,Y), !, partition(L,Y,L1,L2). partition([X|L],Y,L1,[X|L2]) :- !, partition(L,Y,L1,L2). partition([],_,[],[]). lesser(X = _ cf C1 because _, X = _ cf C2 because _) :- C1 < C2. >>>>WINE<<<< % File: /u4/peter/prolog/ks299/wine % Author: Peter Ross % Updated: 6 Sep 84 % Purpose: a simple knowledge base for the KS299 expert system shell 'What is the overall taste (delicate/average/strong)' finds tastiness of meal. 'Is there a sauce (yes/no)' finds has_sauce. 'What sort of sauce (spicy/creamy/tomatoey/other)' finds sauce of meal. 'What body do you prefer (light/medium/full)' finds preferred_body of wine. 'What colour do you prefer (red/white)' finds preferred_colour of wine. 'What sweetness do you prefer (dry/medium/sweet)' finds preferred_sweetness of wine. 'What is the main bit (meat/poultry/fish)' finds main_bit of meal. 'Is it a veal dish (yes/no)' finds has_veal. 'Is it a turkey dish (yes/no)' finds has_turkey. rule1: if tastiness of meal=delicate then body of wine=light cf 800. rule2: if tastiness of meal=average then body of wine=light cf 300 and body of wine=medium cf 600 and body of wine=full cf 300. rule3: if tastiness of meal=strong then body of wine=medium cf 400 and body of wine=full cf 800. rule4: if has_sauce=yes and sauce of meal=spicy then body of wine=full cf 1000 and special_characteristics of meal=spiciness cf 1000. rule5: if has_sauce=yes and sauce of meal=creamy then body of wine=medium cf 400 and body of wine=full cf 600. rule6: if body of wine is unknown and preferred_body of wine is unknown then body of wine=medium cf 1000. rule7: if preferred_body of wine=X and body of wine is known then body of wine=X cf 200. rule8: if preferred_body of wine=light and body of wine=full then body of wine=medium cf 1000. rule9: if preferred_body of wine=full and body of wine=light then body of wine=medium cf 1000. rule10: if preferred_body of wine=X and body of wine is unknown then body of wine=X cf 1000. rule11: if main_bit of meal=meat and has_veal=no then colour of wine=red cf 900. rule12: if main_bit of meal=poultry and has_turkey=no then colour of wine=white cf 900 and colour of wine=red cf 300. rule13: if main_bit of meal=fish then colour of wine=white cf 1000. rule14: if main_bit of meal is known and has_sauce=yes and sauce of meal=tomatoey then colour of wine=red cf 1000. rule15: if main_bit of meal=poultry and has_turkey=yes then colour of wine=red cf 800 and colour of wine=white cf 500. rule16: if main_bit of meal is unknown and has_sauce=yes and sauce of meal=creamy then colour of wine=white cf 400. rule17: if colour of wine is unknown and preferred_colour of wine is unknown then colour of wine=red cf 500 and colour of wine=white cf 500. rule18: if preferred_colour of wine=X and colour of wine is known then colour of wine=X cf 200. rule19: if preferred_colour of wine=X and colour of wine is unknown then colour of wine=X cf 1000. rule20: if has_sauce=yes and sauce of meal=sweet then sweetness of wine=sweet cf 900 and sweetness of wine=medium cf 400. rule21: if sweetness of wine is unknown and preferred_sweetness of wine is unknown then sweetness of wine=medium cf 1000. rule22: if preferred_sweetness of wine=X and sweetness of wine is known then sweetness of wine=X cf 200. rule23: if preferred_sweetness of wine=X and sweetness of wine is unknown then sweetness of wine=X cf 1000. rule24: if preferred_sweetness of wine=dry and sweetness of wine=sweet then sweetness of wine=medium cf 1000. rule25: if preferred_sweetness of wine=sweet and sweetness of wine=dry then sweetness of wine=medium cf 1000. rule26: if colour of wine=red and body of wine=medium and sweetness of wine=medium or sweetness of wine=sweet then wine=gamay cf 1000. rule27: if colour of wine=white and body of wine=light and sweetness of wine=dry then wine=chablis cf 1000. rule28: if colour of wine=white and body of wine=medium and sweetness of wine=dry then wine=sauvignon_blanc cf 1000. rule29: if colour of wine=white and body of wine=medium or body of wine=full and sweetness of wine=dry or sweetness of wine=medium then wine=chardonnay cf 1000. rule30: if colour of wine=white and body of wine=light and sweetness of wine=dry or sweetness of wine=medium then wine=soave cf 1000. rule31: if colour of wine=white and body of wine=light or body of wine=medium and sweetness of wine=sweet or sweetness of wine=medium then wine=riesling cf 1000. rule32: if colour of wine=white and body of wine=full and special_characteristics of meal=spiciness then wine=gewurtztraminer cf 1000. rule33: if colour of wine=white and body of wine=light and sweetness of wine=medium or sweetness of wine=sweet then wine=chenin_blanc cf 1000. rule34: if colour of wine=red and body of wine=light then wine=valpolicella cf 1000. rule35: if colour of wine=red and sweetness of wine=dry or sweetness of wine=medium then wine=cabernet_sauvignon cf 1000 and wine=zinfandel cf 1000. rule36: if colour of wine=red and body of wine=medium and sweetness of wine=medium then wine=pinot_noir cf 1000. rule37: if colour of wine=red and body of wine=full then wine=burgundy cf 1000.