Minimax Search [Knowledge]

Let’s say your program is in a competitive situation against others, and must predict possible consequences of actions to select the one with the best outcome. This is the case in many logic games like Tic-Tac-Toe, Othello, Chess or Go. The minimax algorithm can be applied here as long as:

  • The current state is (at least partly) available to the program. For example, the program knows the position of the pieces on the game board.
  • It’s possible to search through successor states in the future. The program knows how the pieces end up when a move is made.
  • There’s a way to evaluate the utility of certain states. For instance, knowing when about the winning/loosing conditions.

Since this is a competitive setting, you can assume that the others pick actions resulting in a worse outcome for your program. Then, minimax works by taking the predictions of future states, and working back to determine the utility of predecessor states. When your program has estimated the utility of the next states, it can easily select the best.

Description

Minimax is a policy to select optimal utility actions assuming the worst from other programs. Typically, minmax is combined with a depth-first search algorithm that traverses the game tree, predicting what the opponents are likely to do and what your program should do. As such, there are two different levels in the search tree: 1) for the opponents and 2) for team-members.

  1. The level in the game tree dealing with opponents is known as MIN, since they will typically minimize the utility of the outcome state.
  2. Conversely, the search tree’s level dealing with team-members is known as MAX, since they will typically maximize the utility of the outcome state.

The minimax search, in effect, minimizes the maximum possible loss, or looking at it the other way, maximizes the minimum gain.

Min/Max Search in Game Tree

Application

Minimax is applied to many games with great success, especially when the branching factor is small (i.e. there are few options each turn), when the situation is deterministic, and the state is fully observable.

However, in practice, there are significant challenges to applying Minimax to a logic game. Notably, the game tree is usually too large to search until the end, so good estimator functions are required for any state (and not just terminal states). This is not a trivial problem, and significantly affects the outcome of the algorithm. Most state estimation functions are hand written for each problem by experts.

The pseudo-code for a minimax search looks like this:

def minimax(node, depth)    if node.is_terminal() or depth == 0:         return node.calculate_heuristic()    if node.is_opponent():         a = +inf         for child in node.get_children():             a = min(a, minimax(child, depth-1))    else:        a = -inf        for child in node.get_children():             a = max(a, minimax(child, depth-1))    return a

On the bright side, alpha-beta pruning can be implemented easily to improve the efficiency of the search.

Theory

In classical statistical decision theory, an estimator \delta is used to estimate a parameter \theta \in \Theta. Also assume a risk function R(\theta,\delta), usually specified as the integral of a loss function. Given this framework, \tilde{\delta} is called “minimax” if it satisfies:

\sup_\theta R(\theta,\tilde{\delta}) = \inf_\delta \sup_\theta R(\theta,\delta)

Resources

New version of Numenta software is available

I prefer open source software - the license applied to an open source software project lets me know up front what my usage rights and obligations are.

The Numenta Platform for Intelligent Computing (NuPIC) is free for academic and non-commercial research use, but there is so far no definitive information on commercial licensing costs. As a result of this, even though I like the ideas behind NuPIC, I spend relatively little time playing with the examples. I very much enjoyed Jeff Hawkin's book On Intelligence (Amazon purchase link) and I will probably devote a lot more time to experimenting with NuPIC when all of the licensing issues are nailed down.

Verison 2.0 of OpenCyc is available

I installed OpenCyc 2.0 on one of my test servers last night. It is now Java based, so is (in principle) portable: I'll try modifying the start up scripts for OS X when I have time. There is also now a Semantic Web sub project for OpenCyc.

There are now references to other lined data sources - you will notice this as soon as you start experimenting with the browser based interface.

If you have not tried OpenCyc before, it is a free version of the Cyc product. OpenCyc provides an ontology of the "real world" and many facts and relations dealing with what might be called "common sense" knowledge.

What’s Your Biggest Question about Artificial Intelligence? [Article]

Is there anything you want to know about artificial intelligence? If so, fill in our AI survey online; it’s only one question. You can be as brief or expressive as you like.

As artificial intelligence enthusiasts and developers, we’re interested in hearing what you have to say. There are lots of ideas and content in the pipeline for the AI Depot, but we’re particularly interested in what you want from us.

