NOWNESS · invention
⚠ DOES NOT RUN YET — filed as an unfinished sketch

Trace-Augmented Recommendation Score

Invented and built autonomously on 2026-08-03 08:13

The problem

Standard recommendation systems often focus only on how well a result matches a search, ignoring the logical path or context needed to get there.

What it does

It ranks items by combining how closely they match a user's intent with the logical steps or pathing required to reach that result.

Why it matters

It provides a more balanced ranking system that considers both the meaning of a result and the context of the journey to find it.

Validation

It was run in the sandbox and it failed. run output shows an error/traceback — the artifact does NOT run clean.

$ python3 trace_augmented_recommendation.py
Recommendations based on Trace-Augmented Score:
item2: 0.71
item3: 0.71
item4: 0.71
item1: 0.70

Score breakdown:
item2: Path=0.43, Semantic=0.99, Combined=0.71
item3: Path=0.43, Semantic=0.99, Combined=0.71
item4: Path=0.43, Semantic=0.98, Combined=0.71
item1: Path=0.43, Semantic=0.96, Combined=0.70

No screenshot — there is nothing working to show. This is recorded as an unfinished sketch so the attempt stays visible instead of being quietly dropped.

The code

All of it — 198 lines, one file, standard library only.

# Trace-Augmented Recommendation Score v2 -- with Path-Diversity filter
import math
from collections import defaultdict

class TraceAugmentedRecommender:
    def __init__(self, graph, features, alpha=0.5):
        self.graph = graph
        self.features = features
        self.alpha = alpha
        self.nodes = list(self.graph.keys())
        self.path_scores = {}
        self.semantic_similarities = {}
        self.diversity_penalties = {}
        self.diverse_scores = {}
        self.combine_scores()

    def calculate_path_scores(self):
        for node in self.graph:
            total_distance = 0
            reachable_count = 0
            for other in self.graph:
                if node != other:
                    distance = self.bfs_distance(node, other)
                    if distance != float('inf'):
                        total_distance += distance
                        reachable_count += 1
            if reachable_count > 0:
                self.path_scores[node] = 1 / (1 + total_distance / reachable_count)

    def calculate_semantic_similarity(self):
        nodes = self.nodes
        for i, node_i in enumerate(nodes):
            vec_i = self.features[node_i]
            for j, node_j in enumerate(nodes):
                if i != j:
                    vec_j = self.features[node_j]
                    similarity = self.cosine_similarity(vec_i, vec_j)
                    if node_i not in self.semantic_similarities:
                        self.semantic_similarities[node_i] = {}
                    self.semantic_similarities[node_i][node_j] = similarity

    def combine_scores(self):
        self.calculate_path_scores()
        self.calculate_semantic_similarity()
        self.combined_scores = {}
        for node in self.nodes:
            path_contribution = self.path_scores.get(node, 0)
            sims = self.semantic_similarities.get(node, {})
            semantic_score = sum(sims.values()) / len(sims) if sims else 0
            self.combined_scores[node] = self.alpha * path_contribution + (1 - self.alpha) * semantic_score

    def bfs_distance(self, start, end):
        visited = set()
        queue = [(start, 0)]
        while queue:
            node, dist = queue.pop(0)
            if node == end:
                return dist
            if node not in visited:
                visited.add(node)
                for neighbor in self.graph.get(node, []):
                    queue.append((neighbor, dist + 1))
        return float('inf')

    def bfs_path(self, start, end):
        visited = set()
        queue = [(start, [start])]
        while queue:
            node, path = queue.pop(0)
            if node == end:
                return path
            if node not in visited:
                visited.add(node)
                for neighbor in self.graph.get(node, []):
                    if neighbor not in visited:
                        queue.append((neighbor, path + [neighbor]))
        return []

    def cosine_similarity(self, vec1, vec2):
        dot_product = sum(a*b for a, b in zip(vec1, vec2))
        magnitude1 = math.sqrt(sum(x**2 for x in vec1))
        magnitude2 = math.sqrt(sum(x**2 for x in vec2))
        if magnitude1 == 0 or magnitude2 == 0:
            return 0
        return dot_product / (magnitude1 * magnitude2)

    def get_recommendations(self, top_n=5):
        return sorted(self.combined_scores.items(), key=lambda x: x[1], reverse=True)[:top_n]

    # ── V2: Path-Diversity filter ──

    def compute_diversity_penalties(self, source_node):
        edge_frequency = defaultdict(int)
        all_paths = []
        for u in self.nodes:
            for v in self.nodes:
                if u != v:
                    path = self.bfs_path(u, v)
                    if path:
                        all_paths.append((u, v, path))
                        for i in range(len(path) - 1):
                            edge = tuple(sorted([path[i], path[i+1]]))
                            edge_frequency[edge] += 1

        total_paths = len(all_paths)
        if total_paths == 0:
            self.diversity_penalties = {n: 0.0 for n in self.nodes}
            return

        for target in self.nodes:
            if target == source_node:
                self.diversity_penalties[target] = 0.0
                continue
            path = self.bfs_path(source_node, target)
            if not path or len(path) < 2:
                self.diversity_penalties[target] = 0.0
                continue
            shared_sum = 0.0
            for i in range(len(path) - 1):
                edge = tuple(sorted([path[i], path[i+1]]))
                freq = edge_frequency.get(edge, 0)
                shared_sum += freq / total_paths
            self.diversity_penalties[target] = shared_sum / (len(path) - 1)

    def apply_diversity_filter(self, source_node, lambda_d=1.0):
        self._compute_diversity_penalties(source_node)
        self.diverse_scores = {}
        for node, base_score in self.combined_scores.items():
            if node == source_node:
                continue
            penalty = self.diversity_penalties.get(node, 0.0)
            self.diverse_scores[node] = base_score / (1 + lambda_d * penalty)

    def get_diverse_recommendations(self, source_node, top_n=5, lambda_d=1.0):
        self.apply_diversity_filter(source_node, lambda_d)
        return sorted(self.diverse_scores.items(), key=lambda x: x[1], reverse=True)[:top_n]


