lqft-python-engine 0.2.0__tar.gz → 0.5.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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lqft-python-engine
3
- Version: 0.2.0
4
- Summary: LQFT Engine: Full CRUD Support & Enterprise Scaling (v4.5 Stable)
3
+ Version: 0.5.0
4
+ Summary: LQFT Engine: Native Disk Persistence & Cold Start Deserialization (v5.0 Stable)
5
5
  Home-page: https://github.com/ParjadM/Log-Quantum-Fractal-Tree-LQFT-
6
6
  Author: Parjad Minooei
7
7
  License: MIT
@@ -0,0 +1,490 @@
1
+ #define PY_SSIZE_T_CLEAN
2
+ #include <Python.h>
3
+
4
+ #ifndef _CRT_SECURE_NO_WARNINGS
5
+ #define _CRT_SECURE_NO_WARNINGS
6
+ #endif
7
+
8
+ #include <stdio.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+ #include <stdint.h>
12
+
13
+ /**
14
+ * LQFT C-Engine - V0.5.0 (Native Disk Persistence)
15
+ * Architect: Parjad Minooei
16
+ * * CHANGE LOG:
17
+ * - Implemented Binary Serialization (save_to_disk).
18
+ * - Implemented O(1) Cold Start Deserialization (load_from_disk).
19
+ * - DAG Pointer Reconstruction Logic.
20
+ */
21
+
22
+ #define BIT_PARTITION 5
23
+ #define MAX_BITS 64
24
+ #define MASK 0x1F
25
+ #define REGISTRY_SIZE 8000009
26
+ #define TOMBSTONE ((LQFTNode*)1)
27
+
28
+ typedef struct LQFTNode {
29
+ void* value;
30
+ uint64_t key_hash;
31
+ struct LQFTNode* children[32];
32
+ char struct_hash[17];
33
+ uint64_t full_hash_val;
34
+ int ref_count;
35
+ } LQFTNode;
36
+
37
+ static LQFTNode** registry = NULL;
38
+ static int physical_node_count = 0;
39
+ static LQFTNode* global_root = NULL;
40
+
41
+ const uint64_t FNV_OFFSET_BASIS = 14695981039346656037ULL;
42
+ const uint64_t FNV_PRIME = 1099511628211ULL;
43
+
44
+ // -------------------------------------------------------------------
45
+ // Utilities
46
+ // -------------------------------------------------------------------
47
+ uint64_t fnv1a_update(uint64_t hash, const void* data, size_t len) {
48
+ const uint8_t* p = (const uint8_t*)data;
49
+ for (size_t i = 0; i < len; i++) {
50
+ hash ^= p[i];
51
+ hash *= FNV_PRIME;
52
+ }
53
+ return hash;
54
+ }
55
+
56
+ char* portable_strdup(const char* s) {
57
+ if (!s) return NULL;
58
+ #ifdef _WIN32
59
+ return _strdup(s);
60
+ #else
61
+ return strdup(s);
62
+ #endif
63
+ }
64
+
65
+ static int init_registry() {
66
+ if (registry == NULL) {
67
+ registry = (LQFTNode**)calloc(REGISTRY_SIZE, sizeof(LQFTNode*));
68
+ if (registry == NULL) return 0;
69
+ }
70
+ return 1;
71
+ }
72
+
73
+ LQFTNode* create_node(void* value, uint64_t key_hash) {
74
+ LQFTNode* node = (LQFTNode*)malloc(sizeof(LQFTNode));
75
+ if (!node) return NULL;
76
+ node->value = value;
77
+ node->key_hash = key_hash;
78
+ node->full_hash_val = 0;
79
+ node->ref_count = 0;
80
+ for (int i = 0; i < 32; i++) node->children[i] = NULL;
81
+ return node;
82
+ }
83
+
84
+ // -------------------------------------------------------------------
85
+ // Memory Management (ARC)
86
+ // -------------------------------------------------------------------
87
+ void decref(LQFTNode* node) {
88
+ if (!node) return;
89
+ node->ref_count--;
90
+ if (node->ref_count <= 0) {
91
+ for (int i = 0; i < 32; i++) {
92
+ if (node->children[i]) decref(node->children[i]);
93
+ }
94
+ uint32_t idx = node->full_hash_val % REGISTRY_SIZE;
95
+ uint32_t start_idx = idx;
96
+ while (registry[idx] != NULL) {
97
+ if (registry[idx] == node) {
98
+ registry[idx] = TOMBSTONE;
99
+ break;
100
+ }
101
+ idx = (idx + 1) % REGISTRY_SIZE;
102
+ if (idx == start_idx) break;
103
+ }
104
+ if (node->value) free(node->value);
105
+ free(node);
106
+ physical_node_count--;
107
+ }
108
+ }
109
+
110
+ static PyObject* method_free_all(PyObject* self, PyObject* args) {
111
+ if (registry != NULL) {
112
+ for (int i = 0; i < REGISTRY_SIZE; i++) {
113
+ if (registry[i] != NULL && registry[i] != TOMBSTONE) {
114
+ if (registry[i]->value) free(registry[i]->value);
115
+ free(registry[i]);
116
+ }
117
+ registry[i] = NULL;
118
+ }
119
+ free(registry);
120
+ registry = NULL;
121
+ }
122
+ physical_node_count = 0;
123
+ global_root = NULL;
124
+ Py_RETURN_NONE;
125
+ }
126
+
127
+ // -------------------------------------------------------------------
128
+ // Disk Persistence (Binary Serialization)
129
+ // -------------------------------------------------------------------
130
+ static PyObject* method_save_to_disk(PyObject* self, PyObject* args) {
131
+ const char* filepath;
132
+ if (!PyArg_ParseTuple(args, "s", &filepath)) return NULL;
133
+ if (!registry) Py_RETURN_NONE;
134
+
135
+ FILE* fp = fopen(filepath, "wb");
136
+ if (!fp) return PyErr_SetFromErrno(PyExc_IOError);
137
+
138
+ // Header: Magic Bytes, Count, Global Root Hash
139
+ char magic[4] = "LQFT";
140
+ fwrite(magic, 1, 4, fp);
141
+ fwrite(&physical_node_count, sizeof(int), 1, fp);
142
+ uint64_t root_hash = global_root ? global_root->full_hash_val : 0;
143
+ fwrite(&root_hash, sizeof(uint64_t), 1, fp);
144
+
145
+ // Dump Nodes
146
+ for (int i = 0; i < REGISTRY_SIZE; i++) {
147
+ LQFTNode* node = registry[i];
148
+ if (node != NULL && node != TOMBSTONE) {
149
+ fwrite(&node->full_hash_val, sizeof(uint64_t), 1, fp);
150
+ fwrite(&node->key_hash, sizeof(uint64_t), 1, fp);
151
+ fwrite(node->struct_hash, 1, 17, fp);
152
+ fwrite(&node->ref_count, sizeof(int), 1, fp);
153
+
154
+ // Payload
155
+ int has_val = (node->value != NULL) ? 1 : 0;
156
+ fwrite(&has_val, sizeof(int), 1, fp);
157
+ if (has_val) {
158
+ int v_len = (int)strlen((char*)node->value);
159
+ fwrite(&v_len, sizeof(int), 1, fp);
160
+ fwrite(node->value, 1, v_len, fp);
161
+ }
162
+
163
+ // Children pointers (saved as full_hash_val references)
164
+ uint64_t child_refs[32] = {0};
165
+ for (int c = 0; c < 32; c++) {
166
+ if (node->children[c]) child_refs[c] = node->children[c]->full_hash_val;
167
+ }
168
+ fwrite(child_refs, sizeof(uint64_t), 32, fp);
169
+ }
170
+ }
171
+
172
+ fclose(fp);
173
+ Py_RETURN_TRUE;
174
+ }
175
+
176
+ LQFTNode* find_in_registry(uint64_t full_hash) {
177
+ if (full_hash == 0) return NULL;
178
+ uint32_t idx = full_hash % REGISTRY_SIZE;
179
+ uint32_t start_idx = idx;
180
+ while (registry[idx] != NULL) {
181
+ if (registry[idx] != TOMBSTONE && registry[idx]->full_hash_val == full_hash) {
182
+ return registry[idx];
183
+ }
184
+ idx = (idx + 1) % REGISTRY_SIZE;
185
+ if (idx == start_idx) break;
186
+ }
187
+ return NULL;
188
+ }
189
+
190
+ static PyObject* method_load_from_disk(PyObject* self, PyObject* args) {
191
+ const char* filepath;
192
+ if (!PyArg_ParseTuple(args, "s", &filepath)) return NULL;
193
+
194
+ FILE* fp = fopen(filepath, "rb");
195
+ if (!fp) return PyErr_SetFromErrno(PyExc_IOError);
196
+
197
+ char magic[5] = {0};
198
+ fread(magic, 1, 4, fp);
199
+ if (strcmp(magic, "LQFT") != 0) {
200
+ fclose(fp);
201
+ PyErr_SetString(PyExc_ValueError, "Invalid LQFT binary file format.");
202
+ return NULL;
203
+ }
204
+
205
+ // Clear current state safely
206
+ method_free_all(self, NULL);
207
+ init_registry();
208
+
209
+ int total_nodes;
210
+ uint64_t root_hash;
211
+ fread(&total_nodes, sizeof(int), 1, fp);
212
+ fread(&root_hash, sizeof(uint64_t), 1, fp);
213
+
214
+ // Parallel array to store child hashes for Pass 2 (Pointer Reconstruction)
215
+ uint64_t* all_child_refs = (uint64_t*)malloc(total_nodes * 32 * sizeof(uint64_t));
216
+ LQFTNode** loaded_nodes = (LQFTNode**)malloc(total_nodes * sizeof(LQFTNode*));
217
+
218
+ // PASS 1: Read physical nodes into memory
219
+ for (int i = 0; i < total_nodes; i++) {
220
+ LQFTNode* node = create_node(NULL, 0);
221
+ fread(&node->full_hash_val, sizeof(uint64_t), 1, fp);
222
+ fread(&node->key_hash, sizeof(uint64_t), 1, fp);
223
+ fread(node->struct_hash, 1, 17, fp);
224
+ fread(&node->ref_count, sizeof(int), 1, fp);
225
+
226
+ int has_val;
227
+ fread(&has_val, sizeof(int), 1, fp);
228
+ if (has_val) {
229
+ int v_len;
230
+ fread(&v_len, sizeof(int), 1, fp);
231
+ char* val_str = (char*)malloc(v_len + 1);
232
+ fread(val_str, 1, v_len, fp);
233
+ val_str[v_len] = '\0';
234
+ node->value = val_str;
235
+ }
236
+
237
+ fread(&all_child_refs[i * 32], sizeof(uint64_t), 32, fp);
238
+
239
+ // Insert directly into registry
240
+ uint32_t idx = node->full_hash_val % REGISTRY_SIZE;
241
+ while (registry[idx] != NULL) idx = (idx + 1) % REGISTRY_SIZE;
242
+ registry[idx] = node;
243
+
244
+ loaded_nodes[i] = node;
245
+ physical_node_count++;
246
+ }
247
+
248
+ // PASS 2: Pointer Reconstruction (Relinking the DAG)
249
+ for (int i = 0; i < total_nodes; i++) {
250
+ for (int c = 0; c < 32; c++) {
251
+ uint64_t target_hash = all_child_refs[i * 32 + c];
252
+ if (target_hash != 0) {
253
+ loaded_nodes[i]->children[c] = find_in_registry(target_hash);
254
+ }
255
+ }
256
+ }
257
+
258
+ global_root = find_in_registry(root_hash);
259
+
260
+ free(all_child_refs);
261
+ free(loaded_nodes);
262
+ fclose(fp);
263
+
264
+ Py_RETURN_TRUE;
265
+ }
266
+
267
+ // -------------------------------------------------------------------
268
+ // Merkle-DAG Core (Standard Operations)
269
+ // -------------------------------------------------------------------
270
+ LQFTNode* get_canonical(void* value, uint64_t key_hash, LQFTNode** children) {
271
+ if (!init_registry()) return NULL;
272
+
273
+ uint64_t full_hash = FNV_OFFSET_BASIS;
274
+ if (value != NULL) {
275
+ full_hash = fnv1a_update(full_hash, "leaf:", 5);
276
+ full_hash = fnv1a_update(full_hash, value, strlen((char*)value));
277
+ full_hash = fnv1a_update(full_hash, &key_hash, sizeof(uint64_t));
278
+ } else {
279
+ full_hash = fnv1a_update(full_hash, "branch:", 7);
280
+ if (children) {
281
+ for (int i = 0; i < 32; i++) {
282
+ if (children[i]) {
283
+ full_hash = fnv1a_update(full_hash, &i, sizeof(int));
284
+ full_hash = fnv1a_update(full_hash, children[i]->struct_hash, 16);
285
+ }
286
+ }
287
+ }
288
+ }
289
+
290
+ char lookup_hash[17];
291
+ sprintf(lookup_hash, "%016llx", (unsigned long long)full_hash);
292
+ uint32_t idx = full_hash % REGISTRY_SIZE;
293
+ uint32_t start_idx = idx;
294
+ int first_tombstone = -1;
295
+
296
+ while (registry[idx] != NULL) {
297
+ if (registry[idx] == TOMBSTONE) {
298
+ if (first_tombstone == -1) first_tombstone = (int)idx;
299
+ } else if (registry[idx]->full_hash_val == full_hash && strcmp(registry[idx]->struct_hash, lookup_hash) == 0) {
300
+ if (value) free(value);
301
+ return registry[idx];
302
+ }
303
+ idx = (idx + 1) % REGISTRY_SIZE;
304
+ if (idx == start_idx) break;
305
+ }
306
+
307
+ LQFTNode* new_node = create_node(value, key_hash);
308
+ if (!new_node) return NULL;
309
+
310
+ if (children) {
311
+ for (int i = 0; i < 32; i++) {
312
+ new_node->children[i] = children[i];
313
+ if (children[i]) children[i]->ref_count++;
314
+ }
315
+ }
316
+
317
+ strcpy(new_node->struct_hash, lookup_hash);
318
+ new_node->full_hash_val = full_hash;
319
+
320
+ uint32_t insert_idx = (first_tombstone != -1) ? (uint32_t)first_tombstone : idx;
321
+ registry[insert_idx] = new_node;
322
+ physical_node_count++;
323
+
324
+ return new_node;
325
+ }
326
+
327
+ static PyObject* method_insert(PyObject* self, PyObject* args) {
328
+ unsigned long long h;
329
+ char* val_str;
330
+ if (!PyArg_ParseTuple(args, "Ks", &h, &val_str)) return NULL;
331
+
332
+ if (!global_root) {
333
+ if (!init_registry()) return PyErr_NoMemory();
334
+ global_root = get_canonical(NULL, 0, NULL);
335
+ global_root->ref_count++;
336
+ }
337
+
338
+ LQFTNode* old_root = global_root;
339
+ LQFTNode* path_nodes[20];
340
+ uint32_t path_segs[20];
341
+ int path_len = 0;
342
+ LQFTNode* curr = global_root;
343
+ int bit_depth = 0;
344
+
345
+ while (curr != NULL && curr->value == NULL) {
346
+ uint32_t segment = (h >> bit_depth) & MASK;
347
+ path_nodes[path_len] = curr;
348
+ path_segs[path_len] = segment;
349
+ path_len++;
350
+ if (curr->children[segment] == NULL) { curr = NULL; break; }
351
+ curr = curr->children[segment];
352
+ bit_depth += BIT_PARTITION;
353
+ }
354
+
355
+ LQFTNode* new_sub_node = NULL;
356
+ if (curr == NULL) {
357
+ new_sub_node = get_canonical(portable_strdup(val_str), h, NULL);
358
+ } else if (curr->key_hash == h) {
359
+ new_sub_node = get_canonical(portable_strdup(val_str), h, curr->children);
360
+ } else {
361
+ unsigned long long old_h = curr->key_hash;
362
+ char* old_val = portable_strdup((char*)curr->value);
363
+ int temp_depth = bit_depth;
364
+ while (temp_depth < 64) {
365
+ uint32_t s_old = (old_h >> temp_depth) & MASK;
366
+ uint32_t s_new = (h >> temp_depth) & MASK;
367
+ if (s_old != s_new) {
368
+ LQFTNode* c_old = get_canonical(old_val, old_h, curr->children);
369
+ LQFTNode* c_new = get_canonical(portable_strdup(val_str), h, NULL);
370
+ LQFTNode* new_children[32] = {NULL};
371
+ new_children[s_old] = c_old;
372
+ new_children[s_new] = c_new;
373
+ new_sub_node = get_canonical(NULL, 0, new_children);
374
+ break;
375
+ } else {
376
+ path_nodes[path_len] = NULL;
377
+ path_segs[path_len] = s_old;
378
+ path_len++;
379
+ temp_depth += BIT_PARTITION;
380
+ }
381
+ }
382
+ if (new_sub_node == NULL) new_sub_node = get_canonical(portable_strdup(val_str), h, curr->children);
383
+ }
384
+
385
+ for (int i = path_len - 1; i >= 0; i--) {
386
+ if (path_nodes[i] == NULL) {
387
+ LQFTNode* new_children[32] = {NULL};
388
+ new_children[path_segs[i]] = new_sub_node;
389
+ new_sub_node = get_canonical(NULL, 0, new_children);
390
+ } else {
391
+ LQFTNode* p_node = path_nodes[i];
392
+ uint32_t segment = path_segs[i];
393
+ LQFTNode* new_children[32];
394
+ for (int j = 0; j < 32; j++) new_children[j] = p_node->children[j];
395
+ new_children[segment] = new_sub_node;
396
+ new_sub_node = get_canonical(p_node->value, p_node->key_hash, new_children);
397
+ }
398
+ }
399
+
400
+ global_root = new_sub_node;
401
+ global_root->ref_count++;
402
+ if (old_root) decref(old_root);
403
+
404
+ Py_RETURN_NONE;
405
+ }
406
+
407
+ static PyObject* method_search(PyObject* self, PyObject* args) {
408
+ unsigned long long h;
409
+ if (!PyArg_ParseTuple(args, "K", &h)) return NULL;
410
+ if (!global_root) Py_RETURN_NONE;
411
+
412
+ LQFTNode* curr = global_root;
413
+ int bit_depth = 0;
414
+ while (curr != NULL && curr->value == NULL) {
415
+ uint32_t segment = (h >> bit_depth) & MASK;
416
+ curr = curr->children[segment];
417
+ bit_depth += BIT_PARTITION;
418
+ }
419
+
420
+ if (curr != NULL && curr->key_hash == h) return PyUnicode_FromString((char*)curr->value);
421
+ Py_RETURN_NONE;
422
+ }
423
+
424
+ static PyObject* method_delete(PyObject* self, PyObject* args) {
425
+ unsigned long long h;
426
+ if (!PyArg_ParseTuple(args, "K", &h)) return NULL;
427
+ if (!global_root) Py_RETURN_NONE;
428
+
429
+ LQFTNode* path_nodes[20];
430
+ uint32_t path_segs[20];
431
+ int path_len = 0;
432
+ LQFTNode* curr = global_root;
433
+ int bit_depth = 0;
434
+
435
+ while (curr != NULL && curr->value == NULL) {
436
+ uint32_t segment = (h >> bit_depth) & MASK;
437
+ path_nodes[path_len] = curr;
438
+ path_segs[path_len] = segment;
439
+ path_len++;
440
+ curr = curr->children[segment];
441
+ bit_depth += BIT_PARTITION;
442
+ }
443
+
444
+ if (curr == NULL || curr->key_hash != h) Py_RETURN_NONE;
445
+
446
+ LQFTNode* old_root = global_root;
447
+ LQFTNode* new_sub_node = NULL;
448
+
449
+ for (int i = path_len - 1; i >= 0; i--) {
450
+ LQFTNode* p_node = path_nodes[i];
451
+ uint32_t segment = path_segs[i];
452
+ LQFTNode* new_children[32];
453
+ int has_other_children = 0;
454
+
455
+ for (uint32_t j = 0; j < 32; j++) {
456
+ if (j == segment) new_children[j] = new_sub_node;
457
+ else {
458
+ new_children[j] = p_node->children[j];
459
+ if (new_children[j]) has_other_children = 1;
460
+ }
461
+ }
462
+
463
+ if (!has_other_children && i > 0) new_sub_node = NULL;
464
+ else new_sub_node = get_canonical(NULL, 0, new_children);
465
+ }
466
+
467
+ global_root = (new_sub_node) ? new_sub_node : get_canonical(NULL, 0, NULL);
468
+ if (global_root) global_root->ref_count++;
469
+ if (old_root) decref(old_root);
470
+
471
+ Py_RETURN_NONE;
472
+ }
473
+
474
+ static PyObject* method_get_metrics(PyObject* self, PyObject* args) {
475
+ return Py_BuildValue("{s:i}", "physical_nodes", physical_node_count);
476
+ }
477
+
478
+ static PyMethodDef LQFTMethods[] = {
479
+ {"insert", method_insert, METH_VARARGS, "Insert key-value"},
480
+ {"delete", method_delete, METH_VARARGS, "Delete key"},
481
+ {"search", method_search, METH_VARARGS, "Search key"},
482
+ {"save_to_disk", method_save_to_disk, METH_VARARGS, "Serialize to .bin"},
483
+ {"load_from_disk", method_load_from_disk, METH_VARARGS, "Deserialize from .bin"},
484
+ {"get_metrics", method_get_metrics, METH_VARARGS, "Get stats"},
485
+ {"free_all", method_free_all, METH_VARARGS, "Total memory wipe"},
486
+ {NULL, NULL, 0, NULL}
487
+ };
488
+
489
+ static struct PyModuleDef lqftmodule = { PyModuleDef_HEAD_INIT, "lqft_c_engine", NULL, -1, LQFTMethods };
490
+ PyMODINIT_FUNC PyInit_lqft_c_engine(void) { return PyModule_Create(&lqftmodule); }
@@ -1,8 +1,11 @@
1
1
  import hashlib