Those of you who fill in the questionnaire will get exclusive access to the new content before anyone else! You also get the satisfaction of knowing you helped out in shaping the future direction of this site.

We’re looking forward to hearing from you, but be sure to subscribe to our new artificial intelligence feed to hear from us daily!

NLTK: The Natural Language Toolkit

I have a 22 year history of working with natural language processing, but for the most part this was a low level of effort (perhaps averaging 3 to 5 weeks a year). For learning (and perhaps for some production work if you extract the parts that you need) I can very much recommend NLTK.

NLTK developers (or aggregators since NLTK is an aggregate of smaller projects, with a lot of new work added) Steven Bird, Ewan Klein, and Edward Loper are writing a complete book on NLP using NLTK that looks good. I wish that I had a good resource like this 22 years ago!

My OpenCalais Ruby client library

Reuters has a great attitude about openly sharing data and technology. About 8 years ago, I obtained a free license for their 1.2 gigabytes of semantically tagged news corpus text - very useful for automated training of my KBtextmaster system as well as other work.

Reuters has done it again, releasing free access to OpenCalias semantic text processing web services. If you sign up for a free access key (good for 20,000 uses a day of their web services), then you can use my Ruby client library:

# Copyright Mark Watson 2008. All rights reserved.
# Can be used under either the Apache 2 or the LGPL licenses.

require 'simple_http'

require "rexml/document"
include REXML

require 'pp'

MY_KEY = ENV["OPEN_CALAIS_KEY"]
raise(StandardError,"Set Open Calais login key in ENV: 'OPEN_CALAIS_KEY'") if !MY_KEY

PARAMS = "&paramsXML=" + CGI.escape('<c:params xmlns:c="http://s.opencalais.com/1/pred/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><c:processingDirectives c:contentType="text/txt" c:outputFormat="xml/rdf"></c:processingDirectives><c:userDirectives c:allowDistribution="true" c:allowSearch="true" c:externalID="17cabs901" c:submitter="ABC"></c:userDirectives><c:externalMetadata></c:externalMetadata></c:params>')

class OpenCalaisTaggedText
def initialize text=""
data = "licenseID=#{MY_KEY}&content=" + CGI.escape(text)
http = SimpleHttp.new "http://api.opencalais.com/enlighten/calais.asmx/Enlighten"
@response = CGI.unescapeHTML(http.post(data+PARAMS))
end
def get_tags
h = {}
index1 = @response.index('terms of service.-->')
index1 = @response.index('<!--', index1)
index2 = @response.index('-->', index1)
txt = @response[index1+4..index2-1]
lines = txt.split("\n")
lines.each {|line|
index = line.index(":")
h[line[0...index]] = line[index+1..-1].split(',').collect {|x| x.strip} if index
}
h
end
def get_semantic_XML
@response
end
def pp_semantic_XML
Document.new(@response).write($stdout, 0)
end
end

Notice that this code expects an environment variable to be set with your OpenCalais access key - you can just hardwire your key in this code if you want. Here is some sample use:

tt = OpenCalaisTaggedText.new("President George Bush and Tony Blair spoke to Congress")

pp "tags:", tt.get_tags
pp "Semantic XML:", tt.get_semantic_XML
puts "Semantic XML pretty printed:"
tt.pp_semantic_XML

The tags print as:

"tags:"
{"Organization"=>["Congress"],
"Person"=>["George Bush", "Tony Blair"],
"Relations"=>["PersonPolitical"]}

OpenCalais looks like a great service. I am planning on using their service for a technology demo, merging in some of my own semantic text processing tools. I might also use their service for training other machine learning based systems. Reuters will also offer a commercial version with guaranteed service, etc.

Ruby API for accessing Freebase/Metaweb structured data

I had a good talk with some of the Metaweb developers last year and started playing with their Python APIs for accessing structured data. I wanted to be able to use this structured data source in a planned Ruby project and was very pleased to see Christopher Eppstein's new project that provides an ActiveRecord style API on top of Freebase. Here is the web page for Christopher's Freebase API project. Assuming that you do a "gem install freebase", using this API is easy; some examples:

