The London Underground Case Study

Contents

    Before Starting

    This section illustrates a case study that is referenced in the JPC user manual. It requires a basic understanding of Logtalk. In order to download and install this example please refer to the JPC installation guide.

    Introduction

    Our case study addresses a typical problem that can be implemented easily with a logic language: a querying system about subway lines and stations. It has been adapted from the Simply Logical book by Peter Flach. This document describes both a concrete implementation in the Prolog side (using Logtalk) and a set of Java interfaces that model the domain artifacts in the Java side.

    The main entities of our example are shown in figure 1: These are: subway stations, subway lines connecting those stations and the subway itself (referred as metro in the figure). Relevant relations between these entities are:

    The London Underground.
    Fig 1. - The London Underground (adapted from the Simply Logical book).

    The Logtalk Entities

    The first stage of the problem consists in expressing our knowledge about the London Underground as a set of logic statements. Instead of implementing it in plain Prolog as in the original example, we introduce an interesting variation: we use Logtalk, a portable object-oriented layer on top of Prolog.

    In the rest of this section we overview how the artifacts in our example are modelled as Logtalk objects. Figure 2 shows an overview of such objects and their dependencies.

    The Logtalk entity diagram.
    Fig 2. - The Logtalk entities diagram for the London underground example.

    The metro Logtalk Object

    The metro/0 object encapsulates the knowledge about how stations are connected (e.g., the connected/3 predicate). Note that the connected/3 predicate is declared as dynamic (line 7) since new connections can be configured at runtime. Plus the rules for the logic predicates nearby/2, reachable/3, line/1 and remove_all/0. The messages (queries) that the metro object can respond to are specified by the public/1 directive (lines 3-5). Messages in Logtalk are sent using the ::/2 operator, as illustrated on line 23 for the member/2 method.

    :- object(metro).
    
    	:- public([
    		connected/3, nearby/2, reachable/3, line/1, remove_all/0
    	]).
    
        :- dynamic(connected/3).
    
    	nearby(Station1, Station2) :-
    		connected(Station1, Station2, _).
    	nearby(Station1, Station2) :-
    		connected(Station1, IntermediateStation, Line),
    		connected(IntermediateStation, Station2, Line).
    
    	reachable(Station1, Station2, []) :-
    		connected(Station1, Station2, _).
    	reachable(Station1, Station2, [IntermediateStation| IntermediateStations]) :-
    		connected(Station1, IntermediateStation, _),
    		reachable(IntermediateStation, Station2, IntermediateStations).
    
    	line(Name) :-
    		setof(Line, Station1^Station2^connected(Station1,Station2,Line), AllLines),
    		list::member(line(Name), AllLines).
    
        remove_all :-
    		metro::retractall(connected(_,_,_)).
        
    :- end_object.

    The line Logtalk Object

    A subway line is modelled by the Logtalk parametric object line/1. The object parameter denotes the name of the line. It provides predicates answering connected stations by means of the line object (e.g., connects/2). In addition, it allows a programmer to declare new connections between stations by means of the line object (e.g., add_connection/2). These predicates delegate to the metro Logtalk object.

    :- object(line(_Name)).
    
    	:- public([
    		name/1, connects/2, add_connection/2
    	]).
    
    	name(Name) :-
    		parameter(1, Name).
    
    	connects(Station1, Station2) :-
    		self(Self),
    		metro::connected(Station1, Station2, Self).
    
    	add_connection(Station1, Station2) :-
    		self(Self),
    		metro::assertz(connected(Station1, Station2, Self)).
    
    :- end_object.

    The station Logtalk Object

    A subway station is modelled by the Logtalk parametric object station/1. The object parameter denotes the name of the station. It provides predicates answering stations connected to this line (e.g., connected/1 and connected/2), stations that are nearby this station (e.g., nearby/1) and stations that are reachable from this station (e.g., reachable/1 and reachable/2). These predicates delegate to the metro Logtalk object.

    :- object(station(_Name)).
    
    	:- public([
    		name/1, connected/1, connected/2, nearby/1, reachable/1, reachable/2
    	]).
    
    	name(Name) :-
    		parameter(1, Name).
    
    	connected(Station) :-
    		connected(Station, _).
    
    	connected(Station, L) :-
    		self(Self),
    		metro::connected(Self, Station, L).
    
    	nearby(Station) :-
    		self(Self),
    		metro::nearby(Self, Station).
    
    	reachable(Station) :-
    		reachable(Station, _).
    
    	reachable(Station, IntermediateStations) :-
    		self(Self),
    		metro::reachable(Self, Station, IntermediateStations).
    	
    :- end_object.

    The Logtalk Loader File

    Typically Logtalk libraries are loaded by means of a loader file. This file contains one or more logtalk_load/1 predicates loading the distinct required entities. Assuming these entities are defined in files named metro.lgt, station.lgt and line.lgt, the following script will load our library:

    :- initialization((
    	logtalk_load(library(types_loader)),
    	logtalk_load([metro, station, line])
    )).

    In addition to load the main entities of the library, the loader also loads depending entities (e.g., the types library is loaded in line 2).

    Configuring the Underground Structure

    The previous Logtalk entities implements the logic of our application. However, they do not define concrete stations, lines and their relations. For this example, we make use of a separate file that contains directives loading some test data. This file should be loaded at runtime by means of the logtalk_load/1 predicate previously discussed. The content of this data-loading file is shown below:

    :- initialization((
    	line(central)::add_connection(station(bond_street),station(oxford_circus)),
    	line(central)::add_connection(station(oxford_circus),station(tottenham_court_road)),
    	line(jubilee)::add_connection(station(bond_street),station(green_park)),
    	line(jubilee)::add_connection(station(green_park),station(charing_cross)),
    	line(piccadilly)::add_connection(station(green_park),station(piccadilly_circus)),
    	line(piccadilly)::add_connection(station(piccadilly_circus),station(leicester_square)),
    	line(victoria)::add_connection(station(green_park),station(oxford_circus)),
    	line(bakerloo)::add_connection(station(oxford_circus),station(piccadilly_circus)),
    	line(bakerloo)::add_connection(station(piccadilly_circus),station(charing_cross)),
    	line(northern)::add_connection(station(tottenham_court_road),station(leicester_square)),
    	line(northern)::add_connection(station(leicester_square),station(charing_cross))
    )).

    The Java Entities

    In the Java side, we define the Metro, Line and Station interfaces in correspondence with the Logtalk entities described before, as illustrated by figure 3 (not all methods are shown).

    Java-Prolog interaction.
    Fig 3. - Java Prolog interaction.