Fix up bad git subtree import.

This commit is contained in:
2018-06-11 09:39:27 -07:00
parent e7c4c5ba49
commit 6ad979d28f
86 changed files with 1 additions and 0 deletions

View File

@@ -0,0 +1,299 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"import actions, kb, sample"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## AQE: A Query Engine\n",
"\n",
"This is an implementation of a knowledge base, hacked together in Python\n",
"3 (it won't work in Python 2 for reasons of modules) for now to quickly\n",
"iterate on ideas.\n",
"\n",
"The `KnowledgeBase` is a repository of facts."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"skb = sample.load()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A fact is a tuple: (relationship, subject, object). `object` is admittedly a terrible name (and is subject to change) but it's what I came up with and what I'm working with for now."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[('is', 'cbr600', 'Driver'),\n",
" ('at', 'cbr600', 'oakland'),\n",
" ('at', 'airliner', 'denver'),\n",
" ('is', 'oakland', 'Airport'),\n",
" ('is', 'airliner', 'Flyer'),\n",
" ('is', 'oakland', 'City'),\n",
" ('is', 'trooper', 'Driver'),\n",
" ('is', 'denver', 'City'),\n",
" ('is', 'denver', 'Airport')]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"skb.facts()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A KB can be told a fact with the `tell` method."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"skb.tell(('is', 'san francisco', 'cool'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Similarly, the KB can be told a fact is *not* true with the `retract` method."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"skb.retract(('is', 'san francisco', 'cool'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The KB can be queried about the facts it has. There are two types of queries. The first is done with a full fact, and represents the question \"Is this fact true?\""
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[('is', 'oakland', 'City')]\n",
"[]\n"
]
}
],
"source": [
"print(skb.ask(('is', 'oakland', 'City')))\n",
"print(skb.ask(('is', 'cbr600', 'City')))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A query returns a list of facts; the empty list means no facts were found. This might seem an odd way to represent this first question; an invalid fact is represented by an empty list, or it returns a list of a single fact. The reason for doing it this way is to support the second type of question: \"What are the facts for which this query is valid?\" This is done by providing a `None` value to *either* the subject or object. (Eventually, I'll get around to adding support for empty relationships too...)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[('is', 'oakland', 'City'), ('is', 'denver', 'City')]\n"
]
}
],
"source": [
"print(skb.ask(('is', None, 'City')))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Another thing the KB can do is provide some basic substution using the `subst` method. It takes a fact template, a subject, and an object, and returns a fact (without making any statement as to the validity of the fact). The subject and object can be one of several values:\n",
"\n",
"+ `None`: the subject or object (depending on which position is `None`) from the arguments is substituted into the fact.\n",
"+ `?subject`: substitutes the subject.\n",
"+ `?object`: substitutes the object.\n",
"+ `?current`: the current value is kept --- this must be used only with singleton facts.\n",
"+ `?any`: the value is kept as `None`.\n",
"\n",
"Some examples should clarify this."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"('is', 'oakland', 'City')\n",
"('at', 'cbr600', 'oakland')\n",
"('is', 'City', 'oakland')\n"
]
}
],
"source": [
"print(skb.subst(('is', None, 'City'), 'oakland', None))\n",
"print(skb.subst(('at', '?subject', '?current'), 'cbr600', None))\n",
"print(skb.subst(('is', '?object', '?subject'), 'oakland', 'City'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To understand `subst`, it's useful to note that it was written to support actions.\n",
"\n",
"Actions are initialised with a positive precondition (facts that must be valid for the action to be performed), a negative precondition (facts that must not be valid for the action to be performed), a set of retractions, and a set of updates.\n",
"\n",
"To illustrate this, here's a small example of airplanes and airports."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"airport_kb = kb.from_facts([\n",
" ('is', 'N29EO', 'Plane'),\n",
" ('at', 'N29EO', 'dia'),\n",
" ('is', 'N10IV', 'Plane'),\n",
" ('at', 'N10IV', 'oak'),\n",
" ('is', 'N33FR', 'Plane'),\n",
" ('at', 'N33FR', 'lga'),\n",
" ('is', 'dia', 'Airport'),\n",
" ('is', 'lga', 'Airport'),\n",
" ('is', 'oak', 'Airport'),\n",
"])\n",
"\n",
"fly = actions.Action(\n",
" [('is', '?subject', 'Plane'), ('is', '?object', 'Airport')], # Positive preconditions.\n",
" [('at', '?subject', '?object'),], # Negative preconditions.\n",
" [('at', '?subject', '?current'),], # Retractions.\n",
" [('at', '?subject', '?object')]) # Updates."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For a `fly` action to be performed, there's a few facts we should make sure are true:\n",
"\n",
"1. The subject of the action is a `Plane`, and\n",
"2. The object of the action is an `Airport`.\n",
"\n",
"We should make sure that the subject isn't currently at our target airport.\n",
"\n",
"If these hold, we can perform the action. The retraction says that the subject is no longer at the airport it was at before the action, and the KB is updated to say that the plane is at a new airport."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Before flying, is N10IV at LGA? []\n",
"Before flying, is N10IV at OAK? [('at', 'N10IV', 'oak')]\n",
"After flying, is N10IV at LGA? [('at', 'N10IV', 'lga')]\n",
"After flying, is N10IV at OAK? []\n"
]
}
],
"source": [
"print('Before flying, is N10IV at LGA? ', airport_kb.ask(('at', 'N10IV', 'lga')))\n",
"print('Before flying, is N10IV at OAK? ', airport_kb.ask(('at', 'N10IV', 'oak')))\n",
"\n",
"new_airport_kb = fly.perform(airport_kb, 'N10IV', 'lga')\n",
"\n",
"print('After flying, is N10IV at LGA? ', new_airport_kb.ask(('at', 'N10IV', 'lga')))\n",
"print('After flying, is N10IV at OAK? ', new_airport_kb.ask(('at', 'N10IV', 'oak')))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There's more work to be done, but this represents a solid night of putting the plan into action based on what I'd learned from the AI nanodegree. I've got a bigger vision for what I want to do out of this, but it's nice to have a baseline to reason about."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

61
misc/aqe/NOTES.txt Normal file
View File

@@ -0,0 +1,61 @@
Inference example:
Given
airport_kb = kb.from_facts([
('is', 'N29EO', 'Plane'),
('at', 'N29EO', 'dia'),
('is', 'N10IV', 'Plane'),
('at', 'N10IV', 'oak'),
('is', 'N33FR', 'Plane'),
('at', 'N33FR', 'lga'),
('is', 'dia', 'Airport'),
('is', 'lga', 'Airport'),
('is', 'oak', 'Airport'),
])
fly = actions.Action(
[('is', '?subject', 'Plane'), ('is', '?object', 'Airport')],
[('at', '?subject', '?object'),],
[('at', '?subject', '?current'),],
[('at', '?subject', '?object')])
Should be able to do something like:
> infer(airport_kb, [fly], ('at', 'N10IV', 'lga'))
('fly', 'N10IV', 'lga')
------------------------------------------------------------------------------
Inference search example:
airport_kb = [
('is', 'N29EO', 'Plane'),
('at', 'N29EO', 'dia'),
('is', 'N10IV', 'Plane'),
('at', 'N10IV', 'oak'),
('is', 'N33FR', 'Plane'),
('at', 'N33FR', 'lga'),
('is', '1Z12345E0205271688', 'Package'),
('at', '1Z12345E0205271688', 'dia'),
('is', '1Z12345E6605272234', 'Package'),
('at', '1Z12345E6605272234', 'dia'),
('is', '1Z12345E0305271640', 'Package'),
('at', '1Z12345E0305271640', 'oak'),
('is', '1Z12345E1305277940', 'Package'),
('at', '1Z12345E1305277940', 'lga'),
('is', '1Z12345E6205277936', 'Package'),
('at', '1Z12345E6205277936', 'lga'),
('is', 'dia', 'Airport'),
('is', 'lga', 'Airport'),
('is', 'oak', 'Airport'),
]
fly = actions.Action(
[('is', '?subject', 'Plane'), ('is', '?object', 'Airport')],
[('at', '?subject', '?object'),],
[('at', '?subject', '?current'),],
[('at', '?subject', '?object')])
Trying to define load which requires that both package and airplane are
at the same place: how can this be expressed?

44
misc/aqe/README.md Normal file
View File

@@ -0,0 +1,44 @@
## AQE: A Query Engine
This is an implementation of a knowledge base, hacked together in Python
3 (it won't work in Python 2 for reasons of modules) for now to quickly
iterate on ideas.
There are a few key points:
+ A `KnowledgeBase` contains facts.
+ A fact is a tuple: (relationship, subject, object). For example,
`('is', 'sky', 'blue')`.
+ A `KnowledgeBase` has three core methods: ask, retract, and tell.
+ The `ask` method queries the `KnowledgeBase` to ascertain whether
a fact is true. Either the subject or the object may be `None`,
in which case all satisifiable facts are returned.
+ The `retract` method tells the `KnowledgeBase` that the fact is
no longer true. If it's rainy, we might retract our fact about the
sky being blue.
+ The `tell` method tells the `KnowledgeBase` that the fact is
now true. For example, if it's rainy (and we've retracted the previous
'sky is blue' fact), we might tell the `KnowledgeBase` that
`('is', 'sky', 'grey')`.
+ A `KnowledgeBase` can also perform substitutions.
+ An action contains positive and negative preconditions, retractions,
and updates. The positive condition list contains facts that must
be true for a knowledge base, and the negative condition list contains
facts that must be false. If these preconditions hold, the retractions
are applied, followed by the updates.
+ See `test_actions.py` for an example.
### Limitations
+ Singleton facts aren't supported; that is, there is no way to make a
`KnowledgeBase` assert that there is only one relationship → subject
mapping. For example, the `KnowledgeBase` will admit that
`('is', 'shrödingers cat', 'alive')` and
`('is', 'schrödingers cat', 'dead')` are both true simultaneously.
### TODO
+ Inference: given a list of actions, how to go from one state to
another. The first step would be single-step, then integrating
a search into the inference.
+ Rewrite in C++?

0
misc/aqe/__init__.py Normal file
View File

33
misc/aqe/actions.py Normal file
View File

@@ -0,0 +1,33 @@
import copy
import logging
class Action:
def __init__(self, pos_precond, neg_precond, retracts, updates):
self.pos_precond = copy.deepcopy(pos_precond)
self.neg_precond = copy.deepcopy(neg_precond)
self.retracts = copy.deepcopy(retracts)
self.updates = copy.deepcopy(updates)
def satisfied(self, kb, subject, obj):
for fact in self.pos_precond:
if not kb.ask(kb.subst(fact, subject, obj)):
logging.warning('{} is not valid in the current knowledgebase'.format(fact))
return False
for fact in self.neg_precond:
if kb.ask(kb.subst(fact, subject, obj)):
logging.warning('{} is valid in the current knowledgebase'.format(fact))
return False
return True
def perform(self, kb, subject, obj):
if not self.satisfied(kb, subject, obj):
return None
kbprime = copy.deepcopy(kb)
for retraction in self.retracts:
kbprime.retract(kb.subst(retraction, subject, obj))
for update in self.updates:
kbprime.tell(kb.subst(update, subject, obj))
return kbprime

File diff suppressed because one or more lines are too long

153
misc/aqe/example.md Normal file
View File

@@ -0,0 +1,153 @@
```python
import actions, kb, sample
```
## AQE: A Query Engine
This is an implementation of a knowledge base, hacked together in Python
3 (it won't work in Python 2 for reasons of modules) for now to quickly
iterate on ideas.
The `KnowledgeBase` is a repository of facts.
```python
skb = sample.load()
```
A fact is a tuple: (relationship, subject, object). `object` is admittedly a terrible name (and is subject to change) but it's what I came up with and what I'm working with for now.
```python
skb.facts()
```
[('is', 'cbr600', 'Driver'),
('at', 'cbr600', 'oakland'),
('at', 'airliner', 'denver'),
('is', 'oakland', 'Airport'),
('is', 'airliner', 'Flyer'),
('is', 'oakland', 'City'),
('is', 'trooper', 'Driver'),
('is', 'denver', 'City'),
('is', 'denver', 'Airport')]
A KB can be told a fact with the `tell` method.
```python
skb.tell(('is', 'san francisco', 'cool'))
```
Similarly, the KB can be told a fact is *not* true with the `retract` method.
```python
skb.retract(('is', 'san francisco', 'cool'))
```
The KB can be queried about the facts it has. There are two types of queries. The first is done with a full fact, and represents the question "Is this fact true?"
```python
print(skb.ask(('is', 'oakland', 'City')))
print(skb.ask(('is', 'cbr600', 'City')))
```
[('is', 'oakland', 'City')]
[]
A query returns a list of facts; the empty list means no facts were found. This might seem an odd way to represent this first question; an invalid fact is represented by an empty list, or it returns a list of a single fact. The reason for doing it this way is to support the second type of question: "What are the facts for which this query is valid?" This is done by providing a `None` value to *either* the subject or object. (Eventually, I'll get around to adding support for empty relationships too...)
```python
print(skb.ask(('is', None, 'City')))
```
[('is', 'oakland', 'City'), ('is', 'denver', 'City')]
Another thing the KB can do is provide some basic substution using the `subst` method. It takes a fact template, a subject, and an object, and returns a fact (without making any statement as to the validity of the fact). The subject and object can be one of several values:
+ `None`: the subject or object (depending on which position is `None`) from the arguments is substituted into the fact.
+ `?subject`: substitutes the subject.
+ `?object`: substitutes the object.
+ `?current`: the current value is kept --- this must be used only with singleton facts.
+ `?any`: the value is kept as `None`.
Some examples should clarify this.
```python
print(skb.subst(('is', None, 'City'), 'oakland', None))
print(skb.subst(('at', '?subject', '?current'), 'cbr600', None))
print(skb.subst(('is', '?object', '?subject'), 'oakland', 'City'))
```
('is', 'oakland', 'City')
('at', 'cbr600', 'oakland')
('is', 'City', 'oakland')
To understand `subst`, it's useful to note that it was written to support actions.
Actions are initialised with a positive precondition (facts that must be valid for the action to be performed), a negative precondition (facts that must not be valid for the action to be performed), a set of retractions, and a set of updates.
To illustrate this, here's a small example of airplanes and airports.
```python
airport_kb = kb.from_facts([
('is', 'N29EO', 'Plane'),
('at', 'N29EO', 'dia'),
('is', 'N10IV', 'Plane'),
('at', 'N10IV', 'oak'),
('is', 'N33FR', 'Plane'),
('at', 'N33FR', 'lga'),
('is', 'dia', 'Airport'),
('is', 'lga', 'Airport'),
('is', 'oak', 'Airport'),
])
fly = actions.Action(
[('is', '?subject', 'Plane'), ('is', '?object', 'Airport')], # Positive preconditions.
[('at', '?subject', '?object'),], # Negative preconditions.
[('at', '?subject', '?current'),], # Retractions.
[('at', '?subject', '?object')]) # Updates.
```
For a `fly` action to be performed, there's a few facts we should make sure are true:
1. The subject of the action is a `Plane`, and
2. The object of the action is an `Airport`.
We should make sure that the subject isn't currently at our target airport.
If these hold, we can perform the action. The retraction says that the subject is no longer at the airport it was at before the action, and the KB is updated to say that the plane is at a new airport.
```python
print('Before flying, is N10IV at LGA? ', airport_kb.ask(('at', 'N10IV', 'lga')))
print('Before flying, is N10IV at OAK? ', airport_kb.ask(('at', 'N10IV', 'oak')))
new_airport_kb = fly.perform(airport_kb, 'N10IV', 'lga')
print('After flying, is N10IV at LGA? ', new_airport_kb.ask(('at', 'N10IV', 'lga')))
print('After flying, is N10IV at OAK? ', new_airport_kb.ask(('at', 'N10IV', 'oak')))
```
Before flying, is N10IV at LGA? []
Before flying, is N10IV at OAK? [('at', 'N10IV', 'oak')]
After flying, is N10IV at LGA? [('at', 'N10IV', 'lga')]
After flying, is N10IV at OAK? []
There's more work to be done, but this represents a solid night of putting the plan into action based on what I'd learned from the AI nanodegree. I've got a bigger vision for what I want to do out of this, but it's nice to have a baseline to reason about.

159
misc/aqe/kb.py Normal file
View File

@@ -0,0 +1,159 @@
"""
AQE: A Query Engine
This is a proof of concept of a baseline query engine for AI work.
"""
class InvalidQuery(Exception):
pass
class Inconsistency(Exception):
def __init__(self, fact):
self.fact = fact
def __str__(self):
return 'Inconsistency: {}'.format(self.fact)
class KnowledgeBase:
def __init__(self):
# TODO(kyle): support loading an initial set of facts.
self.__kb__ = {}
self.__facts__ = set()
def tell(self, fact):
relationship, subject, obj = fact
# NB: in the future, these assertions may not need to be true; there
# might be space in the world for "fuzzy" facts.
assert(relationship)
assert(subject)
assert(obj)
if relationship not in self.__kb__:
self.__kb__[relationship] = {'subjects':{}, 'objects': {}}
if subject not in self.__kb__[relationship]['subjects']:
self.__kb__[relationship]['subjects'][subject] = set()
self.__kb__[relationship]['subjects'][subject].add(obj)
if obj not in self.__kb__[relationship]['objects']:
self.__kb__[relationship]['objects'][obj] = set()
self.__kb__[relationship]['objects'][obj].add(subject)
self.__facts__.add(fact)
def retract(self, fact):
relationship, subject, obj = fact
# For now, these assertions are required. In the future, it would be
# interesting to say something to the effect of "forget everything you
# know about X".
assert(relationship)
assert(subject)
assert(obj)
# TODO(kyle): answer existential question: if I delete all the objects
# from a subject (or vice versa), should that subject/object be kept or
# removed entirely? This is the difference between "I have no concept
# of X" and "I am aware that X exists but I don't know anything about it".
# For now, I'm electing to keep the entry.
#
# Similarly, if the relationship is empty, we could make the argument
# for removing it --- at the expense of now saying that we have no
# concept of this relationship.
try:
self.__kb__[relationship]['subjects'][subject].remove(obj)
self.__kb__[relationship]['objects'][obj].remove(subject)
self.__facts__.remove(fact)
except KeyError:
# Being told to forget something about something you don't know
# isn't an error.
pass
pass
def ask(self, fact):
relationship, subject, obj = fact
# A future milestone will remove this requirement to support free
# variables.
assert(relationship)
if relationship and subject and obj:
if fact in self.__facts__:
return [fact,]
return []
if relationship and subject:
return [(relationship, subject, _obj) for _obj
in self.__kb__[relationship]['subjects'][subject]]
if relationship and obj:
return [(relationship, _subject, obj) for _subject
in self.__kb__[relationship]['objects'][obj]]
def facts(self):
return list(self.__facts__)
def is_consistent(self):
try:
for fact in self.__facts__:
relationship, subject, obj = fact
if obj not in self.__kb__[relationship]['subjects'][subject]:
raise Inconsistency(fact)
if subject not in self.__kb__[relationship]['objects'][obj]:
raise Inconsistency(fact)
for relationship, v in self.__kb__.items():
for subject in v['subjects'].keys():
for obj in v['subjects'][subject]:
if (relationship, subject, obj) not in self.__facts__:
raise Inconsistency(fact)
for obj in v['objects'].keys():
for subject in v['objects'][obj]:
if (relationship, subject, obj) not in self.__facts__:
raise Inconsistency(fact)
except KeyError:
raise Inconsistency(fact)
return True
def __len__(self):
return len(self.__facts__)
def subst(self, fact, subject, obj):
relationship, _subject, _obj = fact
if _subject is None:
_subject = subject
if _subject == '?any':
_subject = None
elif _subject == '?subject':
_subject = subject
elif _subject == '?object':
_subject = obj
if _obj is None:
_obj = obj
if _obj == '?any':
_obj = None
elif _obj == '?subject':
_obj = subject
elif _obj == '?object':
_obj = obj
if _subject == '?current':
possibilities = self.ask((relationship, None, _obj))
assert(len(possibilities) == 1)
_, _subject, _ = possibilities[0]
elif _obj == '?current':
possibilities = self.ask((relationship, subject, None))
assert(len(possibilities) == 1)
_, _, _obj = possibilities[0]
return (relationship, _subject, _obj)
def from_facts(facts):
kb = KnowledgeBase()
for fact in facts:
kb.tell(fact)
return kb

47
misc/aqe/sample.py Normal file
View File

@@ -0,0 +1,47 @@
import base64
import itertools
import json
import kb
import pickle
import random
FACTS = """
gANdcQAoWAIAAABpc3EBWAgAAABhaXJsaW5lcnECWAUAAABGbHllcnEDh3EEaAFYBwAAAG9ha2xh
bmRxBVgHAAAAQWlycG9ydHEGh3EHaAFoBVgEAAAAQ2l0eXEIh3EJaAFYBgAAAGRlbnZlcnEKaAaH
cQtoAWgKaAiHcQxoAVgGAAAAY2JyNjAwcQ1YBgAAAERyaXZlcnEOh3EPaAFYBwAAAHRyb29wZXJx
EGgOh3ERWAIAAABhdHESaAJoCodxE2gSaA1oBYdxFGUu
"""
def load():
facts = base64.decodebytes(FACTS.encode('ascii'))
facts = pickle.loads(facts)
skb = kb.KnowledgeBase()
for fact in facts:
skb.tell(fact)
return skb
def load_facts(corpus_path='data/corpus.json', is_count=1000000):
facts = set()
corpus = json.loads(open(corpus_path).read())
if 'nouns' in corpus and 'adjectives' in corpus:
perms = list(itertools.product(corpus['nouns'],
corpus['adjectives']))
if len(perms) < is_count:
is_count = len(perms)-1;
pool = random.choices(perms, k=is_count)
for noun, adjective in pool:
facts.add(('is', noun, adjective))
if 'cities' in corpus:
for city in corpus['cities']:
facts.add(('is', city, 'City'))
return facts
def generate_tail_number():
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
tailno = 'N' + str(random.randint(10, 99))
tailno += random.choice(letters)
tailno += random.choice(letters)
return tailno

67
misc/aqe/test_actions.py Normal file
View File

@@ -0,0 +1,67 @@
import actions
import kb
import unittest
INITIAL_FACTS = [
('is', 'N29EO', 'Plane'),
('at', 'N29EO', 'dia'),
('is', 'N10IV', 'Plane'),
('at', 'N10IV', 'oak'),
('is', 'N33FR', 'Plane'),
('at', 'N33FR', 'lga'),
('is', '1Z12345E0205271688', 'Package'),
('at', '1Z12345E0205271688', 'dia'),
('is', '1Z12345E6605272234', 'Package'),
('at', '1Z12345E6605272234', 'dia'),
('is', '1Z12345E0305271640', 'Package'),
('at', '1Z12345E0305271640', 'oak'),
('is', '1Z12345E1305277940', 'Package'),
('at', '1Z12345E1305277940', 'lga'),
('is', '1Z12345E6205277936', 'Package'),
('at', '1Z12345E6205277936', 'lga'),
('is', 'dia', 'Airport'),
('is', 'lga', 'Airport'),
('is', 'oak', 'Airport'),
]
FLY_POS_PRECONDS = [
('is', '?subject', 'Plane'),
('is', '?object', 'Airport'),
]
FLY_NEG_PRECONDS = [
('at', '?subject', '?object'),
]
FLY_RETRACTIONS = [
('at', '?subject', '?current'),
]
FLY_UPDATES = [
('at', '?subject', '?object'),
]
fly = actions.Action(FLY_POS_PRECONDS, FLY_NEG_PRECONDS,
FLY_RETRACTIONS, FLY_UPDATES)
class ActionTestSuite(unittest.TestCase):
def setUp(self):
self.kb = kb.from_facts(INITIAL_FACTS)
def test_a_flight(self):
self.assertTrue(self.kb.ask(('at', 'N10IV', 'oak')))
self.assertFalse(self.kb.ask(('at', 'N10IV', 'lga')))
shadow = fly.perform(self.kb, 'N10IV', 'lga')
self.assertTrue(shadow)
# Shadow should reflect the updates and retractions.
self.assertTrue(shadow.ask(('at', 'N10IV', 'lga')))
self.assertFalse(shadow.ask(('at', 'N10IV', 'oak')))
# The original shouldn't be touched.
self.assertTrue(self.kb.ask(('at', 'N10IV', 'oak')))
self.assertFalse(self.kb.ask(('at', 'N10IV', 'lga')))

59
misc/aqe/test_kb.py Normal file
View File

@@ -0,0 +1,59 @@
import copy
import kb
import random
import sample
import unittest
class KnowledgeBaseTestSuite(unittest.TestCase):
def setUp(self):
self.kb = sample.load()
def test_a_sanity_check(self):
assert(self.kb.is_consistent())
for fact in self.kb.__facts__:
self.assertTrue(self.kb.ask(fact))
def test_tell(self):
new_fact = ('is', 'berkeley', 'City')
# make sure it's not something we already know
self.assertFalse(self.kb.ask(new_fact))
self.kb.tell(new_fact)
answer = self.kb.ask(new_fact)
self.assertListEqual(answer, [new_fact,])
def test_inconsistency(self):
badkb = copy.deepcopy(self.kb)
badfact = random.choice(badkb.facts())
relationship, subject, obj = badfact
# muck with subjects part
badkb.__kb__[relationship]['subjects'][subject].remove(obj)
with self.assertRaises(kb.Inconsistency):
badkb.is_consistent()
# muck with objects part
badkb = copy.deepcopy(self.kb)
badkb.__kb__[relationship]['objects'][obj].remove(subject)
with self.assertRaises(kb.Inconsistency):
badkb.is_consistent()
# muck with facts part
badkb = copy.deepcopy(self.kb)
badkb.__facts__.remove(badfact)
with self.assertRaises(kb.Inconsistency):
badkb.is_consistent()
# inject false data into the subject
badkb = copy.deepcopy(self.kb)
badkb.__kb__[relationship]['subjects'][subject].add('false memory')
with self.assertRaises(kb.Inconsistency):
badkb.is_consistent()
# inject false data into the object
badkb = copy.deepcopy(self.kb)
badkb.__kb__[relationship]['objects'][obj].add('false memory')
with self.assertRaises(kb.Inconsistency):
badkb.is_consistent()

8
misc/aqe/util.py Normal file
View File

@@ -0,0 +1,8 @@
import random
def generate_tail_number():
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
tailno = 'N' + random.randint(10, 99)
tailno += random.choice(letters)
tailno += random.choice(letters)
return tailno