require 'rubygems'
require "freebase"
require 'pp'

an_asteroid = Freebase::Types::Astronomy::Asteroid.find(:first)
#pp "an_asteroid:", an_asteroid
puts "name of asteroid=#{an_asteroid.name}"
puts "spectral type=#{an_asteroid.spectral_type[0].name}"

#all_asteroids = Freebase::Types::Astronomy::Asteroid.find(:all)
#pp "all_asteroids:", all_asteroids

a_company = Freebase::Types::Business::Company.find(:first)
#pp "a_company:", a_company
puts "name=#{a_company.name}"
puts "parent company name=#{a_company.parent_company[0].name}"

You will want to use this API interactively: use the Freebase web site to find type hierarchies that you are interested in, fetch the first object matching a type hierarchy (e.g., Types -> Astronomy -> Asteroid) and pretty print the fetched object to see what data fields are available.

Protégé OWL Ontology Editor

I installed Protégé version 4 alpha last night and it has been solid for me so far. It has been over a year since I upgraded my local Protégé installation, and I like these (new ?) features a lot:

  • Saved XML for OWL ontologies is very readable, with good automatically generated comments and a nice layout
  • Use of the Java OWL API
  • Both Fact++ (using JNI) and Pellet 1.5 are smoothly integrated
  • The Owlviz Plug-in seems to display graphs faster
  • Drag and drop can be used rearrange class hierarchies

I have started working again on an old KnowledgeBooks project: an OWL ontology for news stories and associated "Semantic Scrappers" to populate the ontology from plain texts of news stories. I am still in the experimentation stage for the semantic scrapper: I am trying to decide between pure Ruby code, Java using the available OWL APIs, or a combination of JRuby and the Java OWL APIs. I am currently playing with these three options - for now, I am in no hurry to choose a single option. Using a dynamic language like Ruby has a lot of advantages as far as generating "OWL classes" automatically from an ontology, etc. That said, the Java language has the best semantic web tools and libraries.

Long term, I would like a semi-automatic tool for populating ontologies via custom scrapper libraries. I say "semi-automatic" because it would be useful to integrate with Protégé for manual editing and browsing, while supporting external applications accessing data read-only (?) via the Java OWL APIs.

N-GRAM analysis using Ruby

I dusted off some old code today to look at common word pairs in some customer data. NGRAM analysis finds the most common bi-grams (2 word combinations), tri-grams (3 word combinations), etc. The code is simple, and I share it here in case you ever need to do the same thing:

require 'zip/zipfilesystem'

def words text
text.downcase.scan(/[a-z]+/)
end

Zip::ZipFile.open('../text.txt.zip') { |zipFile| # training data
$words = words(zipFile.read('text.txt')) # is in a ZIP file
}

bi_grams = Hash.new(0)
tri_grams = Hash.new(0)

num = $words.length - 2
num.times {|i|
bi = $words[i] + ' ' + $words[i+1]
tri = bi + ' ' + $words[i+2]
bi_grams[bi] += 1
tri_grams[tri] += 1
}

puts "bi-grams:"
bb = bi_grams.sort{|a,b| b[1] a[1]}
(num / 10).times {|i| puts "#{bb[i][0]} : #{bb[i][1]}"}
puts "tri-grams:"
tt = tri_grams.sort{|a,b| b[1] a[1]}
(num / 10).times {|i| puts "#{tt[i][0]} : #{tt[i][1]}"}

Output might look like this:

bi-grams:
in the : 561
in Java : 213
...
tri-grams:
in the code : 119
Java source code : 78
...

Cool stuff. Ruby is my favorite language for tool building.

Good video: Knowledge Representation and the Semantic Web

Peter Patel-Schneider gave this talk at Google January, 2006.

Peter Patel-Schneider has an interesting perspective: while he has been very involved with Semantic Web technologies like OWL and descriptive logic reasoners (author of the Classic system), he also appears to have a reasonable amount of skepticism. His talk is good because he starts off clearly explaining RDF, RDFS, and the improved formalism and expressiveness of OWL. I especially enjoyed his summarization of different types of logic, what their computational limitations and capabilities are.

