cortexm 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. context_m.py +17 -0
  2. cortexm/__init__.py +45 -0
  3. cortexm/accel.py +403 -0
  4. cortexm/api/__init__.py +0 -0
  5. cortexm/api/chaos.py +118 -0
  6. cortexm/api/memory.py +635 -0
  7. cortexm/bench/__init__.py +0 -0
  8. cortexm/bench/abilities.py +311 -0
  9. cortexm/bench/baselines.py +89 -0
  10. cortexm/bench/beam_loader.py +317 -0
  11. cortexm/bench/generator.py +376 -0
  12. cortexm/bench/harness.py +211 -0
  13. cortexm/bench/messy.py +218 -0
  14. cortexm/bench/micro.py +251 -0
  15. cortexm/bench/ood.py +443 -0
  16. cortexm/bench/run.py +137 -0
  17. cortexm/bridge/__init__.py +0 -0
  18. cortexm/bridge/dates.py +178 -0
  19. cortexm/bridge/decoders.py +204 -0
  20. cortexm/bridge/enrich.py +255 -0
  21. cortexm/bridge/extractor.py +316 -0
  22. cortexm/bridge/fallback.py +332 -0
  23. cortexm/bridge/onnx_runtime.py +158 -0
  24. cortexm/bridge/patterns.py +760 -0
  25. cortexm/bridge/ppr.py +104 -0
  26. cortexm/bridge/prefilter.py +188 -0
  27. cortexm/bridge/query_extract.py +420 -0
  28. cortexm/bridge/reader.py +1174 -0
  29. cortexm/bridge/rerank.py +204 -0
  30. cortexm/bridge/writer.py +492 -0
  31. cortexm/cli.py +295 -0
  32. cortexm/cognition/__init__.py +53 -0
  33. cortexm/cognition/abstraction.py +192 -0
  34. cortexm/cognition/analogy.py +159 -0
  35. cortexm/cognition/engine.py +204 -0
  36. cortexm/cognition/gaps.py +365 -0
  37. cortexm/cognition/scanner.py +204 -0
  38. cortexm/config.py +375 -0
  39. cortexm/cortexm.py +8 -0
  40. cortexm/enterprise/__init__.py +0 -0
  41. cortexm/enterprise/audit.py +178 -0
  42. cortexm/enterprise/governance.py +239 -0
  43. cortexm/errors.py +35 -0
  44. cortexm/features/__init__.py +0 -0
  45. cortexm/features/git.py +204 -0
  46. cortexm/features/prefetch.py +88 -0
  47. cortexm/features/zk.py +105 -0
  48. cortexm/federation/__init__.py +39 -0
  49. cortexm/federation/crdt.py +275 -0
  50. cortexm/federation/fabric.py +109 -0
  51. cortexm/federation/hlc.py +80 -0
  52. cortexm/federation/node.py +145 -0
  53. cortexm/federation/schema_report.py +73 -0
  54. cortexm/federation/transport.py +164 -0
  55. cortexm/index/__init__.py +19 -0
  56. cortexm/index/nsg.py +386 -0
  57. cortexm/mcp/__init__.py +0 -0
  58. cortexm/mcp/server.py +985 -0
  59. cortexm/metrics.py +62 -0
  60. cortexm/migrate/__init__.py +0 -0
  61. cortexm/migrate/importers.py +192 -0
  62. cortexm/provenance/__init__.py +78 -0
  63. cortexm/provenance/agent.py +214 -0
  64. cortexm/provenance/cose.py +201 -0
  65. cortexm/provenance/scitt.py +258 -0
  66. cortexm/provenance/vc.py +250 -0
  67. cortexm/security/__init__.py +0 -0
  68. cortexm/security/crypto.py +162 -0
  69. cortexm/security/hashes.py +140 -0
  70. cortexm/security/injection.py +149 -0
  71. cortexm/security/mind.py +154 -0
  72. cortexm/security/pii.py +265 -0
  73. cortexm/security/rbac.py +169 -0
  74. cortexm/security/sandbox.py +131 -0
  75. cortexm/security/zk_hamming.py +142 -0
  76. cortexm/security/zk_sql.py +485 -0
  77. cortexm/server/__init__.py +0 -0
  78. cortexm/server/metrics.py +88 -0
  79. cortexm/server/rest.py +936 -0
  80. cortexm/server/sparql.py +984 -0
  81. cortexm/text/__init__.py +0 -0
  82. cortexm/text/dissim.py +252 -0
  83. cortexm/text/embedder.py +155 -0
  84. cortexm/text/fuzzy.py +218 -0
  85. cortexm/text/idiolect.py +253 -0
  86. cortexm/text/labse.py +374 -0
  87. cortexm/text/tokenizer.py +79 -0
  88. cortexm/trace/__init__.py +0 -0
  89. cortexm/trace/blob_arena.py +277 -0
  90. cortexm/trace/consolidate.py +337 -0
  91. cortexm/trace/contradictions.py +69 -0
  92. cortexm/trace/dedup.py +114 -0
  93. cortexm/trace/edges.py +214 -0
  94. cortexm/trace/fact.py +121 -0
  95. cortexm/trace/fade.py +245 -0
  96. cortexm/trace/lifecycle.py +112 -0
  97. cortexm/trace/rebuild.py +173 -0
  98. cortexm/trace/rules.py +171 -0
  99. cortexm/trace/store.py +680 -0
  100. cortexm/trace/structural.py +183 -0
  101. cortexm/trace/tmt.py +335 -0
  102. cortexm/util.py +148 -0
  103. cortexm/vsa/__init__.py +0 -0
  104. cortexm/vsa/attribution.py +149 -0
  105. cortexm/vsa/cleanup.py +161 -0
  106. cortexm/vsa/codecs.py +397 -0
  107. cortexm/vsa/hologram_overlay.py +139 -0
  108. cortexm/vsa/index.py +163 -0
  109. cortexm/vsa/ops.py +149 -0
  110. cortexm/vsa/palace.py +446 -0
  111. cortexm/vsa/role_vectors.py +236 -0
  112. cortexm/vsa/slb.py +78 -0
  113. cortexm/vsa/tlsh_trie.py +137 -0
  114. cortexm/vsa/working_memory.py +249 -0
  115. cortexm-0.3.0.dist-info/METADATA +482 -0
  116. cortexm-0.3.0.dist-info/RECORD +120 -0
  117. cortexm-0.3.0.dist-info/WHEEL +5 -0
  118. cortexm-0.3.0.dist-info/entry_points.txt +2 -0
  119. cortexm-0.3.0.dist-info/licenses/LICENSE +190 -0
  120. cortexm-0.3.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,159 @@
