File size: 8,082 Bytes
63dd1f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
"""
ComplexityReasoner — Vitalis FSI

Assesses how hard a problem is BEFORE attempting it.
Allocates cognitive resources accordingly.
Prevents wasted cycles on problems beyond current capability.
Prevents under-allocation on trivial problems.

Complexity dimensions:
  - Structural: how many components does this problem have?
  - Novelty: how far is this from known patterns?
  - Depth: how many reasoning steps are required?
  - Ambiguity: how many valid interpretations exist?
"""
import numpy as np
import os
import json
import time
from vitalis_ide.math_core.kernel import VitalisKernel
from src.cognition.abstraction import AbstractionEngine
from src.hippocampus import Hippocampus


class ComplexityReasoner:
    # Complexity tier thresholds
    TRIVIAL_THRESHOLD    = 0.25
    SIMPLE_THRESHOLD     = 0.45
    MODERATE_THRESHOLD   = 0.65
    COMPLEX_THRESHOLD    = 0.80

    # Resource allocation per tier
    RESOURCE_MAP = {
        "TRIVIAL":  {"cycles": 1,  "abstraction_depth": 1, "analogy_search": False},
        "SIMPLE":   {"cycles": 2,  "abstraction_depth": 2, "analogy_search": False},
        "MODERATE": {"cycles": 4,  "abstraction_depth": 3, "analogy_search": True},
        "COMPLEX":  {"cycles": 8,  "abstraction_depth": 4, "analogy_search": True},
        "FRONTIER": {"cycles": 16, "abstraction_depth": 5, "analogy_search": True},
    }

    def __init__(self):
        self.kernel      = VitalisKernel()
        self.abstraction = AbstractionEngine()
        self.hippocampus = Hippocampus()
        self.path        = os.path.expanduser(
            "~/.vitalis_workspace/complexity_log.json"
        )
        self._log        = self._load_log()
        self._history    = []

    def _load_log(self) -> list:
        if os.path.exists(self.path):
            with open(self.path) as f:
                return json.load(f)
        return []

    def _save_log(self):
        os.makedirs(os.path.dirname(self.path), exist_ok=True)
        with open(self.path, "w") as f:
            json.dump(self._log[-1000:], f, indent=2)

    # ------------------------------------------------------------------
    # Core assessment
    # ------------------------------------------------------------------
    def assess(self, intent: str, context: dict = None) -> dict:
        """
        Full complexity assessment for an intent.
        Returns complexity score, tier, and resource allocation.
        """
        context = context or {}
        tokens  = intent.lower().split()
        vec     = self.kernel.vectorize_tokens(tokens, positional=False)

        # 1. Structural complexity — token count and unique concepts
        structural = self._structural_score(tokens)

        # 2. Novelty — distance from known patterns
        novelty = self._novelty_score(vec)

        # 3. Depth — estimated reasoning steps needed
        depth = self._depth_score(intent, tokens)

        # 4. Ambiguity — spread across abstraction space
        ambiguity = self._ambiguity_score(vec)

        # Weighted composite
        score = (
            structural * 0.20 +
            novelty    * 0.35 +
            depth      * 0.25 +
            ambiguity  * 0.20
        )
        score = float(np.clip(score, 0.0, 1.0))

        tier      = self._tier(score)
        resources = self.RESOURCE_MAP[tier].copy()

        result = {
            "intent":     intent,
            "score":      round(score, 4),
            "tier":       tier,
            "dimensions": {
                "structural": round(structural, 4),
                "novelty":    round(novelty,    4),
                "depth":      round(depth,      4),
                "ambiguity":  round(ambiguity,  4),
            },
            "resources":  resources,
            "timestamp":  time.time(),
        }

        self._log.append(result)
        self._history.append(score)
        if len(self._history) > 100:
            self._history.pop(0)
        self._save_log()
        return result

    # ------------------------------------------------------------------
    # Dimension calculators
    # ------------------------------------------------------------------
    def _structural_score(self, tokens: list) -> float:
        """More tokens + unique concepts = higher structural complexity."""
        n      = len(tokens)
        unique = len(set(tokens))
        # Normalise: 10 tokens = moderate complexity
        return float(np.clip((n / 10.0) * 0.5 + (unique / n if n > 0 else 0) * 0.5, 0, 1))

    def _novelty_score(self, vec: np.ndarray) -> float:
        """
        How far is this from anything the system has seen before?
        High novelty = far from known abstractions.
        """
        candidates = self.abstraction.query_abstractions(vec, top_k=1)
        if not candidates:
            return 1.0  # completely novel
        best_sim = candidates[0][0]
        return float(np.clip(1.0 - best_sim, 0.0, 1.0))

    def _depth_score(self, intent: str, tokens: list) -> float:
        """
        Estimate reasoning depth from linguistic markers.
        Multi-step intents score higher.
        """
        depth_markers = {
            "high":   ["analyze", "verify", "optimize", "refactor",
                       "debug", "compare", "evaluate", "synthesize"],
            "medium": ["write", "scaffold", "create", "build",
                       "generate", "implement"],
            "low":    ["run", "check", "show", "list", "get"],
        }
        for token in tokens:
            if token in depth_markers["high"]:
                return 0.8
            if token in depth_markers["medium"]:
                return 0.5
            if token in depth_markers["low"]:
                return 0.2
        # Connectives suggest multi-step reasoning
        if any(w in intent.lower() for w in ["and then", "after", "before", "while"]):
            return 0.9
        return 0.4  # default moderate

    def _ambiguity_score(self, vec: np.ndarray) -> float:
        """
        How spread are the top matches in abstraction space?
        High spread = high ambiguity = harder problem.
        """
        candidates = self.abstraction.query_abstractions(vec, top_k=3)
        if len(candidates) < 2:
            return 0.5
        scores = [c[0] for c in candidates]
        spread = float(np.std(scores))
        return float(np.clip(spread * 4.0, 0.0, 1.0))

    def _tier(self, score: float) -> str:
        if score < self.TRIVIAL_THRESHOLD:
            return "TRIVIAL"
        elif score < self.SIMPLE_THRESHOLD:
            return "SIMPLE"
        elif score < self.MODERATE_THRESHOLD:
            return "MODERATE"
        elif score < self.COMPLEX_THRESHOLD:
            return "COMPLEX"
        else:
            return "FRONTIER"

    # ------------------------------------------------------------------
    # Trend analysis
    # ------------------------------------------------------------------
    def complexity_trend(self) -> dict:
        """Is the system tackling harder or easier problems over time?"""
        if len(self._history) < 5:
            return {"status": "Insufficient data"}
        recent   = float(np.mean(self._history[-5:]))
        baseline = float(np.mean(self._history))
        trend    = "increasing" if recent > baseline + 0.05 else \
                   "decreasing" if recent < baseline - 0.05 else "stable"
        return {
            "recent_avg":  round(recent,   4),
            "baseline":    round(baseline, 4),
            "trend":       trend,
            "sample_size": len(self._history),
        }

    def report(self) -> dict:
        if not self._log:
            return {"status": "No assessments yet"}
        tiers = {}
        for e in self._log:
            t = e.get("tier", "UNKNOWN")
            tiers[t] = tiers.get(t, 0) + 1
        return {
            "total_assessments": len(self._log),
            "tier_distribution": tiers,
            "avg_complexity":    round(float(np.mean([e["score"] for e in self._log])), 4),
            "trend":             self.complexity_trend(),
        }