I have mixed feeling about trying to implement the Semantic Web using a formal semantically rich language like OWL: I am concerned that encoding information in OWL simply takes too much effort. At the other end of the complexity spectrum, tagging is a very light weight expression of the Semantic Web but tagging is inadequate (but easy enough that many people make the effort).

I have a short list of Semantic Web resources that I find useful.

Using the PowerLoom reasoning system with JRuby

I am writing a free web book on AI programming with Ruby (a work in progress).

I wanted to use the PowerLoom reasoning system for the book chapter on reasoning. I had two choices: using the C++ PowerLoom libraries with Ruby bindings or writing a Java wrapper class and then using JRuby. I decided to use JRuby. After some more work on converting to native Ruby types for query results, the material in this blog will eventually make its way into my Ruby AI book (along with a larger example). Here is a simple PowerLoom source file "test.plm" that I will use (part of an example by Robert M. NacGregor in the PowerLoom distribution):

(defmodule "BUSINESS"
:documentation "Module for the Business demo example used in the PowerLoom Manual."
:includes ("PL-USER"))

(in-module "BUSINESS")

;; clear any info from previous runs:
(clear-module "BUSINESS")
(reset-features)

;;; Concepts

(defconcept company)
(defconcept corporation (?c company))

;;; Relation

(defrelation company-name ((?c company) (?name STRING)))

and here is a JRuby example program that uses the wrapper (seen later):

require "java"
require "powerloom.jar"
require "pl.jar"
require 'pp'

include_class "PowerLoomWrapper"

pl = PowerLoomWrapper.new

pl.load("test.plm")
pl.changeModule("BUSINESS")
pl.assertProposition("(and (company c3) (company-name c3 \"Mom's Grocery\"))")

answers = pl.doQuery("all ?x (company ?x)")
answers.each {|answer| puts answer}

answers = pl.doQuery("all (?x ?name) (and (company ?x) (company-name ?x ?name))")
answers.each {|answer| puts answer}

Assuming that you get the powerloom.jar and stella.jar files from the PowerLoom web site (grab the latest download), this Java wrapper should work for you:

import edu.isi.powerloom.*;
import edu.isi.powerloom.logic.*;
import edu.isi.stella.Cons;
import java.io.File;

public class PowerLoomWrapper {

public PowerLoomWrapper() {
System.out.print("Initializing...");
PLI.initialize();
System.out.println(" done.");
}
public int load(String fpath) {
try {
PLI.load(fpath, null);
return 1;
} catch (Exception ex) {
System.out.println("Error loading " + fpath + " is: " + ex);
return 0;
}
}
public int changeModule(String workingModule) {
try {
PLI.sChangeModule(workingModule, null);
this.workingModule = workingModule;
return 1;
} catch (Exception ex) {
System.out.println("Error changing module " + workingModule + " is: " + ex);
return 0;
}
}
public int assertProposition(String proposition) {
try {
PLI.sAssertProposition(proposition, workingModule, null);
return 1;
} catch (Exception ex) {
System.out.println("Error asserting proposition '" + proposition + "' is: " + ex);
return 0;
}
}
public int createRelation(String relation, int arity) {
try {
PLI.sCreateRelation(relation, arity, workingModule, null);
return 1;
} catch (Exception ex) {
System.out.println("Error creating relation '" + relation + "' with arity=" + arity + " is: " + ex);
return 0;
}
}

// following method donated by Thomas Russ:
public java.util.ArrayList doQuery(String query) {
java.util.ArrayList ret = null;
try {
PlIterator rawAnswer = PLI.sRetrieve(query, workingModule, null);
ret = new java.util.ArrayList(rawAnswer.length());
java.util.Iterator answer =
new edu.isi.stella.javalib.StellaIterator(rawAnswer);
while (answer.hasNext()) {
Object obj = answer.next();
int nItems = PLI.getColumnCount((edu.isi.stella.Stella_Object)obj);
java.util.ArrayList r2 = new java.util.ArrayList(nItems);
for (int i = 0; i r2.add(fix(PLI.getNthValue((edu.isi.stella.Stella_Object)obj, i, null, null)));
}
ret.add(r2);
}
} catch (Exception ex) {
System.out.println("Error with query '" + query + "': " + ex);
ret = new java.util.ArrayList(0);
}
return ret;
}


private String workingModule = "PL-USER";
}

