Semantic networks, inheritance, and default reasoning

Represent categories and properties as a network, and let specific knowledge override general defaults.

Python
parents = {"tweety": "penguin", "penguin": "bird", "bird": "animal", "polly": "parrot", "parrot": "bird"}
properties = {
    "animal": {"alive": True},
    "bird": {"flies": True, "legs": 2},
    "penguin": {"flies": False},
}


def lookup(thing, attribute):
    while thing is not None:
        if attribute in properties.get(thing, {}):
            return properties[thing][attribute]
        thing = parents.get(thing)
    return None


print(lookup("polly", "flies"))
print(lookup("tweety", "flies"))
print(lookup("tweety", "legs"), lookup("tweety", "alive"))
print(lookup("tweety", "color"))
Output
True
False
2 True
None

Explanation

Not all knowledge is best written as logical formulas. A semantic network represents concepts as nodes and relations as labelled links, with is-a links linking an individual or category to a more general one. Frames add structured slots for each concept. These structures make it easy to inherit properties: a bird has two legs, so a parrot, being a bird, has two legs too, without that fact being stored on the parrot.

Inheritance supports default reasoning. 'Birds fly' is a useful rule even though penguins do not. The lookup walks up the is-a chain and returns the first value it finds, so the most specific definition wins: a penguin's flies = False overrides the bird default. This kind of reasoning is nonmonotonic, because learning that Tweety is a penguin withdraws the earlier conclusion that Tweety flies. Classical logic is monotonic: adding a sentence can never remove a conclusion.

Pure single-inheritance trees are easy to read. With multiple inheritance a node may have several parents that disagree, as in the Nixon diamond, so the system needs a rule to resolve or report the conflict. Modern descendants of semantic networks include RDF knowledge graphs, description logics, and the OWL ontology language, which add precise logical meaning and automated reasoning to the same intuitive structure.

Explore the dry run
Guided walkthroughReady
1parents = {"tweety": "penguin", "penguin": "bird", "bird": "animal", "polly": "parrot", "parrot": "bird"}
2properties = {
3 "animal": {"alive": True},
4 "bird": {"flies": True, "legs": 2},
5 "penguin": {"flies": False},
6}
7
8
9def lookup(thing, attribute):
10 while thing is not None:
11 if attribute in properties.get(thing, {}):
12 return properties[thing][attribute]
13 thing = parents.get(thing)
14 return None
15
16
17print(lookup("polly", "flies"))
18print(lookup("tweety", "flies"))
19print(lookup("tweety", "legs"), lookup("tweety", "alive"))
20print(lookup("tweety", "color"))

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
If the property is not defined here, follow the is-a link to the parent concept and look again.—
—
Polly is a parrot, and a parrot is a bird. The bird default says flies = True, so it is inherited.—
True
Tweety is a penguin. penguin defines flies = False, which is found before the bird default and overrides it.—
True
False
legs comes from bird and alive from animal, two and three steps up the chain.—
True
False
2 True
Nothing in the chain defines color, so the answer is unknown: None.—
True
False
2 True
None

Cost and common mistakes

A lookup follows at most the depth of the hierarchy, so it is O(d) for depth d.

  • None means unknown, not false. The closed-world assumption (what is not known is false) is a modelling choice with consequences.
  • Default conclusions can be retracted. Do not treat an inherited default as certain knowledge.
  • With multiple inheritance, a lookup that returns the first path found may hide a conflict. Decide how conflicts are reported or resolved.

Try it yourself

How would you record that 'opus' is a penguin who can fly in a cartoon?

Reveal answer and reasoning

Give opus its own definition: parents["opus"] = "penguin" and properties["opus"] = {"flies": True}. The most specific definition overrides penguin's.

Further reading