A basic API for artificial intelligent agents described in the book "Artificial Intelligence: A Modern Approach", by Russell and Norvig.
We want to solve problems, but to do so, we must first represent them in a
reasonable fashion. In artificial, there are three basic classes used to
describe a problem (and its solution):
- World Encapsulates out-of-computer problem domains and translate them to a digital representation.
- State A model that holds data for both world and agents. It can be used to represent a state of the World or to predict states by agents.
- Agent Abstraction that presents an intelligent behavior.
artificial is written on Python, and requires numpy and scipy.
You can install everything with:
git clone https://github.com/lucasdavid/artificial
cd artificial
python setup.py install --userTo run the examples, matplotlib and scikit-learn are required:
pip install -r docs/requirements-examples.txt --upgrade --user
cd examples
python dirt_cleaner.py
To run tests, additional packages are required:
pip install -r docs/requirements-dev.txt --upgrade --user
# Run tests!
nosetestsThe coverage info will be placed inside coverage folder.
Let's say we want to create an agent that generate strings randomly until it
finds the word "hello world". We start by writing a GeneticState and
Environment that define our problem.
class WordIndividual(base.GeneticState):
def cross(self, other):
"""Simple crossover between two individuals"""
cross_point = random.randint(0, 11)
return WordIndividual(self.data[:cross_point] + other.data[cross_point:])
def mutate(self, factor, probability):
"""Mutation of an individual's genes"""
m = np.random.rand(len(self.data)) < factor * probability # Defines which genes will mutate.
if np.any(m):
data = np.array(list(self.data))
data[m] = [random.choice(string.ascii_lowercase + ' ') for mutated in m if mutated] # Mutate!
self.data = ''.join(data)
return self
@property
def is_goal(self):
return self.data == 'hello world'
@classmethod
def random(cls):
return cls(''.join(random.choice(string.ascii_lowercase + ' ') for _ in range(11))
class World(base.Environment):
state_class_ = WordIndividual
def update(self):
# My world consists on printing agents' solutions. Exciting.
for agent in self.agents:
print('Solution found: %s' % agent.act())Now we define an UtilityBasedAgent that considers samples similar to the
sentence "hello world" as "good":
class Speller(agents.UtilityBasedAgent):
def utility(self, state):
"""Measures how "good" is a word (sum of the matching letters)"""
expected = 'hello world'
return sum(self.data[i] == expected[i] and 1 or 0 for i in range(11)))
def act(self):
# We aren't really interest in acting over the world, but more in
# finding solutions. Hence simply returns the solution candidate,
# which is the fittest individual for the `GeneticAlgorithm` case.
return self.search().solution_candidate_The only thing left is to connect the dots! :-)
world = World(initial_state=WordCandidate.random())
env.agents += [
Speller(environment=world, search=GeneticAlgorithm,
search_params=dict(mutation_factor=.25, mutation_probability=1))]
print('Initial: {%s}' % str(env.current_state))
world.update()Note: take a look at the examples folder to see more concrete usages of searches, optimizations, evolutions and other intelligent stuff.