2
2
  import weakref
3
+ import psutil
4
+ import os
5
+ import json
3
6
 
4
7
  # ---------------------------------------------------------
5
- # LEGACY PURE PYTHON LQFT (For reference/fallback/benchmarks)
8
+ # LEGACY PURE PYTHON LQFT (For reference/fallback)
6
9
  # ---------------------------------------------------------
7
10
  class LQFTNode:
8
11
  __slots__ = ['children', 'value', 'key_hash', 'struct_hash', '__weakref__']
@@ -39,7 +42,6 @@ class LQFTNode:
39
42
  return cls._null_cache['null']
40
43
 
41
44
  class LQFT:
42
- """Legacy Pure Python Iterative Implementation."""
43
45
  def __init__(self, bit_partition=5, max_bits=256):
44
46
  self.partition = bit_partition
45
47
  self.max_bits = max_bits
@@ -62,7 +64,7 @@ class LQFT:
62
64
  break
63
65
  curr = curr.children[segment]
64
66
  bit_depth += self.partition
65
-
67
+
66
68
  new_sub_node = None
67
69
  if curr is null_node:
68
70
  new_sub_node = LQFTNode.get_canonical(value, None, h)
@@ -82,7 +84,7 @@ class LQFT:
82
84
  temp_depth += self.partition