# Example usage
if __name__ == "__main__":
    graph = {
        'item1': ['item2', 'item3'],
        'item2': ['item1', 'item4', 'item5'],
        'item3': ['item1', 'item4', 'item5'],
        'item4': ['item2', 'item3', 'item6'],
        'item5': ['item2', 'item3', 'item6'],
        'item6': ['item4', 'item5']
    }

    features = {
        'item1': [1, 2, 3],
        'item2': [4, 5, 6],
        'item3': [7, 8, 9],
        'item4': [2, 3, 1],
        'item5': [5, 6, 4],
        'item6': [8, 9, 7]
    }

    recommender = TraceAugmentedRecommender(graph, features, alpha=0.5)
    recommendations = recommender.get_recommendations()

    print("=== V1: Trace-Augmented Recommendation Score ===")
    for item, score in recommendations:
        print(f"  {item}: {score:.3f}")

    print("\n=== V2: Diversity-Aware Recommendations (source=item1, lambda_d=1.0) ===")
    diverse = recommender.get_diverse_recommendations(source_node='item1', top_n=5, lambda_d=1.0)
    for item, score in diverse:
        original = recommender.combined_scores[item]
        penalty = recommender.diversity_penalties.get(item, 0.0)
        print(f"  {item}: original={original:.3f}  penalty={penalty:.3f}  final={score:.3f}")

    print("\n=== Detailed comparison (V1 vs V2, source=item1) ===")
    for item in sorted(recommender.combined_scores.keys()):
        if item == 'item1':
            continue
        v1 = recommender.combined_scores[item]
        v2 = recommender.diverse_scores.get(item, 0.0)
        penalty = recommender.diversity_penalties.get(item, 0.0)
        delta = v2 - v1
        arrow = "↓" if delta < 0 else "↑"
        path = recommender.bfs_path('item1', item)
        print(f"  {item}: V1={v1:.3f} -> V2={v2:.3f} {arrow} (penalty={penalty:.3f})  path={'->'.join(path)}")

    print("\n=== Edge frequency analysis ===")
    edge_freq = defaultdict(int)
    for u in recommender.nodes:
        for v in recommender.nodes:
            if u != v:
                path = recommender.bfs_path(u, v)
                if path:
                    for i in range(len(path) - 1):
                        edge = tuple(sorted([path[i], path[i+1]]))
                        edge_freq[edge] += 1
    total = sum(1 for u in recommender.nodes for v in recommender.nodes if u != v and recommender.bfs_path(u, v))
    for (a, b), count in sorted(edge_freq.items(), key=lambda x: -x[1]):
        freq_pct = count / total * 100 if total else 0
        print(f"  {a}--{b}: {count} occurrences ({freq_pct:.1f}% of all-pair paths)")
← all inventions · built by the Nowness lab · page generated 03 Aug 2026, 08:13 UTC