AI Toolkit: support site for
Intelligent Systems for Engineers and Scientists, Third Edition

Intelligent Systems for Engineers and Scientists, Third Edition
This page is under development and is regularly maintained.
Please let me know if you spot any mistakes; my contact details are on my home page.


Back to top

My professorial lecture "Artificial intelligence for all"

View my professorial lecture on YouTube (opens in a new window)

Research in artificial intelligence is based around the goal of mimicking human intelligence in a computer. The extent to which this goal has been achieved is open to debate, but the real success story is in the benefits that the emergent techniques are delivering for business and society. This lecture introduces the principles of artificial intelligence, with a focus on practical applications that range from the control of manufacturing processes to the screening of mouth cancer. No prior knowledge of computer science or artificial intelligence is assumed.

Back to top

AI-2014 special session "Hybrid systems: the future of AI?" 10 December 2014

My slides in PDF format (3 MB) (opens in a new window)

A wide range of techniques has emerged from the field of artificial intelligence including rules, frames, model-based reasoning, case-based reasoning, Bayesian updating, fuzzy logic, multiagent systems, swarm intelligence, genetic algorithms, and neural networks. They are all ingenious, practical, and useful in narrow contexts. It will be argued in this presentation that a truly intelligent system needs to draw on a variety of these approaches within a hybrid system. Five distinct ways to enhance one technique with another will be identified. Several practical examples of hybrids will be presented, ranging from medical diagnosis to the control of specialised manufacturing processes. This presentation will start at an introductory level and is intended to be accessible by all delegates.

Back to top

DARBS multiagent software

DARBS (Distributed Algorithmic and Rule-based Blackboard System) is open-source software for building multiagent systems based on the blackboard model.
Back to top

winGA software for genetic algorithms, courtesy of the Open University

The winGA software runs under most versions of Windows. It is self-contained and does not affect any system or registry files. To install it, simply download this zip file and unzip to a convenient location. Launch the software by double clicking "winGA.exe". (This link was fixed on 12/12/2013 – thanks for the feedback!)

This software was developed for the former Open University course T396: Artificial Intelligence for Technology. The Open University takes no responsibility for the software, e.g. maintenance of code, answering queries, or accountability for its use for any purpose. © 2013 The Open University

Back to top

Electronic Study Guide, courtesy of the Open University

The Electronic Study Guide (eSG) contains two chapters. Chapter 1 covers the use of agents in the Flex programming environment. Chapter 2 covers genetic algorithms and the use of the win-GA software (above). I recommend that you follow the Flex/Flint guidance and exercises below before you attempt Chapter 1. The eSG is platform-independent and will run in any modern browser. You can either:
  1. download this zip file and unzip to a convenient location or
  2. run the eSG on-line (opens in a new window)
This software was developed for the former Open University course "T396: Artificial Intelligence for Technology". Some details may no longer apply (e.g. references to the T396 CD-ROM and program group). The Open University takes no responsibility for the software, e.g. maintenance of code, answering queries, or accountability for its use for any purpose. © 2013 The Open University
Back to top

Flex/Flint software download from Logic Programming Associates Ltd.

To run the examples in the book, you first need to install Flex and Flint from Logic Programming Associates Ltd.
Download your free software here.
Note that you will need your copy of the book to hand in order to verify ownership.
Back to top

Example Flex/Flint programs

Here are a few example programs; more will follow in the coming months!
Back to top

Getting started in Flex and Flint

The software

Flex is a commercial software product, which you may regard as either one, two or three products, depending on your point of view. At the heart of Flex is a Prolog system, distributed by LPA Ltd as WIN-PROLOG. LPA Ltd also supplies an AI toolkit called Flex and uncertainty-handling software called Flint, which are sold as extensions to WIN-PROLOG. Flex and Flint are both automatically loaded in your version of the software.

The console

When you start up Flex, you will be presented with a window called the Console. This is where you can type commands that you want to be acted upon immediately. The 'prompt' (i.e. the symbol encouraging you to type something) looks like this:

| ?-

