Python Ontology Tools

Working with ontologies using Python libraries

Python provides powerful libraries for working with ontologies programmatically. This guide covers the main tools available in this project.

Available Libraries

1. OWLready2

The most comprehensive Python library for OWL ontologies:

  • Load and manipulate OWL files
  • Create classes, properties, and individuals
  • Perform reasoning with external reasoners
  • SPARQL query support

2. RDFLib

Core library for RDF graph manipulation:

  • Multiple serialization formats (Turtle, RDF/XML, JSON-LD)
  • SPARQL endpoint queries
  • Graph operations and transformations
  • Custom namespace management

3. SymPy

Symbolic mathematics for logical reasoning:

  • First-order logic expressions
  • Quantifier manipulation
  • Logical inference and proofs
  • Integration with ontology reasoning

Getting Started

Installation

# Install core dependencies
pip install -r requirements-cpu.txt

# Or for GPU support  
pip install -r requirements-gpu.txt

Project Structure

from ontology_study.core import OntologyManager
from ontology_study.utils import load_config
from ontology_study import cli

Working with OWLready2

Basic Ontology Operations

from owlready2 import *

# Create or load ontology
onto = get_ontology("http://example.org/my_ontology")

# Define classes
class Person(Thing):
    namespace = onto

class Student(Person):
    pass

class Course(Thing):
    namespace = onto

# Define properties
class enrolledIn(ObjectProperty):
    domain = [Student]
    range = [Course]

class hasGPA(DataProperty, FunctionalProperty):
    domain = [Student] 
    range = [float]

# Create individuals
john = Student("john")
math101 = Course("math101")

# Set property values
john.enrolledIn = [math101]
john.hasGPA = 3.7

# Save ontology
onto.save(file="my_ontology.owl")

Advanced Features

# Complex class expressions
class GoodStudent(Student):
    equivalent_to = [Student & hasGPA.some(ConstrainedDatatype(float, min_inclusive=3.5))]

# Property characteristics
class hasParent(ObjectProperty, TransitiveProperty):
    pass

class hasSibling(ObjectProperty, SymmetricProperty):
    pass

# Reasoning with HermiT
with onto:
    sync_reasoner()

# Check for inconsistencies
print("Inconsistent classes:", list(onto.inconsistent_classes()))

RDFLib Integration

Graph Operations

from rdflib import Graph, Namespace, RDF, RDFS, OWL, Literal
from rdflib.namespace import FOAF

# Create graph
g = Graph()

# Define namespaces
EX = Namespace("http://example.org/")
g.bind("ex", EX)
g.bind("foaf", FOAF)

# Add triples
g.add((EX.john, RDF.type, FOAF.Person))
g.add((EX.john, FOAF.name, Literal("John Doe")))
g.add((EX.john, FOAF.age, Literal(25)))

# Serialize in different formats
print("Turtle format:")
print(g.serialize(format="turtle"))

print("\nJSON-LD format:")
print(g.serialize(format="json-ld"))

SPARQL Queries

# Query the graph
query = """
    SELECT ?person ?name ?age
    WHERE {
        ?person rdf:type foaf:Person .
        ?person foaf:name ?name .
        ?person foaf:age ?age .
        FILTER (?age > 18)
    }
"""

results = g.query(query)
for row in results:
    print(f"Person: {row.person}, Name: {row.name}, Age: {row.age}")

SymPy for Logical Reasoning

First-Order Logic

from sympy.logic.boolalg import *
from sympy import symbols

# Define predicates
P, Q, R = symbols('P Q R')

# Create logical expressions
expr1 = Implies(P, Q)  # P → Q
expr2 = Implies(Q, R)  # Q → R
expr3 = Implies(P, R)  # P → R (should follow from 1,2)

# Check logical equivalence
from sympy.logic.inference import satisfiable
premise = And(expr1, expr2)
conclusion = expr3

# Verify: if premise is true, is conclusion true?
combined = Implies(premise, conclusion)
print(f"Valid inference: {combined.is_tautology}")

Quantified Logic

from sympy.logic.predicates import *

# Define predicates with variables
x, y = symbols('x y')
Human = Predicate('Human')
Mortal = Predicate('Mortal')

# Universal quantification: ∀x (Human(x) → Mortal(x))
universal_stmt = ForAll(x, Implies(Human(x), Mortal(x)))

# Existential quantification: ∃x Human(x)
existential_stmt = Exists(x, Human(x))

print(f"Universal: {universal_stmt}")
print(f"Existential: {existential_stmt}")

Project Examples

1. Human-Mortal Classic Example

# From ontology_study/human_mortal.py
from owlready2 import *

