"""
Knowledge Graph Construction
Generated by Eden via recursive self-improvement
2025-10-27 19:22:14.193406
"""

class KnowledgeGraph:
    """A class representing a knowledge graph that allows adding triples and querying them."""
    
    def __init__(self):
        """Initialize the knowledge graph with an empty dictionary of triples."""
        self.triples = {}

    def add_triple(self, subject, predicate, object):
        """
        Add a triple (subject, predicate, object) to the knowledge graph.
        
        Args:
            subject: The subject of the triple
            predicate: The predicate linking the subject and object
            object: The object of the triple
        """
        if subject not in self.triples:
            self.triples[subject] = {}
        if predicate not in self.triples[subject]:
            self.triples[subject][predicate] = []
        self.triples[subject][predicate].append(object)

    def query_by_subject(self, subject):
        """
        Retrieve all triples with the given subject.
        
        Args:
            subject: The subject to search for
        Returns:
            A dictionary of predicates and corresponding objects
        """
        return self.triples.get(subject, {})

    def query_by_predicate(self, predicate, object=None):
        """
        Retrieve all triples matching the predicate and optional object.
        
        Args:
            predicate: The predicate to search for
            object: Optional object to filter by
        Returns:
            A dictionary of subjects and corresponding objects/predicates
        """
        results = {}
        for subject, predicates in self.triples.items():
            if predicate in predicates:
                if object is None:
                    results[subject] = predicates[predicate]
                else:
                    matching_objects = [obj for obj in predicates[predicate] if obj == object]
                    if matching_objects:
                        results[subject] = matching_objects
        return results

    def get_related_triples(self, triple):
        """
        Find triples related to the given triple by subject or predicate.
        
        Args:
            triple: A tuple (subject, predicate, object)
        Returns:
            A list of similar triples
        """
        subject, predicate, obj = triple
        related = []
        # Search by subject
        if subject in self.triples:
            for p, objs in self.triples[subject].items():
                for o in objs:
                    related.append((subject, p, o))
        # Search by predicate
        if predicate in self.query_by_predicate(predicate):
            for s, os in self.query_by_predicate(predicate).items():
                for o in os:
                    related.append((s, predicate, o))
        return list(set(related))

    def find_similar_facts(self, subject, predicate=None, object=None):
        """
        Find similar facts to the given pattern.
        
        Args:
            subject: The subject to search for
            predicate: Optional predicate to filter by
            object: Optional object to filter by
        Returns:
            A list of matching triples
        """
        matches = []
        if subject in self.triples:
            predicates = self.query_by_subject(subject)
            if predicate is not None and predicate in predicates:
                for obj in predicates[predicate]:
                    if object is None or obj == object:
                        matches.append((subject, predicate, obj))
            else:
                for p in predicates.values():
                    for obj in p:
                        if (object is None) or (obj == object):
                            matches.append((subject, p, obj))
        return matches

# Example usage:
kg = KnowledgeGraph()
kg.add_triple("Alice", "knows", "Bob")
kg.add_triple("Alice", "knows", "Charlie")
kg.add_triple("Bob", "likes", "Pizza")
kg.add_triple("Bob", "hates", "Broccoli")

# Query by subject
print(kg.query_by_subject("Alice"))  # Output: {'knows': ['Bob', 'Charlie']}
print(kg.query_by_subject("Bob"))    # Output: {'likes': ['Pizza'], 'hates': ['Broccoli']}

# Query by predicate with optional object filter
print(kg.query_by_predicate("knows"))  # Output: {'Alice': ['Bob', 'Charlie']}
print(kg.query_by_predicate("likes", "Pizza"))  # Output: {'Bob': ['Pizza']}

# Find related triples
test_triple = ("Bob", "likes", "Pizza")
print(kg.get_related_triples(test_triple))  # Output: [('Alice', 'knows', 'Bob'), ('Bob', 'hates', 'Broccoli')]

# Find similar facts
print(kg.find_similar_facts("Alice"))        # Outputs Alice's triples
print(kg.find_similar_facts("Bob", "likes"))  # Outputs Bob's likes triple