"I can represent any fact about the universe in first-order logic. The only catch is that the universe keeps inventing facts faster than I can quantify over them."
A Predicate Calculus With Existential Anxiety
Knowledge representation is the bridge between human understanding and machine reasoning. Before a discovery system can combine facts, test hypotheses, or spot contradictions, it needs a formal language in which those facts can be stated without ambiguity. This section walks the expressiveness ladder from propositional logic (simple but rigid) through first-order logic (powerful but undecidable in full generality) to description logics (the carefully engineered sweet spot that underlies modern ontology languages like Web Ontology Language (OWL)). Along the way, we connect each formalism to concrete tools: Python's sympy.logic, the World Wide Web Consortium (W3C) RDF and OWL standards, and the owlready2 library that lets you build and query ontologies in a few lines of code. Figure 3.1 maps the six formalisms covered here along two axes: expressiveness and computational tractability.
1. Propositional Logic: The Simplest Formal Language
"Aspirin inhibits cyclooxygenase-2 (COX-2)": true or false, no maybes allowed. That uncompromising binary crispness is the starting point of propositional logic, the simplest formal language for encoding scientific knowledge. A proposition is any declarative statement that must be one or the other ("the sample contains graphene," "pH exceeds 7"), and propositional logic gives us five connectives to build compound statements from these atoms:
- Negation (\(\neg p\)): "aspirin does not inhibit COX-2."
- Conjunction (\(p \land q\)): "the sample contains graphene and it is electrically conductive."
- Disjunction (\(p \lor q\)): "the compound is synthesized via route A or route B."
- Implication (\(p \to q\)): "if pH is below 3, then the protein denatures."
- Biconditional (\(p \leftrightarrow q\)): "the reaction is exothermic if and only if \(\Delta H < 0\)."
A knowledge base in propositional logic is a set of such formulas. Reasoning reduces to checking whether a query formula is a logical consequence of the knowledge base, written \(\text{KB} \models q\). For propositional logic this is decidable: truth tables or the Davis-Putnam-Logemann-Loveland (DPLL) algorithm (the ancestor of every Boolean satisfiability (SAT) solver) can answer the question in finite time, although worst-case complexity is NP-complete.
A knowledge base collects every formula the system accepts as true. It is the central data structure in any logic-based AI system, the single source of ground truth against which every query is evaluated. Knowledge bases matter because they separate what the system knows from how it reasons. The same inference engine works across different domains when you swap the stored formulas. The mechanism is straightforward: you add formulas (the "tell" operation) and then ask whether a new formula follows logically from what has been stored (the "ask" operation, implemented via satisfiability checking or resolution). Use a knowledge base when your domain has clear, categorical rules that can be stated as logical sentences; prefer statistical or learned models when the relationships are probabilistic, noisy, or too numerous to enumerate by hand. In short: logic gives a machine the ability to say not just what it knows, but what necessarily follows from what it knows.
Entailment in Practice
Let us build a tiny propositional knowledge base and check entailment using SymPy, which provides a clean symbolic logic engine.
from sympy.logic.boolalg import And, Or, Implies, Not
from sympy.logic.inference import satisfiable
from sympy import symbols
# Define atomic propositions
inhibits_cox2, reduces_inflammation, is_nsaid, has_gi_risk = symbols(
'inhibits_cox2 reduces_inflammation is_nsaid has_gi_risk'
)
# Knowledge base: three rules about NSAIDs
kb = And(
Implies(is_nsaid, inhibits_cox2), # NSAIDs inhibit COX-2
Implies(inhibits_cox2, reduces_inflammation), # COX-2 inhibition reduces inflammation
Implies(is_nsaid, has_gi_risk), # NSAIDs carry GI risk
is_nsaid # Aspirin is an NSAID (ground fact)
)
# Query: does aspirin reduce inflammation?
# Check if KB ∧ ¬query is unsatisfiable (refutation)
query = reduces_inflammation
refutation = And(kb, Not(query))
result = satisfiable(refutation)
print(f"KB entails 'reduces_inflammation': {result == False}")
# Output: KB entails 'reduces_inflammation': True
# Query: does aspirin carry GI risk?
refutation2 = And(kb, Not(has_gi_risk))
result2 = satisfiable(refutation2)
print(f"KB entails 'has_gi_risk': {result2 == False}")
# Output: KB entails 'has_gi_risk': True
Common Misconception
Readers frequently confuse logical implication (\(p \to q\)) with causation. In propositional logic, \(p \to q\) is true whenever \(p\) is false, regardless of \(q\). The statement "if pH is below 3, then the protein denatures" does not assert that low pH causes denaturation; it only says the two truth values are constrained so that \(p\) being true while \(q\) is false is impossible. Causal reasoning requires additional machinery (interventions, counterfactuals) that pure propositional or first-order logic does not provide.
Exercise 3.1.1
Suppose you add a fourth rule to the propositional knowledge base in Listing 3.1: Implies(has_gi_risk, requires_monitoring), along with a new symbol requires_monitoring. Without running the code, determine whether KB |= requires_monitoring holds. Then verify your answer by extending the SymPy code and checking with the refutation method shown above.
Hint
Trace the chain of implications forward from is_nsaid. The KB already entails has_gi_risk (the code proves this). If the new rule links has_gi_risk to requires_monitoring, ask yourself: can you find any truth assignment that makes the KB true while making requires_monitoring false?
The limitation of propositional logic for scientific knowledge is immediately apparent: we cannot say "for all NSAIDs" or "there exists a compound that." Every fact about every individual must be stated separately. To talk about classes, instances, and relationships, we need quantifiers.
2. First-Order Logic: Variables, Quantifiers, and Predicates
First-order logic (FOL) extends propositional logic with three ingredients: variables that range over individuals in a domain, predicates that express properties and relationships, and quantifiers (\(\forall\) and \(\exists\)) that bind variables. The NSAID rules from above become a single universally quantified statement:
$$\forall x \, [\text{NSAID}(x) \to \text{InhibitsCOX2}(x) \land \text{HasGIRisk}(x)]$$This one formula replaces the need to enumerate every NSAID individually. We can also express relational facts: \(\text{Inhibits}(\text{aspirin}, \text{COX\text{-}2})\) is a binary predicate linking a drug to a target. Functions let us build complex terms: \(\text{MolWeight}(\text{aspirin}) = 180.16\) uses a function symbol that maps a compound to its molecular weight.
Full first-order logic is semi-decidable: if a formula is a logical consequence of a knowledge base, a proof procedure will eventually find the proof, but if it is not, the procedure may run forever. This is a fundamental result (Church and Turing, 1936). For building practical discovery systems, we need fragments of FOL that are both expressive enough to capture scientific knowledge and decidable enough to answer queries in finite time. Description logics are precisely such fragments. Figure 3.1 shows where each formalism falls on this trade-off.
FOL is the lingua franca of mathematical reasoning, and its influence pervades every knowledge representation formalism in this book. The Prolog programming language, for example, restricts FOL to Horn clauses (formulas of the form "if \(A_1\) and \(A_2\) and \(\ldots\) then \(B\)," where each \(A_i\) and \(B\) are positive atomic statements) and uses SLD resolution, a goal-directed proof strategy that works backward from the query to the known facts, for backward chaining. The reasoning engines in Chapter 4 build directly on FOL inference.
Here is a compact FOL knowledge base about drug targets, encoded in Python using a dictionary-based representation and a simple forward-chaining reasoner:
from dataclasses import dataclass
from typing import Set, Tuple
@dataclass(frozen=True)
class Fact:
predicate: str
args: tuple
def __repr__(self):
return f"{self.predicate}({', '.join(str(a) for a in self.args)})"
class FOLKnowledgeBase:
"""A toy first-order knowledge base with forward chaining."""
def __init__(self):
self.facts: Set[Fact] = set()
self.rules = [] # list of (condition_fn, consequent_fn)
def tell(self, predicate: str, *args):
self.facts.add(Fact(predicate, args))
def add_rule(self, condition_fn, consequent_fn):
"""condition_fn(fact) -> bool; consequent_fn(fact) -> list of new Facts."""
self.rules.append((condition_fn, consequent_fn))
def forward_chain(self, max_iterations=100):
"""Apply rules until no new facts are derived."""
for _ in range(max_iterations):
new_facts = set()
for fact in self.facts:
for cond, conseq in self.rules:
if cond(fact):
for new_fact in conseq(fact):
if new_fact not in self.facts:
new_facts.add(new_fact)
if not new_facts:
break
self.facts |= new_facts
return self.facts
def ask(self, predicate: str, *args) -> bool:
return Fact(predicate, args) in self.facts
# Build KB
kb = FOLKnowledgeBase()
kb.tell("NSAID", "aspirin")
kb.tell("NSAID", "ibuprofen")
kb.tell("Target", "COX-2")
# Rule: for all x, NSAID(x) -> InhibitsCOX2(x)
kb.add_rule(
lambda f: f.predicate == "NSAID",
lambda f: [Fact("InhibitsCOX2", (f.args[0],)),
Fact("HasGIRisk", (f.args[0],))]
)
# Rule: InhibitsCOX2(x) -> ReducesInflammation(x)
kb.add_rule(
lambda f: f.predicate == "InhibitsCOX2",
lambda f: [Fact("ReducesInflammation", (f.args[0],))]
)
derived = kb.forward_chain()
print(f"Derived {len(derived)} facts:")
for f in sorted(derived, key=str):
print(f" {f}")
print(f"\nDoes ibuprofen reduce inflammation? {kb.ask('ReducesInflammation', 'ibuprofen')}")
# Output:
# Derived 9 facts:
# HasGIRisk(aspirin)
# HasGIRisk(ibuprofen)
# InhibitsCOX2(aspirin)
# InhibitsCOX2(ibuprofen)
# NSAID(aspirin)
# NSAID(ibuprofen)
# ReducesInflammation(aspirin)
# ReducesInflammation(ibuprofen)
# Target(COX-2)
#
# Does ibuprofen reduce inflammation? True
Step-Through: Forward Chaining on the Drug Knowledge Base
Trace through the forward chaining algorithm from Listing 3.2, iteration by iteration, with the exact facts derived at each step.
Initial facts (before chaining): {NSAID(aspirin), NSAID(ibuprofen), Target(COX-2)} (3 facts)
Iteration 1: The engine scans all three facts in self.facts against both rules. Rule 1 matches NSAID(aspirin) and produces InhibitsCOX2(aspirin) and HasGIRisk(aspirin). Rule 1 also matches NSAID(ibuprofen) and produces InhibitsCOX2(ibuprofen) and HasGIRisk(ibuprofen). Rule 2 does not fire because the inner loop iterates over self.facts (the three original facts), not over new_facts; the newly derived InhibitsCOX2 facts are collected in new_facts and are not visible to Rule 2 until the next iteration. New facts added: {InhibitsCOX2(aspirin), InhibitsCOX2(ibuprofen), HasGIRisk(aspirin), HasGIRisk(ibuprofen)} (4 new). Total: 7 facts.
Iteration 2: The engine rescans all 7 facts. Rule 2 now matches InhibitsCOX2(aspirin) and InhibitsCOX2(ibuprofen), producing ReducesInflammation(aspirin) and ReducesInflammation(ibuprofen). Rule 1 re-matches the NSAID facts, but the consequent facts already exist. New facts added: {ReducesInflammation(aspirin), ReducesInflammation(ibuprofen)} (2 new). Total: 9 facts.
Iteration 3: The engine rescans all 9 facts. Every rule match produces facts that already exist in the knowledge base, so new_facts is empty. The algorithm reaches its fixpoint (where fixpoint means the state at which applying the rules produces no further new facts) and terminates.
Key observation: Because the inner loop iterates over self.facts (facts known at the start of each iteration), "grandchild" facts such as ReducesInflammation cannot be derived in the same iteration as their prerequisite InhibitsCOX2 facts. This implementation therefore requires two productive iterations plus one termination check. An alternative design that adds new facts to the working set mid-iteration could collapse these into a single pass, at the cost of making iteration counts order-dependent.
3. Description Logics: The Decidable Core of Ontology Languages
When a hospital's clinical decision system misclassifies a drug interaction because its reasoning engine cannot finish computing in time, patients are at risk. That failure mode is exactly what drove the design of description logics: formalisms where every query is guaranteed to terminate, so that safety-critical systems can rely on them.
FOL's expressive power comes at a steep computational price, so researchers carved out restricted fragments that guarantee termination while retaining enough structure to model real scientific domains.
Description logics (DLs) sit between propositional logic and full FOL. They are carefully calibrated: expressive enough for real-world ontologies, yet decidable for key reasoning tasks. The basic building blocks are:
- Concepts (unary predicates): classes of individuals, such as Drug, Protein, Disease.
- Roles (binary predicates): relationships between individuals, such as inhibits, treats, causedBy.
- Individuals (constants): named entities, such as aspirin, COX-2, rheumatoid_arthritis.
Concepts can be combined using constructors. In the widely-used \(\mathcal{ALC}\) (Attributive Language with Complements) description logic, the constructors are:
$$C, D \;::=\; A \;\mid\; \top \;\mid\; \bot \;\mid\; \neg C \;\mid\; C \sqcap D \;\mid\; C \sqcup D \;\mid\; \forall R.C \;\mid\; \exists R.C$$where \(A\) is an atomic concept, \(R\) is a role, \(\top\) is the universal concept (everything), and \(\bot\) is the empty concept (nothing). The expression \(\exists \text{inhibits}.\text{Enzyme}\) denotes "the class of things that inhibit at least one enzyme." The expression \(\text{Drug} \sqcap \exists \text{treats}.\text{Cancer}\) denotes "drugs that treat some cancer."
Checkpoint
So far: description logics restrict first-order logic to three building blocks (concepts, roles, individuals) combined through a fixed set of constructors (\(\neg\), \(\sqcap\), \(\sqcup\), \(\forall R.C\), \(\exists R.C\)), and this restriction is what makes reasoning decidable. Next, we see how these building blocks are organized into TBox (class-level axioms) and ABox (individual-level facts).
Mental Model
Think of a description logic as the rules governing a library catalog system. Concepts are like catalog categories (Fiction, Biography, Science). Roles are the typed cross-reference links between catalog cards ("written-by," "sequel-to," "published-in"). The constructors let you build composite search queries: \(\text{Fiction} \sqcap \exists \text{writtenBy}.\text{BritishAuthor}\) is the catalog section for "fiction written by at least one British author." The TBox is the cataloging rulebook that says "every Novel is a subclass of Fiction," and the ABox is the actual card drawer full of individual entries. What makes description logics special, and what distinguishes this catalog from a free-text search engine, is that the system can automatically check whether one category is a subset of another, flag contradictory classifications, and infer category memberships you never explicitly entered, all with a guarantee that the process terminates.
A TBox (terminological box, the component of a DL knowledge base that stores concept definitions and axioms about classes) contains concept definitions and axioms:
$$\text{NSAID} \sqsubseteq \text{Drug} \sqcap \exists \text{inhibits}.\text{COXEnzyme}$$This says: every NSAID is a Drug that inhibits some COX enzyme. An ABox (assertional box, the component that stores facts about specific named individuals) contains facts about individuals:
$$\text{NSAID}(\text{aspirin}), \quad \text{inhibits}(\text{aspirin}, \text{COX-2})$$The four standard reasoning tasks for description logics are: satisfiability (can concept \(C\) have any instances?), subsumption (is every instance of \(C\) also an instance of \(D\)?), instance checking (is individual \(a\) an instance of concept \(C\)?), and retrieval (find all individuals that are instances of \(C\)). For \(\mathcal{ALC}\), all four are decidable in EXPTIME (exponential time in the size of the input, meaning the algorithm terminates but may take time that grows exponentially with the number of axioms). Figure 3.1.1 illustrates Description Logic TBox and ABox architecture.
The Gene Ontology (GO) is one of the most successful ontologies in science. It defines roughly 45,000 terms organized into three hierarchies: Biological Process (e.g., "apoptotic process"), Molecular Function (e.g., "protein kinase activity"), and Cellular Component (e.g., "mitochondrial matrix"). GO uses a restricted description logic with role restrictions like \(\text{part\_of}\) and \(\text{regulates}\). Automated reasoners classify new gene annotations by checking subsumption against the existing hierarchy. When a biologist annotates a gene product with a new function, the reasoner automatically infers all parent terms, ensuring consistency across the entire ontology. This is description logic reasoning at work in a real scientific knowledge base reportedly serving millions of queries per day.
4. OWL and RDF: Standards for the Semantic Web
Description logics supply the theoretical foundations, but to share ontologies across labs, hospitals, and databases, those foundations need a concrete file format and query protocol that every tool can agree on.
The W3C's Resource Description Framework (RDF) provides a concrete syntax for expressing knowledge as triples: (subject, predicate, object). Every entity and relationship is identified by a Uniform Resource Identifier (URI), making knowledge globally addressable. The triple \((\text{ex:aspirin}, \text{ex:inhibits}, \text{ex:COX-2})\) is an RDF statement. Collections of triples form an RDF graph.
RDF Schema (RDFS) adds vocabulary for class hierarchies (rdfs:subClassOf), property domains and ranges, and labels. OWL goes further, providing the full expressiveness of description logics: cardinality restrictions, disjointness, equivalence, property characteristics (transitivity, symmetry, functionality), and more. OWL comes in three profiles that trade expressiveness for computational complexity (see also Figure 3.1):
- OWL EL: polynomial-time reasoning, suitable for large biomedical ontologies (SNOMED CT, Gene Ontology).
- OWL QL: designed for query answering over large ABoxes, reducible to SQL.
- OWL DL: corresponds to the description logic \(\mathcal{SROIQ}\), decidable but 2NEXPTIME-complete.
Let us build a small scientific ontology using RDFLib and query it with SPARQL (SPARQL Protocol and RDF Query Language):
from rdflib import Graph, Namespace, Literal, RDF, RDFS, OWL
# Create namespaces
EX = Namespace("http://example.org/discovery/")
g = Graph()
g.bind("ex", EX)
# TBox: define classes
g.add((EX.Drug, RDF.type, OWL.Class))
g.add((EX.Protein, RDF.type, OWL.Class))
g.add((EX.Disease, RDF.type, OWL.Class))
g.add((EX.NSAID, RDF.type, OWL.Class))
g.add((EX.NSAID, RDFS.subClassOf, EX.Drug))
# Define properties
g.add((EX.inhibits, RDF.type, OWL.ObjectProperty))
g.add((EX.inhibits, RDFS.domain, EX.Drug))
g.add((EX.inhibits, RDFS.range, EX.Protein))
g.add((EX.treats, RDF.type, OWL.ObjectProperty))
g.add((EX.treats, RDFS.domain, EX.Drug))
g.add((EX.treats, RDFS.range, EX.Disease))
# ABox: individuals
for drug, cls in [("aspirin", EX.NSAID), ("ibuprofen", EX.NSAID)]:
g.add((EX[drug], RDF.type, cls))
g.add((EX[drug], RDFS.label, Literal(drug)))
g.add((EX.aspirin, EX.inhibits, EX.COX2))
g.add((EX.ibuprofen, EX.inhibits, EX.COX2))
g.add((EX.COX2, RDF.type, EX.Protein))
g.add((EX.COX2, RDFS.label, Literal("Cyclooxygenase-2")))
# SPARQL query: find all drugs that inhibit a protein
query = """
SELECT ?drug ?target WHERE {
?drug ex:inhibits ?target .
?drug a/rdfs:subClassOf* ex:Drug .
?target a ex:Protein .
}
"""
print("Drugs and their protein targets:")
for row in g.query(query, initNs={"ex": EX}):
print(f" {row.drug.split('/')[-1]} inhibits {row.target.split('/')[-1]}")
# Output:
# Drugs and their protein targets:
# aspirin inhibits COX2
# ibuprofen inhibits COX2
print(f"\nTotal triples in graph: {len(g)}")
# Output: Total triples in graph: 16
a/rdfs:subClassOf* syntax, which follows zero or more subclass links in a single pattern) to find all drugs that inhibit a protein.For serious ontology work, the owlready2 library provides Pythonic access to OWL ontologies with integrated reasoning via HermiT or Pellet, two automated OWL reasoners that check consistency, classify concepts, and infer implicit facts (as of 2024, the community-maintained fork Openllet has largely replaced the original Pellet, which is no longer actively developed). What took us 20 lines of RDFLib triple manipulation above becomes:
from owlready2 import get_ontology, Thing, ObjectProperty
onto = get_ontology("http://example.org/discovery.owl")
with onto:
class Drug(Thing): pass
class NSAID(Drug): pass
class Protein(Thing): pass
class inhibits(Drug >> Protein): pass
aspirin = NSAID("aspirin")
cox2 = Protein("COX2")
aspirin.inhibits.append(cox2)
# Run the HermiT reasoner
from owlready2 import sync_reasoner
sync_reasoner()
print(list(onto.search(type=Drug)))
# [discovery.aspirin] (classified via NSAID ⊑ Drug)
sync_reasoner() automatically classifies aspirin as a Drug through the NSAID subclass axiom.owlready2 handles OWL parsing, serialization, class hierarchy inference, and consistency checking. Line count reduction: roughly 3x for simple ontologies, 10x or more for complex ones with reasoning.
5. Semantic Networks and Frames
Before the Semantic Web standardized ontology languages, AI researchers used two related formalisms: semantic networks and frames. A semantic network is a labeled directed graph: nodes are concepts, edges carry typed relationships (IS-A, HAS-PART, CAUSES). Quillian (1968) introduced them for modeling associative memory; Collins and Loftus (1975) added spreading activation, a process in which triggering one node sends a signal along weighted edges to neighboring nodes, with signal strength decaying over distance, where triggering a node propagates signal to its neighbors through weighted edges.
Frames (Minsky, 1975) organize knowledge into structured records: a frame for "Enzyme" might have slots for substrate, product, optimal_pH, cofactor, each with type constraints, default values, and inheritance from parent frames. Frames are the precursor to object-oriented programming and to the slot-and-filler representations used in modern knowledge graphs.
In modern practice, semantic networks are typically implemented using the same graph infrastructure that powers knowledge graphs: property graph databases such as Neo4j or RDF triple stores. The conceptual vocabulary of semantic networks (typed nodes, labeled edges, inheritance via IS-A links) maps directly onto these tools, so rather than building a bespoke semantic network library, practitioners today encode network-style knowledge in RDF (as shown in Section 4 above) or in a property graph (as Section 3.2 demonstrates). What the historical formalisms contribute is the design intuition: knowledge is not a flat collection of facts but a structured network where meaning emerges from connections.
In 1969, McCarthy and Hayes posed the "frame problem": how do you represent what does not change when an action is performed? If a robot moves a beaker from shelf A to shelf B, we need to say that every other object stays where it was, that the beaker's color has not changed, that the lab is still at the same temperature, and so on for every property of every object. The frame problem is not merely a nuisance; it reveals something deep about the brittleness of purely logical representations. Modern approaches sidestep it with the closed-world assumption (the convention that any statement not present in the knowledge base is treated as false) or with learned representations that implicitly encode state.
Real-World Application: SNOMED CT in Clinical Decision Support
SNOMED CT, the Systematized Nomenclature of Medicine, is an OWL EL ontology containing over 350,000 clinical concepts used in electronic health records worldwide. Hospital systems such as Epic and Oracle Health (formerly Cerner, acquired by Oracle in 2022) use SNOMED's description logic axioms to power clinical decision support: when a physician enters a diagnosis, the reasoner traverses the subsumption hierarchy to automatically trigger relevant alerts, suggest laboratory tests, and flag contraindicated medications. The polynomial-time reasoning guarantee of OWL EL is what makes this feasible at the point of care, where queries typically must resolve in milliseconds across hundreds of thousands of concepts.
Research Frontier
The boundary between symbolic (logic, ontologies) and sub-symbolic (embeddings, neural networks) representations continues to dissolve. A major 2023 milestone is LEGO (Learning Embeddings for Gene Ontology) by Chen et al. (2023, Bioinformatics), which learns OWL-aware embeddings that preserve description logic subsumption structure while enabling neural similarity search over the full Gene Ontology. Unlike earlier ontology embedding methods that treated axioms as soft constraints, LEGO guarantees that if \(C \sqsubseteq D\) holds in the ontology, then the embedding geometry reflects this ordering, and the authors report strong protein function prediction results on the CAFA benchmark. More broadly, the neuro-symbolic AI program has matured: systems such as DeepStochLog (Winters et al., 2022) and NESI (Tsamoura et al., 2024) aim to scale probabilistic logic programming with neural predicates to knowledge bases with millions of facts, bringing the hybrid approach closer to practical use in real scientific discovery pipelines rather than toy demonstrations. Chapter 38 returns to this topic at production scale.
6. Choosing a Representation for Scientific Discovery
The formalisms in this section form a spectrum of expressiveness and tractability. The table below summarizes the trade-offs:
| Formalism | Expressiveness | Decidability | Typical Use in Discovery |
|---|---|---|---|
| Propositional Logic | Low (no variables) | NP-complete (SAT) | Configuration checking, constraint satisfaction |
| First-Order Logic | High | Semi-decidable | Mathematical proofs, theorem proving |
| Description Logic (\(\mathcal{ALC}\)) | Medium-high | EXPTIME | Biomedical ontologies, taxonomies |
| OWL EL | Medium | PTIME | Large-scale ontologies (SNOMED CT, GO) |
| RDF/RDFS | Low-medium | PTIME | Linked data, metadata, knowledge graphs |
| Semantic Networks | Informal | N/A (no standard inference) | Concept visualization, exploratory analysis |
In practice, a discovery AI system rarely uses a single formalism. The Discovery Workbench architecture (introduced in Chapter 6) combines an OWL ontology for the domain schema, a property graph for instance-level knowledge, and dense embeddings for similarity search. The next section shows how to build the graph layer.
Try It: Build and Query a Mini Drug Ontology
Put the formalisms from this section into practice by building a small biomedical ontology from scratch. You need only Python and pip install rdflib.
- Define your schema. Create an RDF graph with at least four classes (e.g., Drug, Disease, Protein, Pathway) and three object properties (e.g., inhibits, treats, participatesIn). Use
rdfs:subClassOfto set up a two-level class hierarchy (e.g., NSAID subclass of Drug, Kinase subclass of Protein). - Populate the ABox. Add at least six individuals spread across your classes and link them with the properties you defined. Include at least one chain of relationships (e.g., aspirin inhibits COX-2, COX-2 participatesIn inflammationPathway).
- Write three SPARQL queries. (a) A simple retrieval: find all drugs that inhibit any protein. (b) A path query using property paths: find all drugs connected to a pathway through an intermediate protein. (c) A negation query using FILTER NOT EXISTS: find diseases for which no drug in your graph has a
treatsrelationship. - Serialize and inspect. Save your graph to a Turtle (.ttl) file with
g.serialize("my_ontology.ttl", format="turtle"), open the file in a text editor, and verify that the triples match your intended schema. - Check consistency. Install
owlready2, load your Turtle file, runsync_reasoner(), and confirm that no inconsistencies are reported. Try deliberately adding a contradictory axiom (e.g., declare two classes disjoint and then assert an individual belonging to both) and observe the reasoner's error output.
Lab: Build and Reason Over a Miniature Scientific Ontology
Goal: Experience the difference between storing facts in a flat list and storing them in a formal ontology with automated reasoning.
Tools needed: Python 3.9+, pip install owlready2 rdflib (no other dependencies).
Procedure (25 minutes):
- (5 min) Using
owlready2, create an ontology with four classes:Compound,Enzyme,Pathway,Disease. Defineinhibits(Compound to Enzyme),participatesIn(Enzyme to Pathway), andassociatedWith(Pathway to Disease) as object properties. - (5 min) Populate the ontology with at least 8 individuals (3 compounds, 2 enzymes, 2 pathways, 1 disease) and connect them with the properties above so that at least one compound links to a disease through a two-hop chain.
- (5 min) Declare
CompoundandEnzymeas disjoint classes. Runsync_reasoner()and confirm no errors. Then deliberately assert that one of your compounds is also an instance ofEnzymeand observe the inconsistency error. - (5 min) Add a subclass
KinaseInhibitorunderCompoundwith an equivalent-class axiom:Compound & inhibits.some(Kinase)(whereKinaseis a subclass ofEnzyme). Create a compound that inhibits a Kinase individual, run the reasoner, and verify that the compound is automatically classified as aKinaseInhibitorwithout you asserting it. - (5 min) Export the ontology to OWL/XML with
onto.save(). Open the file and find the axioms the reasoner inferred.
What to vary: Try adding transitive property declarations to participatesIn and observe whether the reasoner can infer longer pathway chains. Compare reasoning time with 10 vs. 100 individuals.
What to observe: Note which class memberships the reasoner infers that you never explicitly asserted. Count how many inferred facts appear relative to the number of stated facts; this ratio illustrates the leverage that formal ontologies provide over flat knowledge stores.
Exercises
- Conceptual. Translate the following informal scientific claim into (a) propositional logic, (b) first-order logic, and (c) a description logic axiom: "All kinase inhibitors that cross the blood-brain barrier are potential treatments for glioblastoma."
- Coding. Using RDFLib, extend the ontology in Listing 3.3 to model a
treatsrelationship between drugs and diseases. Add at least three drug-disease pairs and write a SPARQL query that finds all diseases treatable by NSAIDs. - Analysis. The Gene Ontology uses OWL EL rather than OWL DL. Research and explain (in 2 to 3 paragraphs) what expressiveness GO sacrifices and why the polynomial-time reasoning guarantee matters at its scale (45,000+ terms, millions of annotations).
What's Next
Logic and ontologies give us the schema, the formal vocabulary in which scientific knowledge can be stated. But a vocabulary without data is an empty filing cabinet. In Section 3.2: Knowledge Graphs, we fill the cabinet: we build graphs of entities and relations, query them with Cypher and SPARQL, and then learn continuous representations (TransE, RotatE, ComplEx) that let us predict missing links, the first step toward computational hypothesis generation.
Bibliography
Foundational Papers
Quillian, M. R. (1968). Semantic Memory. In M. Minsky (Ed.), Semantic Information Processing. MIT Press. The original semantic network model of human associative memory.
Minsky, M. (1975). A Framework for Representing Knowledge. In P. H. Winston (Ed.), The Psychology of Computer Vision. McGraw-Hill. Introduced frames as structured knowledge representations with slots, defaults, and inheritance.
Baader, F., Horrocks, I., Lutz, C., & Sattler, U. (2017). An Introduction to Description Logic. Cambridge University Press. A modern, accessible introduction to description logics, their complexity, and their role in OWL.
Standards and Specifications
W3C. (2012). OWL 2 Web Ontology Language Overview. The official specification for OWL 2 and its profiles (EL, QL, RL, DL).
W3C. (2014). RDF 1.1 Concepts and Abstract Syntax. The data model underlying all Semantic Web technologies.
Tools & Libraries
RDFLib. RDFLib Documentation. Pure-Python library for working with RDF, SPARQL, and various serialization formats.
owlready2. owlready2 Documentation. Pythonic access to OWL ontologies with integrated HermiT and Pellet reasoners.
SymPy Logic Module. SymPy Logic Documentation. Symbolic logic, satisfiability checking, and Boolean algebra in Python.
Scientific Ontologies
The Gene Ontology Consortium. (2021). The Gene Ontology Resource: Enriching a GOld Mine. Nucleic Acids Research. The authoritative reference for GO, the most widely used ontology in biology.
Manhaeve, R., Dumancic, S., Kimmig, A., Demeester, T., & De Raedt, L. (2018). DeepProbLog: Neural Probabilistic Logic Programming. NeurIPS. Integrates neural networks into probabilistic logic programs for neuro-symbolic reasoning.