def create_human_mortal_ontology():
    onto = get_ontology("http://example.org/human_mortal")
    
    with onto:
        class LivingThing(Thing): pass
        class Human(LivingThing): pass
        class Mortal(Thing): pass
        
        # All humans are mortal
        Human.is_a.append(Mortal)
        
        # Create individual
        socrates = Human("socrates")
    
    # Reasoning
    with onto:
        sync_reasoner()
    
    # Check if Socrates is mortal
    print(f"Socrates is mortal: {Mortal in socrates.is_a}")
    
    return onto

onto = create_human_mortal_ontology()

2. SPARQL Endpoint Integration

# From ontology_study/sparql_endpoint.py
from SPARQLWrapper import SPARQLWrapper, JSON

def query_dbpedia(query_string):
    sparql = SPARQLWrapper("http://dbpedia.org/sparql")
    sparql.setQuery(query_string)
    sparql.setReturnFormat(JSON)
    
    results = sparql.query().convert()
    return results["results"]["bindings"]

# Example: Find famous scientists
query = """
    SELECT ?scientist ?name WHERE {
        ?scientist dbo:occupation dbr:Scientist .
        ?scientist foaf:name ?name .
    }
    LIMIT 10
"""

scientists = query_dbpedia(query)
for scientist in scientists:
    print(f"{scientist['name']['value']}")

CLI Interface

The project includes a rich command-line interface:

# Navigate to Python directory
cd python/ontology_study/

# Show available commands
python cli.py --help

# Run specific examples
python cli.py human-mortal
python cli.py owlready2-demo  
python cli.py sparql-query
python cli.py quantifiers-study

CLI Features

  • Rich formatting: Colored output and tables
  • Interactive prompts: Guided ontology creation
  • Progress tracking: For long-running operations
  • Export options: Multiple output formats

Integration with Protégé

Export from Python to Protégé

# Create ontology in Python
onto = get_ontology("http://example.org/my_ontology")
# ... define classes, properties, individuals ...

# Save for Protégé
onto.save(file="for_protege.owl", format="rdfxml")

# Open in Protégé Desktop
# File → Open → for_protege.owl

Import from Protégé to Python

# Load ontology created in Protégé
onto = get_ontology("file://path/to/protege_ontology.owl").load()

# Access Protégé-defined entities  
for cls in onto.classes():
    print(f"Class: {cls.name}")
    
for prop in onto.object_properties():
    print(f"Object Property: {prop.name}")
    
for individual in onto.individuals():
    print(f"Individual: {individual.name}")

Best Practices

1. Code Organization

# Recommended project structure
ontology_study/
├── core/
│   ├── __init__.py
│   ├── ontology_manager.py
│   └── reasoner.py
├── utils/
│   ├── __init__.py
│   ├── config.py
│   └── helpers.py
├── examples/
│   ├── human_mortal.py
│   └── pizza_ontology.py
└── cli.py

2. Configuration Management

# utils/config.py
import yaml

def load_config(config_file="config.yml"):
    with open(config_file, 'r') as f:
        return yaml.safe_load(f)

# Configuration file example
config = {
    'reasoner': 'hermit',
    'output_format': 'turtle', 
    'sparql_endpoint': 'http://localhost:7200/repositories/test',
    'namespaces': {
        'ex': 'http://example.org/',
        'foaf': 'http://xmlns.com/foaf/0.1/'
    }
}

3. Error Handling

from owlready2 import OwlReadyError

def safe_ontology_operation(ontology_file):
    try:
        onto = get_ontology(ontology_file).load()
        sync_reasoner()
        return onto
    except OwlReadyError as e:
        print(f"OWL error: {e}")
        return None
    except Exception as e:
        print(f"Unexpected error: {e}")
        return None

Performance Tips

1. Large Ontologies

# For large ontologies, use streaming
def process_large_ontology(file_path):
    # Don't load entire ontology into memory
    onto = get_ontology(file_path)
    
    # Process in chunks
    for i, cls in enumerate(onto.classes()):
        if i % 1000 == 0:
            print(f"Processed {i} classes")
        # Process class...

2. Reasoning Optimization

# Use faster reasoners for development
set_default_reasoner(HermiT)  # Slow but complete
# set_default_reasoner(Pellet)  # Faster alternative

# Disable reasoning for bulk operations
with onto:
    # Create many individuals without reasoning
    for i in range(1000):
        Person(f"person_{i}")
    
    # Reason once at the end
    sync_reasoner()

Next Steps

  1. Explore Examples: Run all CLI examples to see features
  2. Build Custom Tools: Extend the CLI with your own commands
  3. Integration: Connect Python tools with Protégé workflows
  4. Advanced Reasoning: Experiment with different reasoners
  5. SPARQL Mastery: Learn complex query patterns
  6. Performance: Optimize for your specific use cases

Additional Resources