83
85
  if new_sub_node is None:
84
86
  new_sub_node = LQFTNode.get_canonical(value, curr.children, h)
85
-
87
+
86
88
  for entry in reversed(path):
87
89
  if entry[0] == "split":
88
90
  new_sub_node = LQFTNode.get_canonical(None, {entry[1]: new_sub_node}, None)
@@ -104,7 +106,7 @@ class LQFT:
104
106
  return None
105
107
 
106
108
  # ---------------------------------------------------------
107
- # NEW: ADAPTIVE ENTERPRISE ENGINE (MScAC Portfolio)
109
+ # ADAPTIVE ENTERPRISE ENGINE (v0.5.0 - Disk Persistence)
108
110
  # ---------------------------------------------------------
109
111
  try:
110
112
  import lqft_c_engine
@@ -113,63 +115,92 @@ except ImportError:
113
115
  C_ENGINE_READY = False
114
116
 
115
117
  class AdaptiveLQFT:
116
- """
117
- A polymorphic, heuristic-driven data structure wrapper.
118
- - Scale < threshold: Acts as an ultra-lightweight C-Hash (Python Dict).
119
- - Scale > threshold: Automatically migrates to the Native C-Engine LQFT
120
- for Merkle-DAG deduplication and folding.
121
- """
122
118
  def __init__(self, migration_threshold=50000):
