Source code for ontolearn.incomplete_kb

from owlready2 import *
import random
from typing import Set


[docs] def make_kb_incomplete_ass(kb_path, output_path, rate, seed): """ Makes the knowledge base incomplete by removing a certain percentage of statements (triples). Inputs: --------------- kb_path: Path to the input knowledge base. output_path: Path to save the modified (incomplete) knowledge base. rate: Percentage of statements to remove (0-100). seed: random seed for reproducibility. Output: --------------- Incomplete KB at level rate % """ random.seed(seed) # Load the ontology kb = get_ontology(kb_path).load() # Get all individuals in the ontology all_individuals = list(kb.individuals()) # Collect all triples (subject-predicate-object) related to the individuals all_triples = [] for individual in all_individuals: for prop in individual.get_properties(): for value in prop[individual]: all_triples.append((individual, prop, value)) # Calculate the number of triples to remove based on the rate num_to_remove = int(len(all_triples) * (rate / 100)) # Randomly select triples to remove triples_to_remove = random.sample(all_triples, num_to_remove) # Remove the selected triples for subject, predicate, obj in triples_to_remove: predicate[subject].remove(obj) # Save the modified ontology to a new file kb.save(file=output_path, format="rdfxml")
[docs] def make_kb_incomplete(kb_path, output_path, rate, seed)-> Set[str]: """ Makes the knowledge base incomplete by removing a certain percentage of individuals. Inputs: --------------- kb_path: Path to the input knowledge base. output_path: Path to save the modified (incomplete) knowledge base. rate: Percentage of individuals to remove (0-100). seed: random seed for reproducibility. Output: --------------- Incomplete KB at level rate % """ random.seed(seed) # Load the ontology kb = get_ontology(kb_path).load() # Get all individuals (instances) in the ABox all_individuals = list(kb.individuals()) # Calculate the number of individuals to remove based on the rate num_to_remove = int(len(all_individuals) * (rate / 100)) # Randomly select individuals to remove individuals_to_remove = random.sample(all_individuals, num_to_remove) # Remove the selected individuals for individual in individuals_to_remove: destroy_entity(individual) # Save the modified ontology to a new file kb.save(file=output_path, format="rdfxml")
[docs] def make_kb_inconsistent(kb_path, output_path, rate, seed, max_attempts=100): """ This function makes the knowledge base (KB) inconsistent by introducing incorrect statements. Parameters: kb_path (str): Path to the original OWL ontology file. output_path (str): Path to save the inconsistent ontology file. rate (float): Percentage of incorrect statements to introduce (0-100). seed (int): Seed for reproducibility. max_attempts (int): Maximum attempts to find a valid incorrect statement. """ # Set the random seed for reproducibility random.seed(seed) # Load the ontology onto = get_ontology(kb_path).load() # Get all individuals, classes, and properties all_individuals = list(onto.individuals()) all_classes = list(onto.classes()) all_object_properties = list(onto.object_properties()) all_data_properties = list(onto.data_properties()) def count_triples(): """Count the number of triples (statements) in the ontology.""" return len(list(onto.world.sparql(""" SELECT ?s ?p ?o WHERE { ?s ?p ?o . } """))) def generate_incorrect_class_assertion(individual): """Generate an incorrect class assertion by adding a disjoint or contradictory class.""" class_candidates = [cls for cls in all_classes if cls not in individual.is_a] if not class_candidates: return None selected_class = random.choice(class_candidates) individual.is_a.append(selected_class) print(f"Added incorrect class assertion: {individual} rdf:type {selected_class}") return f"Added incorrect class assertion: {individual} rdf:type {selected_class}" def generate_incorrect_object_property(individual): """Generate an incorrect object property assertion.""" prop = random.choice(all_object_properties) incorrect_object = random.choice(all_individuals) if incorrect_object not in prop[individual]: prop[individual].append(incorrect_object) print(f"Added incorrect object property assertion: {individual} {prop.name} {incorrect_object}") return f"Added incorrect object property assertion: {individual} {prop.name} {incorrect_object}" def generate_incorrect_data_property(individual): """Generate an incorrect data property assertion (if exist in the KB).""" if len(all_data_properties) != 0: prop = random.choice(all_data_properties) incorrect_value = "inconsistent_value" # Example of an incorrect data value if incorrect_value not in prop[individual]: setattr(individual, prop.name, incorrect_value) print(f"Added incorrect data property assertion: {individual} {prop.name} {incorrect_value}") return f"Added incorrect data property assertion: {individual} {prop.name} {incorrect_value}" def insert_incorrect_statements(): """Insert incorrect statements based on the specified rate.""" num_triples = count_triples() # Use the total number of triples in the KB num_incorrect = int(num_triples * (rate / 100)) incorrect_statements = [] for _ in range(num_incorrect): attempts = 0 while attempts < max_attempts: individual = random.choice(all_individuals) statement_type = random.choice(['class', 'object_property']) #could also add data properties later on if statement_type == 'class': result = generate_incorrect_class_assertion(individual) elif statement_type == 'object_property': result = generate_incorrect_object_property(individual) if result: incorrect_statements.append(result) break attempts += 1 return incorrect_statements # Insert incorrect statements inconsistencies = insert_incorrect_statements() # Save the modified ontology onto.save(file=output_path, format="rdfxml") # Return the list of inconsistencies added return inconsistencies