The question mark indicates that whatever you type at this prompt will be interpreted as a query. Although you can delete the question mark, it makes no difference – whatever you type at the Console will be interpreted in the same way. When you enter a query, what is being queried is a database of things Flex knows about, i.e. its knowledge base. Even before you have supplied any knowledge, the knowledge base is not empty; it contains information about the current status of Flex itself, for example.

Try typing the following at the prompt (not forgetting the full stop):

statistics(X, Y) .

Notice that the case (i.e. capitalization) of your input is important, but the spaces are optional. The full stop is important as it marks the end of the query. Now press the RETURN key and you will receive a message similar to this:

X = runtime ,
Y = {10175706, 10175706} ;

Your query can be regarded as a relation in Flex, and the system has tried to find a pair of values for X and Y. However, this isn't the only solution. To see the others, keep hitting the space bar. If, after you have seen one or more such solutions, you hit the RETURN key, the system will stop looking for alternative solutions.

You may find it useful to maximize the screen space available by clicking the mouse on the middle box in the top right-hand corner of the main application window. Click on the middle box again to return to using only part of the screen. Similarly, you can increase the size of the Console window by clicking on the middle box towards the top right-hand corner of the window. The Console, and any other windows that you open, will now fill the screen. Click on the same box to return to normal size windows.

Using files

To open a file, select Open from the File menu and select a file. Select Flex/Flint source files (*.ksl), as the default is Prolog (*.pl). The files listed correspond to one folder only, which is your default folder. If you select a different folder, that will become your default even if you press the Cancel button. Flex programs must have the extension .KSL. Prolog programs are given the extension .PL, although in practice they can have any extension.

In order to define a Flex program, you need to create and save a file. Go to the File menu and select New. This will create a new window called 'Untitled', where you can type your Flex program. Before using a Flex program you must save it with a .KSL extension, for example MYPROG.KSL. This is important because when you come to compile your program, Flex will need to decide whether to interpret the code as Flex or Prolog commands. If the file name ends in .KSL the file is interpreted as Flex code, otherwise it is interpreted as Prolog code.