123
119
  self.threshold = migration_threshold
124
120
  self.size = 0
125
121
  self.is_native = False
126
-
127
- # The "Mini Version": Python's highly optimized built-in dictionary
128
122
  self._light_store = {}
123
+
124
+ self.auto_purge_enabled = True
125
+ self.max_memory_mb = 1000.0
126
+ self.total_ops = 0
127
+ self._process = psutil.Process(os.getpid())
128
+
129
+ def _validate_type(self, key, value=None):
130
+ if not isinstance(key, str):
131
+ raise TypeError(f"LQFT keys must be strings. Received: {type(key).__name__}")
132
+ if value is not None and not isinstance(value, str):
133
+ raise TypeError(f"LQFT values must be strings. Received: {type(value).__name__}")
129
134
 
130
135
  def _get_64bit_hash(self, key):
131
- """Generates a 64-bit unsigned hash for the C-Engine."""
132
- return int(hashlib.md5(str(key).encode()).hexdigest()[:16], 16)
136
+ return int(hashlib.md5(key.encode()).hexdigest()[:16], 16)
137
+
138
+ def set_auto_purge_threshold(self, threshold: float):
139
+ self.purge_threshold = threshold
140
+
141
+ def purge(self):
142
+ current_mb = self._process.memory_info().rss / (1024 * 1024)
143
+ if current_mb >= self.max_memory_mb:
144
+ print(f"\n[⚠️ CIRCUIT Breaker] Engine exceeded {self.max_memory_mb} MB limit (Currently {current_mb:.1f} MB). Auto-Purging!")
145
+ self.clear()
146
+
147
+ # --- Phase 1: Native Disk Persistence ---
148
+ def save_to_disk(self, filepath: str):
149
+ """Serializes the engine state to disk (O(1) Time context scaling)."""
150
+ if not self.is_native:
151
+ with open(filepath, 'w') as f:
152
+ json.dump(self._light_store, f)
153
+ else:
154
+ lqft_c_engine.save_to_disk(filepath)
155
+
156
+ def load_from_disk(self, filepath: str):
157
+ """Instantly reconstructs C-Pointers from a binary file."""
158
+ if not os.path.exists(filepath):
159
+ raise FileNotFoundError(f"Missing LQFT database file: {filepath}")
160
+
161
+ if not self.is_native:
162
+ # Check if it's a C-Engine binary file (magic bytes "LQFT")
163
+ with open(filepath, 'rb') as f:
164
+ if f.read(4) == b'LQFT':
165
+ self._migrate_to_native()
166
+ lqft_c_engine.load_from_disk(filepath)
167
+ return
168
+ with open(filepath, 'r') as f:
169
+ self._light_store = json.load(f)
170
+ self.size = len(self._light_store)
171
+ else:
172
+ lqft_c_engine.load_from_disk(filepath)
133
173
 
