Back

TechnologyNov 08, 2016

Solving Logical Business Problems Using Prolog: Part Four

Micah Jones

In part three of this series, we presented a Prolog program to schedule employee assignments given a large collection of constraints. In this part, we will show how to run that program and then process its results from within a Java code environment.

We will continue to use SWI-Prolog, which comes with a useful set of libraries called JPL. JPL can be used as an interface between Java and Prolog, allowing Java programs to execute Prolog code and vice versa. We will be using the jpl.jar library file from the swipl-7.2.3 release, located in the installed lib folder.

There is some environment setup required, which differs between operating systems. On Windows you should be able to follow the instructions in the official documentation. I use Mac OS X, which unfortunately requires some extra effort. I installed Prolog using macports to get a working version that could be called by the JVM, and then added its location to my java.library.path variable. See this Stack Overflow article for more details.

Our Java project for calling the Prolog Scheduler program can be found on GitHub.

Our program interacts with Prolog by instantiating and executing Query objects, which generate Prolog predicate calls. To build the call to load our Scheduler program, we create the following Java object:

new Query("consult",           new Term[] {new Atom("shift_scheduler_clp.pl")})

This query will call the built-in consult predicate, which loads a source file into the Prolog engine. A predicate’s arguments are represented by an array of Term objects. In this case we need only one argument: an Atom referring to the source file shift_scheduler_clp.pl. In effect, this Query is equivalent to the Prolog call consult(shift_scheduler_clp.pl). We execute it by calling the Query’s member method hasSolutions(), which returns true if the query is successful or false if it fails.

Our Scheduler program uses a database of Prolog facts to generate its constraints and find a valid result schedule. To create those facts, we use the built-in Prolog predicate assert. For example, to create an entry for an employee named micah, our Java Employee class runs the following code:

new Query("assert",           new Term[] {new Compound("employee", new Term[] {               new Atom("micah")})}).hasSolution();

This is equivalent to executing the Prolog call assert(employee(micah)), which generates the fact employee(micah). Here we have used a Compound object, which takes as input the functor name employee and its list of terms, in this case the lone atom micah.

After asserting our database of facts, we build a call to our main predicate schedule(Schedule):

Query q1 = new Query("schedule", new Term[] {new Variable("Schedule")});

Here we use a Variable object to declare a name for the variable we will read when viewing solutions. Remember that Prolog can non-deterministically return several solutions; JPL allows us to read each one in succession by using the Query object’s nextSolution() method.

Our primary loop will read up to NUM_SOLUTIONS results into Java ScheduleEntry objects and display the results:

for (int i = 0; (i < NUM_SOLUTIONS && q1.hasMoreSolutions()); i++) {      System.out.println("Solution #" + (i+1) + ":");      List<ScheduleEntry> entries = new ArrayList<ScheduleEntry>();      Map<String,Term> resultMap = q1.nextSolution();      Term[] results = resultMap.get("Schedule").toTermArray();      for (Term term : results) {          Term[] termArgs = term.args();          String employee = termArgs[0].args()[0].toString();          String taskName = termArgs[1].args()[0].toString();          String shiftDay = termArgs[1].args()[1].args()[0].toString();          String shiftPeriod = termArgs[1].args()[1].args()[1].toString();          entries.add(new ScheduleEntry(              employee, taskName, shiftDay, shiftPeriod));      }      for (ScheduleEntry entry : entries) {          System.out.println(entry.toString());      }      System.out.println(); }

The main inner for loop processes the next returned Schedule object by looping over its terms. Each term is a single employee assignment and is of the following form in Prolog:

assign(employee(EmployeeName),task(TaskName,shift(ShiftDay,ShiftPeriod)))

We extract the variables inside that term to populate the Java String variables employee, taskName, shiftDay, and shiftPeriod. Those variables are then used to instantiate a new ScheduleEntry object, which then can be referenced in Java as needed. In this case we simply output all of our ScheduleEntry objects after collecting them from the Schedule result.

Conclusion

This is a simple program that is only intended as a proof of concept. However, it would be easy to expand it to, for example, a SOAP endpoint in a Spring Framework web server. By doing so, we could use our employee scheduler as part of a larger system coded in Java.

Thus concludes our series on Prolog, a powerful logic programming language that uses deduction to infer solutions to complex, challenging problems. We have shown how to reduce an employee scheduling system to a satisfiability problem, which is easy to solve with SWI-Prolog’s CLP libraries. In addition, we have used the JPL libraries to build an interface between Prolog and Java, allowing us to utilize the strengths of both in real-world systems.

Micah Jones is a consultant in Credera’s Integration & Data Services (IDS) practice. In 2011, he received his doctorate in computer science from the University of Texas at Dallas, specializing in programming language-based security.