When you are ready to test your program, select Compile from the Run menu. Note that your program should be in the active window. A window can be activated by clicking on it, or by selecting it in the Window menu, where the active window is marked with a tick. (Compile All will compile every program that appears in an open Flex window.) A report on the compilation (whether successful or not) will appear in the Console. If you wish to see the compilation report in a separate window called 'Messages', choose Preferences… from the Options menu. In the field titled Report Stream, click on the radio button for Message Log; then leave the Preferences window by clicking on OK. If you try to compile Flex code in an Untitled window without saving, it will be interpreted as Prolog code, and the compilation will fail (because it isn't Prolog code ).

The forward-chaining dialog box

Under the Flex menu, you will find an option called Forward Chaining… . If you select this option, you will be presented with a dialog box containing a variety of buttons and messages. You might occasionally find this facility useful for testing the effects of some of the alternative rule-firing strategies that are covered in Chapter 3. However, it is generally better to explicitly code the desired behaviour in your Flex program, so as to explicitly record how you are using the program. You can specify all of your chosen forward-chaining options in the definition of the 'ruleset' in the Flex program. Thus the Forward Chaining dialog box is not actually needed. Here's an example of explicit coding in a Flex program:

ruleset boiler
contains all rules ;
select rule using first come first served ;
update ruleset by removing each selected rule;
initiate by doing restart .

The main advantages of explicitly coding the desired behaviour for yourself are:

  1. The preferred rule-firing mechanism becomes part of your program, and is thus stored alongside the rest of your program. The program is now complete, and does not require any separate instructions on how it should be run.
  2. By defining a Flex 'action', which invokes the ruleset, you can automatically perform any other operations that you want immediately before or after the rules have cycled.
  3. You are less likely to create an infinite loop.

Making it happen

Once you have built your first set of Flex rules, relations, frames, etc., you will want to run the program. I have already advised against relying on the Forward Chaining dialog box for this purpose. Typically, you will define an explicit Flex action, for example:

action start;
do invoke ruleset boiler
and write('Finished ').

Once you have compiled your program, you can then type


in the Console window to start the program.

If at any time you are having difficulty in making Flex behave as you expect, you may find it helpful to:

  1. issue the command initialise (this command, described below, clears the system's rule and knowledge base)
  2. re-compile your program file(s).

A vital space

Care needs to be taken when a Flex definition ends with an integer number. For example, it is quite normal in Flex to attach a priority value or 'score' to a rule. The following rule would not work:

rule r1
if water_level is low
then write('** open the control valve **')
score 10.

The problem here is that the full stop (or period) is not recognized as such. Instead it is interpreted as a decimal point associated with the number 10. The correct way to write this rule would be:

rule r1a
if water_level is low
then write('** open the control valve **')
score 10 .

The extra space before the dot makes all the difference!

The semicolon character

It is often difficult to decide when Flex expects a semicolon, and when it does not. The best way to be sure is to look at some of the examples provided. That way you will soon recognize when you need a semicolon. Some definitions, such as actions, make use of the word and to separate their subparts, for example:

action write_value(Variable)
do write($Variable) % write name of variable
and write(' is ')
and write(Variable) % write value of variable
and nl .

Here, a semicolon can be included after the name of the action and its parameters, but is not required. (You are likely to find write_value a useful action to include in your own programs. To do this, you will need to include its definition in your programs.) Other definitions, such as rulesets, use semicolons (in place of the word 'and') to separate their sub-parts. In these cases you must not put a semicolon after the name of the ruleset:

ruleset boiler
contains all rules ;
select rule using first come first served ;
update ruleset by removing each selected rule ;
initiate by doing restart .

The dollar character

Take another look at the definition of the action write_value above. Notice that the dollar character is used. This stops a process called dereferencing. Its effect is best shown by example. Suppose your Flex program contains the following action definition:

action setpressure
do pressure becomes 10
and write_value($pressure) .

If, after compiling the program, you were to type


in the Console window, the response would be:

pressure is 10.

With the dollar character inserted in the definition of write_value, the variable's name (i.e. pressure) is used. Without the dollar, the variable's value (i.e. 10) is used. You can check the value of a global variable, such as pressure, by typing a query like this in the Console window:

write_value(pressure) .

Many ways to skin a cat

In general, there will be alternative ways to design the overall structure of your program, all of which may achieve the same result. At the more detailed level, Flex allows some flexibility in the use of certain words. Thus, for example, words like 'the' and 'a' can be included to improve the readability of your programs. Similarly, 'are' can replace 'is' if it sounds better. This flexibility is sometimes called syntactic sugar, as it sweetens the occasionally bitter experience of programming! Notice also that '=' can replace 'is' in the condition part of a rule, and ':=' can replace 'becomes' in the conclusion part of a rule. The following rules are, therefore, equivalent.

rule version1
if systems is knowledgebased
then choice becomes flex
and write_value($choice) .

rule version2
if the systems are knowledgebased
then a choice becomes flex
and write_value($choice) .

rule version3
if systems = knowledgebased
then choice := flex
and write_value($choice) .

Forcing Flex to forget

If you close a compiled program file (probably after saving it), the relations that you have defined will not be removed from memory. This contrasts with the way Prolog files are handled - the contents of a compiled Prolog file will be removed from the system's working memory after closing, whereas the contents of the compiled Flex program will be remembered whether you like it or not. To clear the system's memory of Flex commands and definitions you should type:


in the Console window and press the RETURN key. Note that initialise is spelt with an 's' rather than a 'z', and that as ever it is important to include the full stop. This command causes all Flex definitions to be forgotten. Any definitions that you want to be remembered will have to be recompiled.

After you have finished working on one problem, and before working on the next, it is wise to issue the initialise command.

Escaping from a loop

It is all too easy in Flex to find your program stuck in an infinite loop. Typically, the same words will be continually written in your Console window, or it may appear that nothing is happening at all. To escape from an infinite loop, hit the BREAK key while holding down the CONTROL key.

Elaborating your code with comments

In order to improve the clarity of your programs, you may want to use comments, for example to group related program components, or explain how particular sections of your program work. Comments are ignored by Flex, so they can contain almost any text you like. You can use two sorts of comment in Flex:

/* this is a comment
split over several lines */

% this is also a comment

In the first case, everything between the /* and */ is treated as a comment. In the second case, everything after the % on that line is treated as a comment.


If errors in a program are detected during compilation, some clues as to the location of the error appear in a pop-up box. The expression that Flex could not understand is reproduced in the Console window, along with some suggestions for fixing the problem. Often, however, the suggestions are quite misleading, so treat them with caution!

Sometimes your program may contain valid Flex, but doesn't behave as you expect. Unfortunately, the supplied debugging tools generally work at the level of the underlying Prolog and are of little help when developing Flex programs. If a Flex program compiles, but doesn't work as expected, you can introduce 'spies' as explained in the Flex Reference Manual (see the Flex predicates entry for spyfact). You may also find it helpful to make your program explain what it is doing, for example:

rule r6
if write('inspecting rule r6') and nl
/* Announce that this rule is being examined */
and steam is escaping
then steam_outlet := blocked
and write('firing rule r6')
/* Announce that this rule is firing */
and nl . % print a 'new line' character

Back to top

Forward chaining with rules in Flex

In order to open the files, remember to select Flex/Flint source files (*.ksl), as the default is Prolog (*.pl).

A good place to start is with the boiler example from Chapter 2. In order for you to run the rules in Flex, you need some way of telling the system the current state of the boiler. That is why there are some questions defined in FORWARDCHAINBOILER1.KSL. To run the demonstration, you will need to compile the Flex program by choosing Compile from the Run menu. Then go to the Console window, where you should find the following prompt:

| ?-

Commands for immediate execution are typed after this prompt. Such commands are sometimes called queries, owing to way in which they are treated by the Prolog language that underlies Flex. After the prompt, type start. remembering to include the full stop or period. Flex will execute the start action defined in the program, which in turn causes it to forward-chain through the rules, asking you some questions about the state of the boiler as it goes.

For example, you will be asked 'What is the temperature?'. Try clicking 'Explain' for a reason, then select 'high'. Then set the release valve to 'closed', the transducer output to 'low' and the flow rate to 'low'. What advice do you get?

You should have found that the instruction to open the control valve was offered twice and the instruction to shut down the boiler tubes was offered once. The 'open the control valve!' instruction was repeated because both rules r2_1 and r2_2 have fired. There should also be messages resulting from the firing of other rules. Finally you will see the word 'yes' (sometimes you will see 'no' instead). This indicates whether or not Prolog's final effort at matching a pattern was successful; as a Flex user, you can usually disregard it.

After you have tried running the ruleset a few times, it will become tiresome to answer questions about the state of the boiler. In anticipation of this, I have prepared a console interface. Close the FORWARDCHAINBOILER1.KSL window and type initialise. in the Console window, so that the previous rules are forgotten. Then open FORWARDCHAINBOILER2.KSL, compile it, and type controlpanel. in the Console window. A rudimentary boiler control panel will appear on your screen after you press RETURN. You can now make whatever changes you wish to the boiler via the control panel (the activated setting is marked with a black radio button; to change settings, click on the button). These values will be picked up by the Flex rules when you type start. in the Console window. The four little windows that comprise the control panel can be moved around like any other windows, and can be activated using the Flex menu entitled Windows. You will probably find it easiest to resize the Console window so that it no longer overlaps the control panel, as I have shown below.


Back to top

Conflict resolution

Try running the boiler control system with these starting conditions: As described in Section 2.5 of the book, you will see that rule r2_7 fires, regardless of the fact that you set the transducer voltage to 'low'. This is not what the rule-writer intended, since, according to rule r2_8, the water level is low. The problem has arisen because, given the order in which the rules are placed, rule r2_7 is examined before rule r2_8 under the "first come first served" conflict resolution. Under the closed-world assumption, the condition water_level is not low is considered to be true until rule r2_8 has fired. To make sure that rule r2_8 is examined before rule r2_7 you could simply reverse their order in the ruleset. However, this is a most unreliable approach, as the rules may be re-arranged when new rules are added and old ones thrown away. It is also contrary to the spirit of rule-based programming. A better option is the use of priority values.

Flex offers two types of conflict resolution: first come, first served (which it regards as conflict resolution "switched off") and priority values (conflict resolution "switched on"). The first come, first served mechanism is summarized in Figure 2.7 of the book. Conflict resolution in general is shown in Figure 2.2 of the book, which illustrates forward chaining. Where priority values are being used, the box marked "select a rule from the conflict set..." can be imagined instead as a box marked "select a rule with the highest priority value...". In Flex, the priority values for conflict resolution are called scores.

Now try altering rules r2_7 and r2_8 in FORWARDCHAINBOILER2.KSL so that they have scores of 1 and 10 respectively. A few hints:

Replace the words "first come first served" in the definition of the ruleset boiler with the words "conflict resolution". When you have initialised the system, and recompiled FORWARDCHAINBOILER2.KSL, you should run the boiler using the same starting conditions. You should find that the problem you discovered earlier (relating to the closed-world assumption) has been solved. Thus rule r2_7 only fires when the temperature is high and rule r2_8 has failed to fire.

Flex allows the scores to be allocated dynamically. This means that they don't have to be stated explicitly in advance, but can be calculated as an 'action' when a rule is fired. Take a look at WATER.KSL among the classic example programs to see some examples of dynamic scoring.

Back to top

Backward chaining with relations in Flex

After reading Chapter 2 of the book you should have a clear understanding of the difference between forward and backward chaining of rules. It is pointed out in Section 2.9.4 of the book that many rule-based systems, including Flex, have a different syntax for forward- and backward-chaining rules, with the conclusion often preceding the condition for backward-chaining rules. A forward-chaining production rule in Flex looks like this:

rule r1
if release_valve is stuck
then steam_outlet becomes blocked.

Whenever you write a rule like this, Flex assumes that you want to apply forward chaining. Further preferences for how the forward chaining should be applied can be given, as in the definition of the ruleset boiler that appears in FORWARDCHAINBOILER2.KSL. Backward-chaining rules in flex are called relations rather than rules. Rules are simply conditional relationships, so the Flex terminology can be justified. Re-writing rule r1 as a backward-chaining relation, I get:

relation blocked(steam_outlet)
if stuck(release_valve).

The full stop at the end is an important part of both rule r1 and the corresponding relation. Notice that although the rule and the relation are doing a similar job, they are not directly interchangeable. When the condition part of rule r1 is satisfied and the rule is chosen for firing, a value (blocked) is assigned to a variable (steam_outlet). In contrast, when Flex backward-chains it only uses those relations that it needs in its attempt to satisfy a goal, which is usually entered as a query. No explicit assignments of values to global variables are made. For example, assume that the above relation was part of a compiled program, and the following query was entered at the Console:


When Flex encounters a variable that begins with an upper-case letter or an underscore, it recognizes it as a local variable and looks for values that can be matched with it. In this case, Flex would recognize that this query could be satisfied if the local variable X were matched to steam_outlet, and if the truth of another relation, stuck(release_valve), could be established. The latter relation becomes the new goal and, if it is satisfied, Flex will return:

X = steam_outlet

Notice, by the way, that you can also write relations that are unconditional:

relation blocked(steam_outlet).

Instead of a rule, this relation now represents a fact. In this example, the relation name is blocked, and steam_outlet is the value of its single argument. This relation could be replaced with an equivalent relation that uses the local variable Y:

relation blocked(Y)
if Y is steam_outlet.

The file MIXEDCHAINBOILER.KSL shows how forward-chaining rules and backward-chaining relations can be used together. The relations are used for getting values from the control panel, so the only rules are the familiar ones r2_1 - r2_10 (with conflict resolution used to ensure the correct ordering of rules r7 and r8 - see Conflict resolution). If you want to try it out: initialise; open and compile MIXEDCHAINBOILER.KSL; if necessary, recreate a control panel by typing controlpanel. in the Console; make your selections in the control panel, and type "start." .

In BACKCHAINBOILER.KSL, I have replaced rules r2_1 - r2_10 with backward-chaining relations. We no longer need any of the action or ruleset definitions. To test whether the relations are working, type a query at the console, e.g.:

?- needs_opening(X).

Flex will return all matches (if any) to X, which ought to be the names of all valves that need opening. The matches are displayed one at a time. To see more matches, press the space bar. (In this case there may only be one match.)

Back to top

Bayesian updating in Flint

Open the file BAYESBOILER.KSL, compile it, and then type the following query in the Console window:

bayes_boiler( 0, 1, 1, 0.1, 0.02, P ).

You should receive the following response:

P = 0.6918 ...

The figures in brackets in your query represent the starting conditions given in the example in the book. The first three are explicitly-stated starting conditions. The fourth and fifth are not explicitly given in the book, so the prior probabilities are used:

Compare the response that you have obtained with the worked example in the book. They should be the same, except that the book has rounded the answer to two decimal places. In order to help you to see what is going on, FLINT includes a 'trace' facility, that displays the evolving odds or probability values. You can switch the tracing on or off by typing the following commands in the Console window:


You can also switch the tracing of uncertainty on or off within your flex programs by including the following instructions:

trace propagation .
notrace propagation .

Turn on the trace facility, and repeat the query that you set previously. You will see that the trace is expressed in terms of probability values. To see the same calculations as odds values, alter the penultimate line of the program so that it reads:

and propagate bayes_boiler_rules odds rules

Recompile the program and set the query again. You should now see an equivalent trace, this time with the calculations expressed as odds. Both sets of outputs should match the worked example in the book.

Back to top

Certainty factors in Flint

Certainty factors are handled using FLINT in a similar way to Bayesian updating. Open and compile CFBOILER.KSL, and then type the following in the Console window:

cert_boiler( -1, 1, 1, _, _, C ).

You should receive the following response:

C = 0.56

You can turn the trace facility on or off as described above. Compare the response that you have obtained with the worked example in the book. They should be the same. The first three figures in brackets in your query represent the starting conditions given in Section 3.3.4 of the book:

Notice that there is no need to provide a 'prior' value for CF4 or CF5, as these are assumed to be 0 initially. The underscore characters ('_') mean "I don't care". Try altering the third line of the definition of cert_boiler so that it reads

and the certainty_factor that the water_level is not low = CF1

Recompile the file and type the following query at the Console:

?- cert_boiler( 1, 1, 1, _, _, C ).

You should receive the same response as before because 'not low' with certainty 1 is the same as 'low' with certainty 1.

Back to top

Fuzzy logic in Flint

As with Bayesian updating and certainty factors, facilities for building and tracing the behaviour of a fuzzy system are included in FLINT. With the file FUZZYBOILER.KSL in the foreground, select Fuzzy Editor from the Run menu. A fuzzy variable editor will pop up. This editor provides a graphical display of the fuzzy sets associated with the fuzzy variables defined in the program window. To view any of the fuzzy sets defined in FUZZYBOILER.KSL, select the name of the fuzzy variable from the Name menu.

As well as displaying fuzzy sets, you can use the editor to change a wide variety of the parameters of a fuzzy variable or even define new fuzzy variables. If you select OK when you have finished, the editor will automatically generate Flint code that defines the fuzzy variables and fuzzy sets you created or changed using the editor. In the current example, your changes will appear in the window containing FUZZYBOILER.KSL. Any changes made using the fuzzy variable editor will include a comment showing the time and date of the edit. For now, discard any changes that you have made by closing the FUZZY1.KSL window without saving your changes, then opening the file again.

Compile FUZZYBOILER.KSL and type the following in the Console window:

fuzzy_boiler(180, Pressure).

This will set Flint the goal of determining the pressure when the temperature is 180°C, which is the same example that is used in the book. You should receive the following response (as certainty tracing is explicitly switched on within the program, the response will be preceded by some trace information):

Pressure = 0.625

Units of MNm-2 (or MPa) are assumed. The book shows that this is the expected response when defuzzification uses Larsen's product operation rule and the mirror rule. FLINT incorporates the following defuzzification options, with which you may wish to experiment:

In FUZZYBOILER.KSL, try changing one of the defuzzification options for pressure from mirror rule to bounded range. You can do this by editing the file directly or by using the fuzzy editor and clicking OK. Recompile the file and set the same query as before:

fuzzy_boiler(180, Pressure).

This time you should receive the response shown in the book for the case where a bounded range was used (except that only three places of decimals are given in the book):

Pressure = 0.5272 ...

I suggest you close FUZZYBOILER.KSL without saving your changes.

Back to top

Return to Adrian Hopgood's home page.

Valid HTML 4.01! Owner:
Adrian Hopgood
April 2016
     Follow @adrianhopgood on Twitter