134
174
  def _migrate_to_native(self):
135
- """The 'Curve Flip' mechanism: moves all data to the Heavy Engine."""
136
175
  if not C_ENGINE_READY:
137
- print("[!] Warning: C-Engine missing. Staying in lightweight mode.")
138
- self.threshold = float('inf') # Prevent continuous upgrade attempts
176
+ self.threshold = float('inf')
139
177
  return
140
-
141
178
  for key, val in self._light_store.items():
142
179
  h = self._get_64bit_hash(key)
143
- lqft_c_engine.insert(h, str(val))
144
-
145
- # Clear the lightweight store to free up memory
180
+ lqft_c_engine.insert(h, val)
146
181
  self._light_store.clear()
147
182
  self.is_native = True
148
183
 
149
184
  def insert(self, key, value):
185
+ self._validate_type(key, value)
186
+ self.total_ops += 1
187
+ if self.auto_purge_enabled and self.total_ops % 5000 == 0:
188
+ current_mb = self._process.memory_info().rss / (1024 * 1024)
189
+ if current_mb >= self.max_memory_mb:
190
+ self.purge()
191
+
150
192
  if not self.is_native:
151
- # Phase 1: Small Data Operations
152
- if key not in self._light_store:
193
+ if key not in self._light_store:
153
194
  self.size += 1