1
+ """AnalogyDetector — finds structurally isomorphic domains via bipartite
2
+ relation mapping.
3
+
4
+ Fourth stage of the HMS cognition engine. Given two domains (sets of
5
+ entities + relations), find an isomorphism between their relation
6
+ graphs that maximizes structural overlap.
7
+
8
+ Classic example from Hofstadter/Mitchell's Copycat:
9
+ "The atom is to the solar system as the cell is to the city."
10
+ The detector finds that the (nucleus, orbits, electron) relation
11
+ triple in atoms has the same SHAPE as (sun, orbits, planet) in solar
12
+ systems and (mayor, governs, citizen) in cities.
13
+
14
+ For Context-M, we implement a simpler version: for each pair of
15
+ relations (r_a, r_b) with the same fanout pattern across subjects,
16
+ report an analogy. E.g.:
17
+ (father, male_parent) and (mother, female_parent) have identical
18
+ fanout → report analogy: father ≈ mother in shape (under sex-swap).
19
+
20
+ This is the seed for cross-domain transfer learning — once we know
21
+ the structural mapping, retrieval can find facts in domain A that
22
+ have analogues in domain B.
23
+
24
+ Hypotheses are emitted as derived facts:
25
+ (r_a, ANALOGOUS_TO, r_b) with confidence < 0.5
26
+
27
+ So a reader can traverse the analogy graph to find structural
28
+ equivalents across domains.
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import json
34
+ from collections import defaultdict
35
+ from dataclasses import dataclass, field
36
+ from datetime import datetime, timezone
37
+
38
+ from cortexm.cognition.scanner import ScanResult
39
+ from cortexm.trace.store import TraceStore
40
+ from cortexm.util import iso, new_id
41
+
42
+
43
+ # Edge kind used for analogy relations
44
+ ANALOGOUS_TO = "ANALOGOUS_TO"
45
+
46
+
47
+ @dataclass
48
+ class Analogy:
49
+ """A structural analogy between two relations."""
50
+ relation_a: str
51
+ relation_b: str
52
+ shared_fanout: int = 0 # how many subjects share the fanout pattern
53
+ overlap_score: float = 0.0 # jaccard similarity of subject sets
54
+ confidence: float = 0.0
55
+
56
+
57
+ @dataclass
58
+ class AnalogyResult:
59
+ analogies: list[Analogy] = field(default_factory=list)
60
+ edges_added: int = 0
61
+ duration_ms: float = 0.0
62
+
63
+
64
+ class AnalogyDetector:
65
+ """Finds structurally isomorphic relation pairs."""
66
+
67
+ def __init__(self, store: TraceStore,
68
+ min_overlap: float = 0.40,
69
+ min_support: int = 2) -> None:
70
+ self.store = store
71
+ self.min_overlap = min_overlap
72
+ self.min_support = min_support
73
+
74
+ def run(self, scan: ScanResult, *,
75
+ dry_run: bool = False,
76
+ commit_id: str | None = None,
77
+ user_id: str | None = None) -> AnalogyResult:
78
+ """Find analogies by comparing relation subject sets."""
79
+ import time
80
+ t0 = time.perf_counter()
81
+
82
+ # build a per-relation subject set
83
+ rel_subjects: dict[str, set[str]] = defaultdict(set)
84
+ where = "is_active=1 AND quarantined=0"
85
+ args: tuple = ()
86
+ if user_id is not None:
87
+ where += " AND user_id=?"
88
+ args = (user_id,)
89
+ rows = self.store.conn.execute(
90
+ f"SELECT relation, subject FROM facts WHERE {where}", args)
91
+ for r in rows:
92
+ rel_subjects[r[0]].add(r[1])
93
+
94
+ # for each pair of relations with sufficient support, compute
95
+ # the jaccard similarity of their subject sets
96
+ relations = list(rel_subjects.keys())
97
+ analogies: list[Analogy] = []
98
+ for i, ra in enumerate(relations):
99
+ sa = rel_subjects[ra]
100
+ if len(sa) < self.min_support:
101
+ continue
102
+ for rb in relations[i + 1:]:
103
+ sb = rel_subjects[rb]
104
+ if len(sb) < self.min_support:
105
+ continue
106
+ inter = len(sa & sb)
107
+ if inter < self.min_support:
108
+ continue
109
+ union = len(sa | sb)
110
+ if union == 0:
111
+ continue
112
+ jac = inter / union
113
+ if jac < self.min_overlap:
114
+ continue
115
+ analogies.append(Analogy(
116
+ relation_a=ra,
117
+ relation_b=rb,
118
+ shared_fanout=inter,
119
+ overlap_score=jac,
120
+ confidence=min(0.49, 0.10 + 0.20 * jac),
121
+ ))
122
+
123
+ # emit edges as derived facts: (ra, ANALOGOUS_TO, rb)
124
+ edges_added = 0
125
+ if not dry_run and analogies:
126
+ ts = iso(_now())
127
+ for a in analogies:
128
+ fid = new_id()
129
+ self.store.conn.execute(
130
+ "INSERT INTO facts "
131
+ "(id, subject, relation, value, valid_from, tx_from, "
132
+ " confidence, user_id, memory_type, is_derived, "
133
+ " is_active, birth_commit, provenance) "
134
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
135
+ (fid, a.relation_a, ANALOGOUS_TO, a.relation_b,
136
+ ts, ts, a.confidence,
137
+ user_id or "default", "long_term",
138
+ 1, 1, commit_id,
139
+ json.dumps({
140
+ "kind": "analogy",
141
+ "overlap_score": round(a.overlap_score, 4),
142
+ "shared_fanout": a.shared_fanout,
143
+ "generated_by": "cognition.analogy",
144
+ })))
145
+ edges_added += 1
146
+ if commit_id:
147
+ self.store.update_commit_n_facts(commit_id, edges_added)
148
+
149
+ return AnalogyResult(
150
+ analogies=analogies,
151
+ edges_added=edges_added,
152
+ duration_ms=(time.perf_counter() - t0) * 1000.0)
153
+
154
+
155
+ def _now():
156
+ return datetime.now(timezone.utc)
157
+
158
+
159
+ __all__ = ["AnalogyDetector", "Analogy", "AnalogyResult", "ANALOGOUS_TO"]
@@ -0,0 +1,204 @@
1
+ """CognitionEngine — orchestrates the 5-stage self-organization pipeline.
2
+
3
+ Triggered from `cortexm consolidate` (or `Memory.consolidate()`) so
4
+ the engine is deterministic, auditable, and never produces surprise
5
+ writes in the foreground path. Each pass:
6
+
7
+ 1. PatternScanner.run() — surface structural regularities
8
+ 2. AbstractionEngine.run() — build prototype categories
9
+ 3. GapDetector.run() — find missing relations
10
+ 4. HypothesisEngine.run() — propose fillers for gaps
11
+ 5. AnalogyDetector.run() — find cross-domain analogies
12
+
13
+ Output is committed as derived facts with confidence < 0.5 and tagged
14
+ is_derived=1, so they don't pollute the active fact count and don't
15
+ fire in retrieval unless explicitly promoted (via
16
+ `Memory.promote_hypothesis(fact_id)`).
17
+
18
+ The HYPOTHESIZED_BY edge kind is registered here so
19
+ `cortexm.trace.edges.ALL_KINDS` includes it. PROMOTED_FROM is the
20
+ inverse — when a hypothesis is confirmed by user input, the new fact
21
+ points back to its hypothesis origin.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from dataclasses import dataclass, field
27
+ from typing import Any
28
+
29
+ from cortexm.cognition.scanner import PatternScanner, ScanResult
30
+ from cortexm.cognition.abstraction import AbstractionEngine, AbstractionResult
31
+ from cortexm.cognition.gaps import (
32
+ GapDetector, HypothesisEngine, GapResult, HypothesisResult, HYPOTHESIZED_BY)
33
+ from cortexm.cognition.analogy import (
34
+ AnalogyDetector, AnalogyResult, ANALOGOUS_TO)
35
+ from cortexm.trace.store import TraceStore
36
+
37
+
38
+ # Edge kinds produced by the cognition engine. These are added to the
39
+ # trace.edges vocabulary. We register them lazily on first cognition
40
+ # pass so a Trace that never ran cognition doesn't see these kinds in
41
+ # queries (avoiding schema pollution).
42
+ PROMOTED_FROM = "PROMOTED_FROM" # user-confirmed fact -> hypothesis origin
43
+ ABSTRACTS = "ABSTRACTS" # abstraction -> member
44
+ INSTANTIATES = "INSTANTIATES" # member -> abstraction
45
+
46
+
47
+ @dataclass
48
+ class CognitionReport:
49
+ """Full report of one cognition pass."""
50
+ scan: dict = field(default_factory=dict)
51
+ abstraction: dict = field(default_factory=dict)
52
+ gaps: dict = field(default_factory=dict)
53
+ hypotheses: dict = field(default_factory=dict)
54
+ analogies: dict = field(default_factory=dict)
55
+ commit_id: str | None = None
56
+ dry_run: bool = False
57
+ total_derived_facts: int = 0
58
+ duration_ms: float = 0.0
59
+
60
+
61
+ class CognitionEngine:
62
+ """Orchestrates the 5-stage self-organization pipeline."""
63
+
64
+ def __init__(self, store: TraceStore, palace=None) -> None:
65
+ self.store = store
66
+ self.palace = palace
67
+ self.scanner = PatternScanner(store)
68
+ self.abstraction = AbstractionEngine(store)
69
+ self.gap_detector = GapDetector(store)
70
+ # pass palace for Hopfield cleanup path (currently not used
71
+ # by the simple strategies but the API accepts it)
72
+ self.hypothesis = HypothesisEngine(store, palace=palace)
73
+ self.analogy = AnalogyDetector(store)
74
+
75
+ def run(self, *,
76
+ dry_run: bool = False,
77
+ user_id: str | None = None) -> CognitionReport:
78
+ """Run the full 5-stage pipeline.
79
+
80
+ All writes happen inside a single commit if not dry_run.
81
+ Returns a CognitionReport with per-stage stats.
82
+ """
83
+ import time
84
+ t0 = time.perf_counter()
85
+
86
+ # if not dry_run, we expect the caller to have already started
87
+ # a batch + commit (consolidate() does this). We accept an
88
+ # optional commit_id from the caller via store._cognition_commit
89
+ # (stored as a session-level hint). If not present, we create
90
+ # our own commit.
91
+ own_commit = False
92
+ commit_id = None
93
+ if not dry_run:
94
+ try:
95
+ self.store.begin_batch()
96
+ commit_id = self.store.create_commit(
97
+ "cognition: scan+abstract+gap+hypothesis+analogy",
98
+ n_facts=0)
99
+ own_commit = True
100
+ except Exception:
101
+ # caller already in a batch — use their commit
102
+ commit_id = getattr(self.store, "_active_commit_id", None)
103
+
104
+ report = CognitionReport(dry_run=dry_run, commit_id=commit_id)
105
+
106
+ # 1. PatternScanner
107
+ scan = self.scanner.run(user_id=user_id)
108
+ report.scan = {
109
+ "patterns": len(scan.patterns),
110
+ "n_facts_scanned": scan.n_facts_scanned,
111
+ "n_relations": scan.n_relations,
112
+ "n_subjects": scan.n_subjects,
113
+ "n_values": scan.n_values,
114
+ "duration_ms": round(scan.duration_ms, 2),
115
+ "by_kind": _count_by_kind(scan.patterns),
116
+ }
117
+
118
+ # 2. AbstractionEngine
119
+ ab = self.abstraction.run(scan, dry_run=dry_run,
120
+ commit_id=commit_id, user_id=user_id)
121
+ report.abstraction = {
122
+ "abstractions": len(ab.abstractions),
123
+ "membership_edges_added": ab.membership_edges_added,
124
+ "duration_ms": round(ab.duration_ms, 2),
125
+ }
126
+
127
+ # 3. GapDetector
128
+ gaps = self.gap_detector.run(scan, user_id=user_id)
129
+ report.gaps = {
130
+ "gaps": len(gaps.gaps),
131
+ "n_subjects_compared": gaps.n_subjects_compared,
132
+ "duration_ms": round(gaps.duration_ms, 2),
133
+ "by_basis": _count_gaps_by_basis(gaps.gaps),
134
+ }
135
+
136
+ # 4. HypothesisEngine
137
+ hyp = self.hypothesis.run(gaps.gaps, dry_run=dry_run,
138
+ commit_id=commit_id, user_id=user_id)
139
+ report.hypotheses = {
140
+ "hypotheses": len(hyp.hypotheses),
141
+ "facts_added": hyp.facts_added,
142
+ "duration_ms": round(hyp.duration_ms, 2),
143
+ }
144
+
145
+ # 5. AnalogyDetector
146
+ ana = self.analogy.run(scan, dry_run=dry_run,
147
+ commit_id=commit_id, user_id=user_id)
148
+ report.analogies = {
149
+ "analogies": len(ana.analogies),
150
+ "edges_added": ana.edges_added,
151
+ "duration_ms": round(ana.duration_ms, 2),
152
+ }
153
+
154
+ report.total_derived_facts = (
155
+ ab.membership_edges_added
156
+ + hyp.facts_added
157
+ + ana.edges_added
158
+ )
159
+ report.duration_ms = (time.perf_counter() - t0) * 1000.0
160
+
161
+ if not dry_run and own_commit:
162
+ # update commit n_facts to the actual count we wrote
163
+ try:
164
+ self.store.update_commit_n_facts(
165
+ commit_id, report.total_derived_facts)
166
+ except Exception:
167
+ pass
168
+ try:
169
+ self.store.end_batch()
170
+ except Exception:
171
+ pass
172
+
173
+ return report
174
+
175
+
176
+ def run_cognition_pass(store: TraceStore, palace=None, *,
177
+ dry_run: bool = False,
178
+ user_id: str | None = None) -> CognitionReport:
179
+ """Convenience function: one-shot cognition pass on a TraceStore."""
180
+ return CognitionEngine(store, palace=palace).run(
181
+ dry_run=dry_run, user_id=user_id)
182
+
183
+
184
+ def _count_by_kind(patterns) -> dict[str, int]:
185
+ out: dict[str, int] = {}
186
+ for p in patterns:
187
+ out[p.kind] = out.get(p.kind, 0) + 1
188
+ return out
189
+
190
+
191
+ def _count_gaps_by_basis(gaps) -> dict[str, int]:
192
+ out: dict[str, int] = {}
193
+ for g in gaps:
194
+ out[g.basis] = out.get(g.basis, 0) + 1
195
+ return out
196
+
197
+
198
+ __all__ = [
199
+ "CognitionEngine", "CognitionReport",
200
+ "run_cognition_pass",
201
+ "HYPOTHESIZED_BY", "PROMOTED_FROM",
202
+ "ABSTRACTS", "INSTANTIATES",
203
+ "ANALOGOUS_TO",
204
+ ]