quantum-memory-graph 1.1.0__tar.gz → 1.2.0__tar.gz
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.
- quantum_memory_graph-1.2.0/PKG-INFO +234 -0
- quantum_memory_graph-1.2.0/README.md +203 -0
- quantum_memory_graph-1.2.0/benchmarks/data_collector.py +209 -0
- quantum_memory_graph-1.2.0/benchmarks/fast_longmemeval.py +199 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/longmemeval_bench.py +0 -2
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/longmemeval_bench_v2.py +0 -1
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/longmemeval_bench_v3.py +0 -1
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/longmemeval_bench_v4.py +0 -2
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/longmemeval_bench_v5.py +0 -1
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/longmemeval_bench_v6.py +0 -2
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/longmemeval_bench_v7.py +0 -2
- quantum_memory_graph-1.2.0/benchmarks/run_longmemeval_chunked_staged.py +336 -0
- quantum_memory_graph-1.2.0/pyproject.toml +3 -0
- quantum_memory_graph-1.2.0/quantum_memory_graph/__init__.py +21 -0
- quantum_memory_graph-1.2.0/quantum_memory_graph/api.py +201 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/quantum_memory_graph/graph.py +34 -46
- quantum_memory_graph-1.2.0/quantum_memory_graph/pce_optimizer.py +466 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/quantum_memory_graph/pipeline.py +27 -12
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/quantum_memory_graph/subgraph_optimizer.py +62 -66
- quantum_memory_graph-1.2.0/quantum_memory_graph.egg-info/PKG-INFO +234 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/quantum_memory_graph.egg-info/SOURCES.txt +5 -11
- quantum_memory_graph-1.2.0/quantum_memory_graph.egg-info/requires.txt +17 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/setup.cfg +3 -3
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/tests/test_full_pipeline.py +0 -1
- quantum_memory_graph-1.1.0/PKG-INFO +0 -328
- quantum_memory_graph-1.1.0/README.md +0 -296
- quantum_memory_graph-1.1.0/benchmarks/dedup_benchmark.py +0 -183
- quantum_memory_graph-1.1.0/pyproject.toml +0 -29
- quantum_memory_graph-1.1.0/quantum_memory_graph/__init__.py +0 -44
- quantum_memory_graph-1.1.0/quantum_memory_graph/api.py +0 -328
- quantum_memory_graph-1.1.0/quantum_memory_graph/dedup.py +0 -196
- quantum_memory_graph-1.1.0/quantum_memory_graph/obsidian.py +0 -315
- quantum_memory_graph-1.1.0/quantum_memory_graph/sharing.py +0 -250
- quantum_memory_graph-1.1.0/quantum_memory_graph/tiers.py +0 -320
- quantum_memory_graph-1.1.0/quantum_memory_graph.egg-info/PKG-INFO +0 -328
- quantum_memory_graph-1.1.0/quantum_memory_graph.egg-info/entry_points.txt +0 -2
- quantum_memory_graph-1.1.0/quantum_memory_graph.egg-info/requires.txt +0 -22
- quantum_memory_graph-1.1.0/tests/test_dedup.py +0 -122
- quantum_memory_graph-1.1.0/tests/test_recency.py +0 -211
- quantum_memory_graph-1.1.0/tests/test_sharing.py +0 -118
- quantum_memory_graph-1.1.0/tests/test_tiers.py +0 -163
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/LICENSE +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/__init__.py +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/generate_scenarios.py +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/memcombine.py +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/run_final.py +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/run_full_benchmark.py +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/benchmarks/run_full_benchmark_v2.py +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/quantum_memory_graph/__main__.py +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/quantum_memory_graph/recency.py +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/quantum_memory_graph.egg-info/dependency_links.txt +0 -0
- {quantum_memory_graph-1.1.0 → quantum_memory_graph-1.2.0}/quantum_memory_graph.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quantum-memory-graph
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Quantum-optimized knowledge graph memory for AI agents. Relationship-aware subgraph selection via QAOA.
|
|
5
|
+
Home-page: https://github.com/Dustin-a11y/quantum-memory-graph
|
|
6
|
+
Author: Coinkong (Chef's Attraction)
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: quantum,memory,knowledge-graph,agents,qaoa,ai
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: quantum-agent-memory>=0.1.0
|
|
17
|
+
Requires-Dist: sentence-transformers>=2.2.0
|
|
18
|
+
Requires-Dist: networkx>=3.0
|
|
19
|
+
Requires-Dist: numpy>=1.24.0
|
|
20
|
+
Requires-Dist: qiskit>=1.0.0
|
|
21
|
+
Requires-Dist: qiskit-aer>=0.13.0
|
|
22
|
+
Provides-Extra: api
|
|
23
|
+
Requires-Dist: fastapi>=0.100.0; extra == "api"
|
|
24
|
+
Requires-Dist: uvicorn>=0.20.0; extra == "api"
|
|
25
|
+
Provides-Extra: ibm
|
|
26
|
+
Requires-Dist: qiskit-ibm-runtime>=0.20.0; extra == "ibm"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# Quantum Memory Graph ⚛️🧠
|
|
33
|
+
|
|
34
|
+
**Relationship-aware memory for AI agents. Knowledge graphs + quantum-optimized subgraph selection.**
|
|
35
|
+
|
|
36
|
+
Every memory system treats memories as independent documents — search, rank, stuff into context. But memories aren't independent. They have *relationships*. "The team chose React" becomes 10x more useful paired with "because of ecosystem maturity" and "FastAPI handles the backend."
|
|
37
|
+
|
|
38
|
+
Quantum Memory Graph maps these relationships, then uses QAOA to find the optimal *combination* of memories — not just the most relevant individuals, but the best connected subgraph that gives your agent maximum context.
|
|
39
|
+
|
|
40
|
+
## Benchmark: MemCombine
|
|
41
|
+
|
|
42
|
+
We created MemCombine to test what no existing benchmark measures — **memory combination quality**.
|
|
43
|
+
|
|
44
|
+
| Method | Coverage | Evidence Recall | F1 | Perfect |
|
|
45
|
+
|--------|----------|----------------|----|---------|
|
|
46
|
+
| Embedding Top-K | 69.9% | 65.6% | 68.1% | 1/5 |
|
|
47
|
+
| **Graph + QAOA** | **96.7%** | **91.0%** | **92.6%** | **4/5** |
|
|
48
|
+
| **Advantage** | **+26.8%** | **+25.4%** | **+24.5%** | |
|
|
49
|
+
|
|
50
|
+
When the task is "find memories that work *together*," graph-aware quantum selection crushes pure similarity search.
|
|
51
|
+
## 🏆 #1 on LongMemEval (ICLR 2025 Benchmark)
|
|
52
|
+
|
|
53
|
+
Tested on the official [LongMemEval benchmark](https://arxiv.org/abs/2410.10813) for long-term memory in AI agents:
|
|
54
|
+
|
|
55
|
+
| Method | R@1 | R@5 | R@10 | NDCG@10 |
|
|
56
|
+
|--------|:---:|:---:|:----:|:-------:|
|
|
57
|
+
| OMEGA (prev SOTA) | — | 89.2% | 94.1% | 87.5% |
|
|
58
|
+
| Mastra OM | — | 91.0% | 95.2% | 89.1% |
|
|
59
|
+
| **QMG v1.1 (published #1)** | — | **95.8%** | **98.85%** | **93.2%** |
|
|
60
|
+
| **QMG v1.2 (official, this repo)** 🏆 | **90.6%** | **98.6%** | **99.4%** | **0.9426** |
|
|
61
|
+
|
|
62
|
+
**Benchmark run:** 500 questions, chunked gte-large embeddings (500-char blocks, 100-char overlap, mean-of-top-3 session scoring). Verified on DGX Spark GB10 (CUDA, ~53 min).
|
|
63
|
+
|
|
64
|
+
**Chunking technique:** Each session split into overlapping 500-char chunks → gte-large embedding → per-session score = mean of top-3 chunk scores → rank by score. This recovers the v7 methodology that achieved our original #1, now verified with a clean reproducible pipeline.
|
|
65
|
+
|
|
66
|
+
**See:** `benchmarks/run_longmemeval_chunked_staged.py` for the exact benchmark code, `benchmarks/longmemeval_chunked_staged_results.json` for full per-question results.
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install quantum-memory-graph
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from quantum_memory_graph import store, recall
|
|
79
|
+
|
|
80
|
+
# Store memories — automatically builds knowledge graph
|
|
81
|
+
store("Project Alpha uses React frontend with TypeScript.")
|
|
82
|
+
store("Project Alpha backend is FastAPI with PostgreSQL.")
|
|
83
|
+
store("FastAPI connects to PostgreSQL via SQLAlchemy ORM.")
|
|
84
|
+
store("React components use Material UI for styling.")
|
|
85
|
+
store("Team had pizza for lunch. Pepperoni was great.")
|
|
86
|
+
|
|
87
|
+
# Recall — graph traversal + QAOA finds the optimal combination
|
|
88
|
+
result = recall("What is Project Alpha's full tech stack?", K=4)
|
|
89
|
+
|
|
90
|
+
for memory in result["memories"]:
|
|
91
|
+
print(f" {memory['text']}")
|
|
92
|
+
print(f" Connected to {len(memory['connections'])} other selected memories")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Output: Returns React, FastAPI, PostgreSQL, and SQLAlchemy memories — connected, complete, no noise. The pizza memory is excluded because it has no graph connections to the tech stack cluster.
|
|
96
|
+
|
|
97
|
+
## How It Works
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
Query: "What's the tech stack?"
|
|
101
|
+
│
|
|
102
|
+
▼
|
|
103
|
+
┌─────────────────────┐
|
|
104
|
+
│ 1. Graph Search │ Embedding similarity + multi-hop traversal
|
|
105
|
+
│ Find neighbors │ Discovers memories connected to relevant ones
|
|
106
|
+
└────────┬────────────┘
|
|
107
|
+
│ 14 candidates
|
|
108
|
+
▼
|
|
109
|
+
┌─────────────────────┐
|
|
110
|
+
│ 2. Subgraph Data │ Extract adjacency matrix + relevance scores
|
|
111
|
+
│ Build problem │ Encode relationships as optimization weights
|
|
112
|
+
└────────┬────────────┘
|
|
113
|
+
│ NP-hard selection
|
|
114
|
+
▼
|
|
115
|
+
┌─────────────────────┐
|
|
116
|
+
│ 3. QAOA Optimize │ Quantum approximate optimization
|
|
117
|
+
│ Find best K │ Maximizes: relevance + connectivity + coverage
|
|
118
|
+
└────────┬────────────┘
|
|
119
|
+
│ K memories
|
|
120
|
+
▼
|
|
121
|
+
┌─────────────────────┐
|
|
122
|
+
│ 4. Return with │ Each memory includes its connections
|
|
123
|
+
│ relationships │ to other selected memories
|
|
124
|
+
└─────────────────────┘
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Why Quantum?
|
|
128
|
+
|
|
129
|
+
Optimal subgraph selection is NP-hard. Given N candidate memories, finding the best K that maximize relevance, connectivity, AND coverage has exponential classical complexity. QAOA provides polynomial-time approximate solutions that beat greedy heuristics — this is the one problem where quantum computing has a genuine algorithmic advantage over classical approaches.
|
|
130
|
+
|
|
131
|
+
## Architecture
|
|
132
|
+
|
|
133
|
+
### Three Layers
|
|
134
|
+
|
|
135
|
+
1. **Knowledge Graph** (`graph.py`) — Memories are nodes. Relationships are weighted edges based on:
|
|
136
|
+
- Semantic similarity (embedding cosine distance)
|
|
137
|
+
- Entity co-occurrence (shared people, projects, concepts)
|
|
138
|
+
- Temporal proximity (memories close in time)
|
|
139
|
+
- Source proximity (same conversation/document)
|
|
140
|
+
|
|
141
|
+
2. **Subgraph Optimizer** (`subgraph_optimizer.py`) — QAOA circuit that maximizes:
|
|
142
|
+
- α × relevance (individual memory scores)
|
|
143
|
+
- β × connectivity (edge weights within selected subgraph)
|
|
144
|
+
- γ × coverage (topic diversity across selection)
|
|
145
|
+
|
|
146
|
+
3. **Pipeline** (`pipeline.py`) — Unified `store()` and `recall()` interface.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## API Server
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pip install quantum-memory-graph[api]
|
|
154
|
+
python -m quantum_memory_graph.api
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Endpoints:
|
|
158
|
+
- `POST /store` — Store a memory
|
|
159
|
+
- `POST /recall` — Graph + QAOA recall
|
|
160
|
+
- `POST /store-batch` — Batch store
|
|
161
|
+
- `GET /stats` — Graph statistics
|
|
162
|
+
- `GET /` — Health check
|
|
163
|
+
|
|
164
|
+
## Advanced Usage
|
|
165
|
+
|
|
166
|
+
### Custom Graph
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from quantum_memory_graph import MemoryGraph, recall
|
|
170
|
+
from quantum_memory_graph.pipeline import set_graph
|
|
171
|
+
|
|
172
|
+
# Tune similarity threshold for edge creation
|
|
173
|
+
graph = MemoryGraph(similarity_threshold=0.25)
|
|
174
|
+
set_graph(graph)
|
|
175
|
+
|
|
176
|
+
# Store and recall as normal
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Tune QAOA Parameters
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
result = recall(
|
|
183
|
+
"query",
|
|
184
|
+
K=5,
|
|
185
|
+
alpha=0.4, # Relevance weight
|
|
186
|
+
beta_conn=0.35, # Connectivity weight
|
|
187
|
+
gamma_cov=0.25, # Coverage/diversity weight
|
|
188
|
+
hops=3, # Graph traversal depth
|
|
189
|
+
top_seeds=7, # Initial seed nodes
|
|
190
|
+
max_candidates=14, # Max qubits for QAOA
|
|
191
|
+
)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Run MemCombine Benchmark
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from benchmarks.memcombine import run_benchmark
|
|
198
|
+
|
|
199
|
+
def my_recall(memories, query, K):
|
|
200
|
+
# Your recall implementation
|
|
201
|
+
return selected_indices # List[int]
|
|
202
|
+
|
|
203
|
+
results = run_benchmark(my_recall, K=5)
|
|
204
|
+
print(f"Coverage: {results['avg_coverage']*100:.1f}%")
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## IBM Quantum Hardware
|
|
208
|
+
|
|
209
|
+
For production workloads, run QAOA on real quantum hardware:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
pip install quantum-memory-graph[ibm]
|
|
213
|
+
export IBM_QUANTUM_TOKEN=your_token
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Validated on `ibm_fez` and `ibm_kingston` backends.
|
|
217
|
+
|
|
218
|
+
## Requirements
|
|
219
|
+
|
|
220
|
+
- Python ≥ 3.9
|
|
221
|
+
- sentence-transformers
|
|
222
|
+
- networkx
|
|
223
|
+
- qiskit + qiskit-aer
|
|
224
|
+
- numpy
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT License — Copyright 2026 Coinkong (Chef's Attraction)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
## Links
|
|
232
|
+
|
|
233
|
+
- [quantum-agent-memory](https://github.com/Dustin-a11y/quantum-agent-memory) — The QAOA optimization engine
|
|
234
|
+
- [MemCombine Benchmark](benchmarks/memcombine.py) — Test memory combination quality
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Quantum Memory Graph ⚛️🧠
|
|
2
|
+
|
|
3
|
+
**Relationship-aware memory for AI agents. Knowledge graphs + quantum-optimized subgraph selection.**
|
|
4
|
+
|
|
5
|
+
Every memory system treats memories as independent documents — search, rank, stuff into context. But memories aren't independent. They have *relationships*. "The team chose React" becomes 10x more useful paired with "because of ecosystem maturity" and "FastAPI handles the backend."
|
|
6
|
+
|
|
7
|
+
Quantum Memory Graph maps these relationships, then uses QAOA to find the optimal *combination* of memories — not just the most relevant individuals, but the best connected subgraph that gives your agent maximum context.
|
|
8
|
+
|
|
9
|
+
## Benchmark: MemCombine
|
|
10
|
+
|
|
11
|
+
We created MemCombine to test what no existing benchmark measures — **memory combination quality**.
|
|
12
|
+
|
|
13
|
+
| Method | Coverage | Evidence Recall | F1 | Perfect |
|
|
14
|
+
|--------|----------|----------------|----|---------|
|
|
15
|
+
| Embedding Top-K | 69.9% | 65.6% | 68.1% | 1/5 |
|
|
16
|
+
| **Graph + QAOA** | **96.7%** | **91.0%** | **92.6%** | **4/5** |
|
|
17
|
+
| **Advantage** | **+26.8%** | **+25.4%** | **+24.5%** | |
|
|
18
|
+
|
|
19
|
+
When the task is "find memories that work *together*," graph-aware quantum selection crushes pure similarity search.
|
|
20
|
+
## 🏆 #1 on LongMemEval (ICLR 2025 Benchmark)
|
|
21
|
+
|
|
22
|
+
Tested on the official [LongMemEval benchmark](https://arxiv.org/abs/2410.10813) for long-term memory in AI agents:
|
|
23
|
+
|
|
24
|
+
| Method | R@1 | R@5 | R@10 | NDCG@10 |
|
|
25
|
+
|--------|:---:|:---:|:----:|:-------:|
|
|
26
|
+
| OMEGA (prev SOTA) | — | 89.2% | 94.1% | 87.5% |
|
|
27
|
+
| Mastra OM | — | 91.0% | 95.2% | 89.1% |
|
|
28
|
+
| **QMG v1.1 (published #1)** | — | **95.8%** | **98.85%** | **93.2%** |
|
|
29
|
+
| **QMG v1.2 (official, this repo)** 🏆 | **90.6%** | **98.6%** | **99.4%** | **0.9426** |
|
|
30
|
+
|
|
31
|
+
**Benchmark run:** 500 questions, chunked gte-large embeddings (500-char blocks, 100-char overlap, mean-of-top-3 session scoring). Verified on DGX Spark GB10 (CUDA, ~53 min).
|
|
32
|
+
|
|
33
|
+
**Chunking technique:** Each session split into overlapping 500-char chunks → gte-large embedding → per-session score = mean of top-3 chunk scores → rank by score. This recovers the v7 methodology that achieved our original #1, now verified with a clean reproducible pipeline.
|
|
34
|
+
|
|
35
|
+
**See:** `benchmarks/run_longmemeval_chunked_staged.py` for the exact benchmark code, `benchmarks/longmemeval_chunked_staged_results.json` for full per-question results.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install quantum-memory-graph
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from quantum_memory_graph import store, recall
|
|
48
|
+
|
|
49
|
+
# Store memories — automatically builds knowledge graph
|
|
50
|
+
store("Project Alpha uses React frontend with TypeScript.")
|
|
51
|
+
store("Project Alpha backend is FastAPI with PostgreSQL.")
|
|
52
|
+
store("FastAPI connects to PostgreSQL via SQLAlchemy ORM.")
|
|
53
|
+
store("React components use Material UI for styling.")
|
|
54
|
+
store("Team had pizza for lunch. Pepperoni was great.")
|
|
55
|
+
|
|
56
|
+
# Recall — graph traversal + QAOA finds the optimal combination
|
|
57
|
+
result = recall("What is Project Alpha's full tech stack?", K=4)
|
|
58
|
+
|
|
59
|
+
for memory in result["memories"]:
|
|
60
|
+
print(f" {memory['text']}")
|
|
61
|
+
print(f" Connected to {len(memory['connections'])} other selected memories")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Output: Returns React, FastAPI, PostgreSQL, and SQLAlchemy memories — connected, complete, no noise. The pizza memory is excluded because it has no graph connections to the tech stack cluster.
|
|
65
|
+
|
|
66
|
+
## How It Works
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Query: "What's the tech stack?"
|
|
70
|
+
│
|
|
71
|
+
▼
|
|
72
|
+
┌─────────────────────┐
|
|
73
|
+
│ 1. Graph Search │ Embedding similarity + multi-hop traversal
|
|
74
|
+
│ Find neighbors │ Discovers memories connected to relevant ones
|
|
75
|
+
└────────┬────────────┘
|
|
76
|
+
│ 14 candidates
|
|
77
|
+
▼
|
|
78
|
+
┌─────────────────────┐
|
|
79
|
+
│ 2. Subgraph Data │ Extract adjacency matrix + relevance scores
|
|
80
|
+
│ Build problem │ Encode relationships as optimization weights
|
|
81
|
+
└────────┬────────────┘
|
|
82
|
+
│ NP-hard selection
|
|
83
|
+
▼
|
|
84
|
+
┌─────────────────────┐
|
|
85
|
+
│ 3. QAOA Optimize │ Quantum approximate optimization
|
|
86
|
+
│ Find best K │ Maximizes: relevance + connectivity + coverage
|
|
87
|
+
└────────┬────────────┘
|
|
88
|
+
│ K memories
|
|
89
|
+
▼
|
|
90
|
+
┌─────────────────────┐
|
|
91
|
+
│ 4. Return with │ Each memory includes its connections
|
|
92
|
+
│ relationships │ to other selected memories
|
|
93
|
+
└─────────────────────┘
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Why Quantum?
|
|
97
|
+
|
|
98
|
+
Optimal subgraph selection is NP-hard. Given N candidate memories, finding the best K that maximize relevance, connectivity, AND coverage has exponential classical complexity. QAOA provides polynomial-time approximate solutions that beat greedy heuristics — this is the one problem where quantum computing has a genuine algorithmic advantage over classical approaches.
|
|
99
|
+
|
|
100
|
+
## Architecture
|
|
101
|
+
|
|
102
|
+
### Three Layers
|
|
103
|
+
|
|
104
|
+
1. **Knowledge Graph** (`graph.py`) — Memories are nodes. Relationships are weighted edges based on:
|
|
105
|
+
- Semantic similarity (embedding cosine distance)
|
|
106
|
+
- Entity co-occurrence (shared people, projects, concepts)
|
|
107
|
+
- Temporal proximity (memories close in time)
|
|
108
|
+
- Source proximity (same conversation/document)
|
|
109
|
+
|
|
110
|
+
2. **Subgraph Optimizer** (`subgraph_optimizer.py`) — QAOA circuit that maximizes:
|
|
111
|
+
- α × relevance (individual memory scores)
|
|
112
|
+
- β × connectivity (edge weights within selected subgraph)
|
|
113
|
+
- γ × coverage (topic diversity across selection)
|
|
114
|
+
|
|
115
|
+
3. **Pipeline** (`pipeline.py`) — Unified `store()` and `recall()` interface.
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API Server
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install quantum-memory-graph[api]
|
|
123
|
+
python -m quantum_memory_graph.api
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Endpoints:
|
|
127
|
+
- `POST /store` — Store a memory
|
|
128
|
+
- `POST /recall` — Graph + QAOA recall
|
|
129
|
+
- `POST /store-batch` — Batch store
|
|
130
|
+
- `GET /stats` — Graph statistics
|
|
131
|
+
- `GET /` — Health check
|
|
132
|
+
|
|
133
|
+
## Advanced Usage
|
|
134
|
+
|
|
135
|
+
### Custom Graph
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from quantum_memory_graph import MemoryGraph, recall
|
|
139
|
+
from quantum_memory_graph.pipeline import set_graph
|
|
140
|
+
|
|
141
|
+
# Tune similarity threshold for edge creation
|
|
142
|
+
graph = MemoryGraph(similarity_threshold=0.25)
|
|
143
|
+
set_graph(graph)
|
|
144
|
+
|
|
145
|
+
# Store and recall as normal
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Tune QAOA Parameters
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
result = recall(
|
|
152
|
+
"query",
|
|
153
|
+
K=5,
|
|
154
|
+
alpha=0.4, # Relevance weight
|
|
155
|
+
beta_conn=0.35, # Connectivity weight
|
|
156
|
+
gamma_cov=0.25, # Coverage/diversity weight
|
|
157
|
+
hops=3, # Graph traversal depth
|
|
158
|
+
top_seeds=7, # Initial seed nodes
|
|
159
|
+
max_candidates=14, # Max qubits for QAOA
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Run MemCombine Benchmark
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from benchmarks.memcombine import run_benchmark
|
|
167
|
+
|
|
168
|
+
def my_recall(memories, query, K):
|
|
169
|
+
# Your recall implementation
|
|
170
|
+
return selected_indices # List[int]
|
|
171
|
+
|
|
172
|
+
results = run_benchmark(my_recall, K=5)
|
|
173
|
+
print(f"Coverage: {results['avg_coverage']*100:.1f}%")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## IBM Quantum Hardware
|
|
177
|
+
|
|
178
|
+
For production workloads, run QAOA on real quantum hardware:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
pip install quantum-memory-graph[ibm]
|
|
182
|
+
export IBM_QUANTUM_TOKEN=your_token
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Validated on `ibm_fez` and `ibm_kingston` backends.
|
|
186
|
+
|
|
187
|
+
## Requirements
|
|
188
|
+
|
|
189
|
+
- Python ≥ 3.9
|
|
190
|
+
- sentence-transformers
|
|
191
|
+
- networkx
|
|
192
|
+
- qiskit + qiskit-aer
|
|
193
|
+
- numpy
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
MIT License — Copyright 2026 Coinkong (Chef's Attraction)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
## Links
|
|
201
|
+
|
|
202
|
+
- [quantum-agent-memory](https://github.com/Dustin-a11y/quantum-agent-memory) — The QAOA optimization engine
|
|
203
|
+
- [MemCombine Benchmark](benchmarks/memcombine.py) — Test memory combination quality
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""
|
|
2
|
+
QMG Benchmark Data Collector.
|
|
3
|
+
|
|
4
|
+
Logs every benchmark run with full metadata for study and analysis.
|
|
5
|
+
Data is append-only, timestamped, and structured for easy analysis.
|
|
6
|
+
|
|
7
|
+
DK 🦍
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
from benchmarks.data_collector import QMGBenchmarkLogger
|
|
11
|
+
|
|
12
|
+
logger = QMGBenchmarkLogger()
|
|
13
|
+
logger.log_memcombine_run(method, results, params)
|
|
14
|
+
logger.log_qaoa_run(n_candidates, n_qubits, selection_result, timing)
|
|
15
|
+
logger.export_csv() # Export all data for study
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import time
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
from typing import Dict, List, Optional
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
BENCHMARK_LOG_DIR = Path(os.environ.get(
|
|
27
|
+
"QMG_BENCHMARK_LOG",
|
|
28
|
+
"/home/dt/.hermes/profiles/dk/quantum-tracker/benchmarks"
|
|
29
|
+
))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class QMGBenchmarkLogger:
|
|
33
|
+
"""Append-only benchmark data logger."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, log_dir: str = None):
|
|
36
|
+
self.log_dir = Path(log_dir) if log_dir else BENCHMARK_LOG_DIR
|
|
37
|
+
self.log_dir.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
self._session_id = f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
39
|
+
|
|
40
|
+
def log_memcombine_run(self, method: str, results: Dict, params: Dict = None):
|
|
41
|
+
"""Log a MemCombine benchmark run."""
|
|
42
|
+
entry = {
|
|
43
|
+
"type": "memcombine_run",
|
|
44
|
+
"session_id": self._session_id,
|
|
45
|
+
"timestamp": datetime.now().isoformat(),
|
|
46
|
+
"unix_time": time.time(),
|
|
47
|
+
"method": method,
|
|
48
|
+
"results": {
|
|
49
|
+
"coverage": results.get("avg_coverage", results.get("coverage")),
|
|
50
|
+
"evidence_recall": results.get("avg_evidence_recall", results.get("evidence_recall")),
|
|
51
|
+
"f1": results.get("avg_f1", results.get("f1")),
|
|
52
|
+
"perfect": results.get("perfect_coverage", results.get("perfect")),
|
|
53
|
+
"perfect_pct": results.get("perfect_coverage_pct", results.get("perfect_pct")),
|
|
54
|
+
"n_scenarios": results.get("n_scenarios", len(results.get("per_scenario", []))),
|
|
55
|
+
},
|
|
56
|
+
"params": params or {},
|
|
57
|
+
"per_scenario": results.get("per_scenario", []),
|
|
58
|
+
}
|
|
59
|
+
self._write("memcombine", entry)
|
|
60
|
+
return entry
|
|
61
|
+
|
|
62
|
+
def log_longmemeval_run(self, method: str, results: Dict, model: str = None, params: Dict = None):
|
|
63
|
+
"""Log a LongMemEval benchmark run."""
|
|
64
|
+
entry = {
|
|
65
|
+
"type": "longmemeval_run",
|
|
66
|
+
"session_id": self._session_id,
|
|
67
|
+
"timestamp": datetime.now().isoformat(),
|
|
68
|
+
"unix_time": time.time(),
|
|
69
|
+
"model": model or "unknown",
|
|
70
|
+
"method": method,
|
|
71
|
+
"results": {
|
|
72
|
+
"recall_at_5": results.get("recall_at_5"),
|
|
73
|
+
"recall_at_10": results.get("recall_at_10"),
|
|
74
|
+
"ndcg_at_10": results.get("ndcg_at_10"),
|
|
75
|
+
"n": results.get("n"),
|
|
76
|
+
},
|
|
77
|
+
"params": params or {},
|
|
78
|
+
}
|
|
79
|
+
self._write("longmemeval", entry)
|
|
80
|
+
return entry
|
|
81
|
+
|
|
82
|
+
def log_qaoa_run(self,
|
|
83
|
+
n_candidates: int,
|
|
84
|
+
n_qubits: int,
|
|
85
|
+
K: int,
|
|
86
|
+
p_layers: int,
|
|
87
|
+
method: str,
|
|
88
|
+
selection_result: Dict,
|
|
89
|
+
timing_ms: float,
|
|
90
|
+
params: Dict = None):
|
|
91
|
+
"""Log a single QAOA optimization run."""
|
|
92
|
+
entry = {
|
|
93
|
+
"type": "qaoa_run",
|
|
94
|
+
"session_id": self._session_id,
|
|
95
|
+
"timestamp": datetime.now().isoformat(),
|
|
96
|
+
"unix_time": time.time(),
|
|
97
|
+
"n_candidates": n_candidates,
|
|
98
|
+
"n_qubits": n_qubits,
|
|
99
|
+
"K": K,
|
|
100
|
+
"p_layers": p_layers,
|
|
101
|
+
"method": method,
|
|
102
|
+
"compression_ratio": selection_result.get("compression_ratio", f"{n_candidates}→{n_qubits}"),
|
|
103
|
+
"qaoa_score": selection_result.get("score"),
|
|
104
|
+
"qaoa_vs_greedy_pct": selection_result.get("qaoa_vs_greedy_pct"),
|
|
105
|
+
"qaoa_vs_optimal_pct": selection_result.get("qaoa_vs_optimal_pct"),
|
|
106
|
+
"timing_ms": timing_ms,
|
|
107
|
+
"params": params or {},
|
|
108
|
+
}
|
|
109
|
+
self._write("qaoa", entry)
|
|
110
|
+
return entry
|
|
111
|
+
|
|
112
|
+
def log_graph_stats(self, graph_stats: Dict, tag: str = ""):
|
|
113
|
+
"""Log memory graph statistics."""
|
|
114
|
+
entry = {
|
|
115
|
+
"type": "graph_stats",
|
|
116
|
+
"session_id": self._session_id,
|
|
117
|
+
"timestamp": datetime.now().isoformat(),
|
|
118
|
+
"unix_time": time.time(),
|
|
119
|
+
"tag": tag,
|
|
120
|
+
"nodes": graph_stats.get("nodes"),
|
|
121
|
+
"edges": graph_stats.get("edges"),
|
|
122
|
+
"density": graph_stats.get("density"),
|
|
123
|
+
"components": graph_stats.get("components"),
|
|
124
|
+
"avg_degree": graph_stats.get("avg_degree"),
|
|
125
|
+
}
|
|
126
|
+
self._write("graph", entry)
|
|
127
|
+
return entry
|
|
128
|
+
|
|
129
|
+
def log_hardware_run(self, backend: str, n_qubits: int, result: Dict, timing_ms: float):
|
|
130
|
+
"""Log a hardware execution run (IBM Quantum)."""
|
|
131
|
+
entry = {
|
|
132
|
+
"type": "hardware_run",
|
|
133
|
+
"session_id": self._session_id,
|
|
134
|
+
"timestamp": datetime.now().isoformat(),
|
|
135
|
+
"unix_time": time.time(),
|
|
136
|
+
"backend": backend,
|
|
137
|
+
"n_qubits": n_qubits,
|
|
138
|
+
"result": {
|
|
139
|
+
"score": result.get("score"),
|
|
140
|
+
"method": result.get("method"),
|
|
141
|
+
"error_mitigation": result.get("error_mitigation", "none"),
|
|
142
|
+
},
|
|
143
|
+
"timing_ms": timing_ms,
|
|
144
|
+
}
|
|
145
|
+
self._write("hardware", entry)
|
|
146
|
+
return entry
|
|
147
|
+
|
|
148
|
+
def _write(self, category: str, entry: Dict):
|
|
149
|
+
"""Append entry to category log file."""
|
|
150
|
+
log_file = self.log_dir / f"{category}_log.jsonl"
|
|
151
|
+
with open(log_file, "a") as f:
|
|
152
|
+
f.write(json.dumps(entry) + "\n")
|
|
153
|
+
|
|
154
|
+
def export_csv(self, category: str = None) -> str:
|
|
155
|
+
"""
|
|
156
|
+
Export logged data as CSV for analysis.
|
|
157
|
+
Returns path to CSV file.
|
|
158
|
+
"""
|
|
159
|
+
import csv
|
|
160
|
+
from collections import OrderedDict
|
|
161
|
+
|
|
162
|
+
categories = [category] if category else ["memcombine", "longmemeval", "qaoa", "graph", "hardware"]
|
|
163
|
+
output_paths = []
|
|
164
|
+
|
|
165
|
+
for cat in categories:
|
|
166
|
+
log_file = self.log_dir / f"{cat}_log.jsonl"
|
|
167
|
+
if not log_file.exists():
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
csv_path = self.log_dir / f"{cat}_export.csv"
|
|
171
|
+
entries = []
|
|
172
|
+
with open(log_file) as f:
|
|
173
|
+
for line in f:
|
|
174
|
+
line = line.strip()
|
|
175
|
+
if line:
|
|
176
|
+
entries.append(json.loads(line))
|
|
177
|
+
|
|
178
|
+
if not entries:
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
# Gather all keys
|
|
182
|
+
all_keys = []
|
|
183
|
+
for e in entries:
|
|
184
|
+
for k in e.keys():
|
|
185
|
+
if k not in all_keys:
|
|
186
|
+
all_keys.append(k)
|
|
187
|
+
|
|
188
|
+
with open(csv_path, "w", newline="") as f:
|
|
189
|
+
writer = csv.DictWriter(f, fieldnames=all_keys, extrasaction="ignore")
|
|
190
|
+
writer.writeheader()
|
|
191
|
+
for e in entries:
|
|
192
|
+
writer.writerow(e)
|
|
193
|
+
|
|
194
|
+
output_paths.append(str(csv_path))
|
|
195
|
+
|
|
196
|
+
return ", ".join(output_paths) if output_paths else "No data logged yet"
|
|
197
|
+
|
|
198
|
+
def summary(self) -> Dict:
|
|
199
|
+
"""Quick summary of all logged data."""
|
|
200
|
+
result = {}
|
|
201
|
+
for cat in ["memcombine", "longmemeval", "qaoa", "graph", "hardware"]:
|
|
202
|
+
log_file = self.log_dir / f"{cat}_log.jsonl"
|
|
203
|
+
if log_file.exists():
|
|
204
|
+
with open(log_file) as f:
|
|
205
|
+
lines = [l for l in f if l.strip()]
|
|
206
|
+
result[cat] = len(lines)
|
|
207
|
+
else:
|
|
208
|
+
result[cat] = 0
|
|
209
|
+
return result
|