Here is the Makefile that I use for building a second jar file pl.jar" and running the sample program:

all:
javac -classpath .:powerloom.jar:stella.jar PowerLoomWrapper.java
jar cvf pl.jar PowerLoomWrapper.class PowerLoomWrapper.java
/Users/markw/bin/jruby-1.0/bin/jruby jruby_powerloom.rb

RapidMiner machine learning, data mining, and visualization tool

When analyzing data for customers I usually start with a tool like Weka or occasionally write custom analysis code. The GUI tool RapidMiner (used to be called YALE) provides a nice environment for setting up experiments by selecting operations and models to apply to your data sets. RapidMiner uses Weka, libsvm, JAMA, and several graphics visualization libraries. RapidMiner has data loaders for relational databases, spreadsheets, comma delimited files, etc.

It is well worth watching the demo video for a quick introduction before working through the tutorial.

texai.org

Stephen Reed is doing some interesting research at texai.org. This project is interesting to me because there is a lot of overlap with my own KBSportal.com project (which I am currently re-writing in Ruby and Ruby on Rails: the first two versions were in Common Lisp and Java): RDF, Sesame, knowledge representation, and NLP.

For me, working on KBSportal.com has been a learning process, and I think that texai also serves that purpose for Stephen.

Something like Google page rank for semantic web URIs

A key idea of the semantic web is reusing other people's URIs - this reuse forms interconnections between different RDF data stores. The problem, as I see it, is that while URIs are unique, we will end up with a situation where (as an example) there might be thousands of URIs for NewsItem. The Swoogle semantic web search engine is useful for finding already existing URIs - a good first step toward reuse.

Swoogle will return results using their type of 'page ranking'.

So, we need two things to accelerate the adoption and utility of the semantic web: web sites must start to include RDF and this included RDF must reuse common URIs for concepts and instances.

My experiences writing AI software for vehicle control in games and virtual reality systems

I have in the past offered advice in the comp.ai.games news group re: AI control of vehicles. Here are a few short notes based on some experience that I have had. I have had the good fortune of getting to spend a fair amount of time (both on my own, and while being paid) writing code to control vehicles in games, etc.:

* Writing example code for my book AI Agents in Virtual Reality Worlds
* Working on the Indy 500 car simulation system at SAIC. I actually worked on the motion platform control, network programming, and sound system software. For this project, you always raced against other people, also in simulators.
* At Angel Studios working on 2 Nintendo games and a PC-based hover craft racing game.

Anyway, I do have some advice on how to write AI driving opponents:

The first thing that you want to do is to define data structures for navigational "way points" that AI vehicles follow and simply have the vehicles move between these "way points". If possible, AI controlled vehicles should use the same physics simulation code as the player's vehicle (at least when the AI vehicle is close and in the player's field of view).

After your AI controlled vehicles can move along using "way points", then, based on the game theme, add logic for detecting the proximity of the player's vehicle (e.g., AI vehicle might swerve into the player's vehicle under specified circumstances), etc. Do not bother with complex behavior if a vehicle is not in the rendered scene for a player.

It should be fairly easy to get the code running to follow way points, but that is definitely your first step. Combining driving logic control with "lerping" between weigh points will also make the vehicles look a lot more realistic while more or less keeping them where you want them.

When possible, your movement logic should depend heavily on where the AI vehicle is in the player's field of view. For efficiency, it might be expedient to use simple finite state machines for the control logic. However, I have experimented (again, see my book) with using expert systems, neural networks, and genetic algorithms. For Java programmers, my book "Intelligent Java Applications" contains a (not practical, but fun!) example of using genetic algorithms to train simple control programs in an "asteroids" style arcade game.

Good luck, and have fun!

I have a new page on Knowledge Management

I have a new page on Knowledge Management where I share some philosophy on why I believe that open source plays such an important role in building effective KM systems. I believe that as much as possible it is best to use available open source infrastructure software and use available resources for "value add" user modeling and AI components. I list on the right side of this web page some of the better free resources for doing KM work.