154
195
  self._light_store[key] = value
155
-
156
- # Check for migration threshold
157
- if self.size >= self.threshold:
196
+ if self.size >= self.threshold:
158
197
  self._migrate_to_native()
159
- else:
160
- # Phase 2: Massive Data Operations (Native C-Heap)
161
- h = self._get_64bit_hash(key)
162
- lqft_c_engine.insert(h, str(value))
163
-
164
- def search(self, key):
165
- if not self.is_native:
166
- return self._light_store.get(key, None)
167
198
  else:
168
199
  h = self._get_64bit_hash(key)
169
- return lqft_c_engine.search(h)
200
+ lqft_c_engine.insert(h, value)
170
201
 
171
202
  def remove(self, key):
172
- """Deletes a key from either the light store or the Merkle tree."""
203
+ self._validate_type(key)
173
204
  if not self.is_native:
174
205
  if key in self._light_store:
175
206
  del self._light_store[key]
@@ -179,35 +210,33 @@ class AdaptiveLQFT:
179
210
  lqft_c_engine.delete(h)
180
211
 
181
212
  def delete(self, key):
182
- """Alias for remove to satisfy all testing suites."""
183
213
  self.remove(key)
184
214
 
215
+ def search(self, key):
216
+ self._validate_type(key)
217
+ if not self.is_native:
218
+ return self._light_store.get(key, None)
219
+ else:
220
+ h = self._get_64bit_hash(key)
221
+ return lqft_c_engine.search(h)
222
+
185
223
  def clear(self):
186
- """
187
- Memory Reclamation: Manually trigger heap cleanup.
188
- In the Adaptive model, this handles both the Python dict and the C-Registry.
189
- """
190
224
  self._light_store.clear()
191
225
  self.size = 0
192
- if C_ENGINE_READY:
226
+ if C_ENGINE_READY:
193
227
  return lqft_c_engine.free_all()
194
228
  return 0
195
229
 
196
230
  def get_stats(self):
197
- """Fetches memory metrics from the C-Engine if active."""
198
- if self.is_native and C_ENGINE_READY:
231
+ if self.is_native and C_ENGINE_READY:
199
232
  return lqft_c_engine.get_metrics()
200
233
  return {"physical_nodes": 0}
201
234
 
202
235
  def __del__(self):
203
- """Finalizer: Reclaims unmanaged C memory when the Python object is deleted."""
204
- try:
205
- self.clear()
206
- except:
207
- pass
236
+ try: self.clear()
237
+ except: pass
208
238
 
209
239
  def status(self):
210
- """Returns the current state of the engine."""
211
240
  return {
212
241
  "mode": "Native Merkle-DAG" if self.is_native else "Lightweight C-Hash",
213
242
  "items": self.size if not self.is_native else lqft_c_engine.get_metrics().get('physical_nodes', self.size),
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lqft-python-engine
3
- Version: 0.2.0
4
- Summary: LQFT Engine: Full CRUD Support & Enterprise Scaling (v4.5 Stable)
3
+ Version: 0.5.0
4
+ Summary: LQFT Engine: Native Disk Persistence & Cold Start Deserialization (v5.0 Stable)
5
5
  Home-page: https://github.com/ParjadM/Log-Quantum-Fractal-Tree-LQFT-
6
6
  Author: Parjad Minooei
7
7
  License: MIT
@@ -27,8 +27,8 @@ lqft_extension = Extension(
27
27
 
28
28
  setup(
29
29
  name="lqft-python-engine",
30
- version="0.2.0",
31
- description="LQFT Engine: Full CRUD Support & Enterprise Scaling (v4.5 Stable)",
30
+ version="0.5.0",
31
+ description="LQFT Engine: Native Disk Persistence & Cold Start Deserialization (v5.0 Stable)",
32
32
  long_description=long_description,
33
33
  long_description_content_type="text/markdown",
34
34
  author="Parjad Minooei",
@@ -1,282 +0,0 @@
1
- #define PY_SSIZE_T_CLEAN
2
- #include <Python.h>
3
-
4
- #ifndef _CRT_SECURE_NO_WARNINGS
5
- #define _CRT_SECURE_NO_WARNINGS
6
- #endif
7
-
8
- #include <stdio.h>
9
- #include <stdlib.h>
10
- #include <string.h>
11
- #include <stdint.h>
12
-
13
- /**
14
- * LQFT C-Engine - V4.5 (Full CRUD & Large Payload)
15
- * Architect: Parjad Minooei
16
- * * CHANGE LOG:
17
- * - Added 'delete' method with bottom-up path reconstruction.
18
- * - Maintains Incremental FNV-1a Hashing for large payloads.
19
- * - Fixed Stack-to-Heap collision for multi-MB data.
20
- */
21
-
22
- #define BIT_PARTITION 5
23
- #define MAX_BITS 64
24
- #define MASK 0x1F
25
- #define REGISTRY_SIZE 8000009
26
-
27
- typedef struct LQFTNode {
28
- void* value;
29
- uint64_t key_hash;
30
- struct LQFTNode* children[32];
31
- char struct_hash[17];
32
- } LQFTNode;
33
-
34
- static LQFTNode** registry = NULL;
35
- static int physical_node_count = 0;
36
- static LQFTNode* global_root = NULL;
37
-
38
- const uint64_t FNV_OFFSET_BASIS = 14695981039346656037ULL;
39
- const uint64_t FNV_PRIME = 1099511628211ULL;
40
-
41
- uint64_t fnv1a_update(uint64_t hash, const void* data, size_t len) {
42
- const uint8_t* p = (const uint8_t*)data;
43
- for (size_t i = 0; i < len; i++) {
44
- hash ^= p[i];
45
- hash *= FNV_PRIME;
46
- }
47
- return hash;
48
- }
49
-
50
- char* portable_strdup(const char* s) {
51
- if (!s) return NULL;
52
- #ifdef _WIN32
53
- return _strdup(s);
54
- #else
55
- return strdup(s);
56
- #endif
57
- }
58
-
59
- static int init_registry() {
60
- if (registry == NULL) {
61
- registry = (LQFTNode**)calloc(REGISTRY_SIZE, sizeof(LQFTNode*));
62
- if (registry == NULL) return 0;
63
- }
64
- return 1;
65
- }
66
-
67
- LQFTNode* create_node(void* value, uint64_t key_hash) {
68
- LQFTNode* node = (LQFTNode*)malloc(sizeof(LQFTNode));
69
- if (!node) return NULL;
70
- node->value = value;
71
- node->key_hash = key_hash;
72
- for (int i = 0; i < 32; i++) node->children[i] = NULL;
73
- return node;
74
- }
75
-
76
- LQFTNode* get_canonical(void* value, uint64_t key_hash, LQFTNode** children) {
77
- if (!init_registry()) return NULL;
78
-
79
- uint64_t full_hash = FNV_OFFSET_BASIS;
80
- if (value != NULL) {
81
- full_hash = fnv1a_update(full_hash, "leaf:", 5);
82
- full_hash = fnv1a_update(full_hash, value, strlen((char*)value));
83
- full_hash = fnv1a_update(full_hash, &key_hash, sizeof(uint64_t));
84
- } else {
85
- full_hash = fnv1a_update(full_hash, "branch:", 7);
86
- if (children) {
87
- for (int i = 0; i < 32; i++) {
88
- if (children[i]) {
89
- full_hash = fnv1a_update(full_hash, &i, sizeof(int));
90
- full_hash = fnv1a_update(full_hash, children[i]->struct_hash, 16);
91
- }
92
- }
93
- }
94
- }
95
-
96
- char lookup_hash[17];
97
- sprintf(lookup_hash, "%016llx", (unsigned long long)full_hash);
98
- uint32_t idx = full_hash % REGISTRY_SIZE;
99
- uint32_t start_idx = idx;
100
-
101
- while (registry[idx] != NULL) {
102
- if (strcmp(registry[idx]->struct_hash, lookup_hash) == 0) {
103
- if (value) free(value);
104
- return registry[idx];
105
- }
106
- idx = (idx + 1) % REGISTRY_SIZE;
107
- if (idx == start_idx) break;
108
- }
109
-
110
- LQFTNode* new_node = create_node(value, key_hash);
111
- if (!new_node) return NULL;
112
- if (children) {
113
- for (int i = 0; i < 32; i++) new_node->children[i] = children[i];
114
- }
115
- strcpy(new_node->struct_hash, lookup_hash);
116
- registry[idx] = new_node;
117
- physical_node_count++;
118
- return new_node;
119
- }
120
-
121
- static PyObject* method_delete(PyObject* self, PyObject* args) {
122
- unsigned long long h;
123
- if (!PyArg_ParseTuple(args, "K", &h)) return NULL;
124
- if (!global_root) Py_RETURN_NONE;
125
-
126
- LQFTNode* path_nodes[20];
127
- uint32_t path_segs[20];
128
- int path_len = 0;
129
- LQFTNode* curr = global_root;
130
- int bit_depth = 0;
131
-
132
- // Trace path to target
133
- while (curr != NULL && curr->value == NULL) {
134
- uint32_t segment = (h >> bit_depth) & MASK;
135
- path_nodes[path_len] = curr;
136
- path_segs[path_len] = segment;
137
- path_len++;
138
- curr = curr->children[segment];
139
- bit_depth += BIT_PARTITION;
140
- }
141
-
142
- // If not found, exit
143
- if (curr == NULL || curr->key_hash != h) Py_RETURN_NONE;
144
-
145
- // Rebuild bottom-up: start with NULL (the deleted leaf)
146
- LQFTNode* new_sub_node = NULL;
147
-
148
- for (int i = path_len - 1; i >= 0; i--) {
149
- LQFTNode* p_node = path_nodes[i];
150
- uint32_t segment = path_segs[i];
151
- LQFTNode* new_children[32];
152
- int has_other_children = 0;
153
-
154
- for (int j = 0; j < 32; j++) {
155
- if (j == segment) new_children[j] = new_sub_node;
156
- else {
157
- new_children[j] = p_node->children[j];
158
- if (new_children[j]) has_other_children = 1;
159
- }
160
- }
161
-
162
- if (!has_other_children && i > 0) {
163
- new_sub_node = NULL; // Contract empty branch
164
- } else {
165
- new_sub_node = get_canonical(NULL, 0, new_children);
166
- }
167
- }
168
-
169
- global_root = (new_sub_node) ? new_sub_node : get_canonical(NULL, 0, NULL);
170
- Py_RETURN_NONE;
171
- }
172
-
173
- static PyObject* method_free_all(PyObject* self, PyObject* args) {
174
- if (registry != NULL) {
175
- for (int i = 0; i < REGISTRY_SIZE; i++) {
176
- if (registry[i] != NULL) {
177
- if (registry[i]->value) free(registry[i]->value);
178
- free(registry[i]);
179
- registry[i] = NULL;
180
- }
181
- }
182
- free(registry);
183
- registry = NULL;
184
- }
185
- physical_node_count = 0;
186
- global_root = NULL;
187
- Py_RETURN_NONE;
188
- }
189
-
190
- static PyObject* method_insert(PyObject* self, PyObject* args) {
191
- unsigned long long h;
192
- char* val_str;
193
- if (!PyArg_ParseTuple(args, "Ks", &h, &val_str)) return NULL;
194
- if (!global_root) {
195
- if (!init_registry()) return PyErr_NoMemory();
196
- global_root = get_canonical(NULL, 0, NULL);
197
- }
198
- LQFTNode* path_nodes[20];
199
- uint32_t path_segs[20];
200
- int path_len = 0;
201
- LQFTNode* curr = global_root;
202
- int bit_depth = 0;
203
- while (curr != NULL && curr->value == NULL) {
204
- uint32_t segment = (h >> bit_depth) & MASK;
205
- path_nodes[path_len] = curr;
206
- path_segs[path_len] = segment;
207
- path_len++;
208
- if (curr->children[segment] == NULL) { curr = NULL; break; }
209
- curr = curr->children[segment];
210
- bit_depth += BIT_PARTITION;
211
- }
212
- LQFTNode* new_sub_node = NULL;
213
- if (curr == NULL) { new_sub_node = get_canonical(portable_strdup(val_str), h, NULL); }
214
- else if (curr->key_hash == h) { new_sub_node = get_canonical(portable_strdup(val_str), h, curr->children); }
215
- else {
216
- unsigned long long old_h = curr->key_hash;
217
- char* old_val = portable_strdup((char*)curr->value);
218
- int temp_depth = bit_depth;
219
- while (temp_depth < 64) {
220
- uint32_t s_old = (old_h >> temp_depth) & MASK;
221
- uint32_t s_new = (h >> temp_depth) & MASK;
222
- if (s_old != s_new) {
223
- LQFTNode* c_old = get_canonical(old_val, old_h, curr->children);
224
- LQFTNode* c_new = get_canonical(portable_strdup(val_str), h, NULL);
225
- LQFTNode* new_children[32] = {NULL};
226
- new_children[s_old] = c_old;
227
- new_children[s_new] = c_new;
228
- new_sub_node = get_canonical(NULL, 0, new_children);
229
- break;
230
- } else { path_nodes[path_len] = NULL; path_segs[path_len] = s_old; path_len++; temp_depth += BIT_PARTITION; }
231
- }
232
- if (new_sub_node == NULL) new_sub_node = get_canonical(portable_strdup(val_str), h, curr->children);
233
- }
234
- for (int i = path_len - 1; i >= 0; i--) {
235
- if (path_nodes[i] == NULL) {
236
- LQFTNode* new_children[32] = {NULL};
237
- new_children[path_segs[i]] = new_sub_node;
238
- new_sub_node = get_canonical(NULL, 0, new_children);
239
- } else {
240
- LQFTNode* p_node = path_nodes[i];
241
- uint32_t segment = path_segs[i];
242
- LQFTNode* new_children[32];
243
- for (int j = 0; j < 32; j++) new_children[j] = p_node->children[j];
244
- new_children[segment] = new_sub_node;
245
- new_sub_node = get_canonical(p_node->value, p_node->key_hash, new_children);
246
- }
247
- }
248
- global_root = new_sub_node;
249
- Py_RETURN_NONE;
250
- }
251
-
252
- static PyObject* method_search(PyObject* self, PyObject* args) {
253
- unsigned long long h;
254
- if (!PyArg_ParseTuple(args, "K", &h)) return NULL;
255
- if (!global_root) Py_RETURN_NONE;
256
- LQFTNode* curr = global_root;
257
- int bit_depth = 0;
258
- while (curr != NULL && curr->value == NULL) {
259
- uint32_t segment = (h >> bit_depth) & MASK;
260
- curr = curr->children[segment];
261
- bit_depth += BIT_PARTITION;
262
- }
263
- if (curr != NULL && curr->key_hash == h) return PyUnicode_FromString((char*)curr->value);
264
- Py_RETURN_NONE;
265
- }
266
-
267
- static PyObject* method_get_metrics(PyObject* self, PyObject* args) {
268
- return Py_BuildValue("{s:i}", "physical_nodes", physical_node_count);
269
- }
270
-
271
- static PyMethodDef LQFTMethods[] = {
272
- {"insert", method_insert, METH_VARARGS, "Insert key-value"},
273
- {"delete", method_delete, METH_VARARGS, "Delete key"},
274
- {"search", method_search, METH_VARARGS, "Search key"},
275
- {"get_metrics", method_get_metrics, METH_VARARGS, "Get engine stats"},
276
- {"free_all", method_free_all, METH_VARARGS, "Total memory wipe"},
277
- {NULL, NULL, 0, NULL}
278
- };
279
-
280
- static struct PyModuleDef lqftmodule = { PyModuleDef_HEAD_INIT, "lqft_c_engine", NULL, -1, LQFTMethods };
281
-
282
- PyMODINIT_FUNC PyInit_lqft_c_engine(void) { return PyModule_Create(&lqftmodule); }