Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1 +1,1511 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
PRACTICALITY UNIFIED SERVER 22.1 β LATEST GEMINI SDK & STRICT BOUNDARY
|
| 4 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 5 |
+
ARCHITECTURE:
|
| 6 |
+
User Text β [GEMINI 3.1 ENCODER] β AXLProblemDef (Strict Math Representation)
|
| 7 |
+
β [WISDOM LAYER] β Hypothesis Engine (CreativeSeeder) generates Axiom rays
|
| 8 |
+
β PyTorch Masked Deduction + Oracle solves topology
|
| 9 |
+
β Verification (Gate 1 & Gate 2)
|
| 10 |
+
β [Self-Correcting Feedback Loop if G2 fails]
|
| 11 |
+
β [GEMINI 3.1 COLLAPSER] β Domain-language explanation
|
| 12 |
+
|
| 13 |
+
LATEST UPDATES (22.1):
|
| 14 |
+
- Migrated to the new `google-genai` SDK.
|
| 15 |
+
- Implemented `gemini-3.1-pro-preview` with `ThinkingConfig` for maximum semantic routing.
|
| 16 |
+
- Top-level API key configuration for rapid deployment.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import os, time, random, math, threading, warnings, queue, json, textwrap
|
| 20 |
+
from functools import reduce
|
| 21 |
+
from typing import Optional, Callable, List, Dict, Tuple, Any, Set
|
| 22 |
+
from dataclasses import dataclass, field
|
| 23 |
+
from collections import defaultdict, deque
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
import sympy as sp
|
| 27 |
+
from sympy.parsing.sympy_parser import parse_expr
|
| 28 |
+
import torch
|
| 29 |
+
import gradio as gr
|
| 30 |
+
|
| 31 |
+
# ββ NEW GOOGLE GENAI SDK ββββββββββββββββββββββββββββββββββββββββββ
|
| 32 |
+
from google import genai
|
| 33 |
+
from google.genai import types
|
| 34 |
+
|
| 35 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 36 |
+
# AI CONFIGURATION (Set your key here!)
|
| 37 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 38 |
+
DEFAULT_GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
|
| 39 |
+
GEMINI_MODEL = "gemini-3.1-pro-preview"
|
| 40 |
+
|
| 41 |
+
warnings.filterwarnings("ignore")
|
| 42 |
+
USE_GPU = torch.cuda.is_available()
|
| 43 |
+
DEVICE = torch.device("cuda" if USE_GPU else "cpu")
|
| 44 |
+
print(f"[UNIFIED SERVER] Compute: {DEVICE.type.upper()} | System 22.1 (GenAI SDK + Neuro-Symbolic Boundary)")
|
| 45 |
+
|
| 46 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 47 |
+
# SECTION 1: CONSTANTS
|
| 48 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 49 |
+
SOLVE_THRESHOLD = 0.05
|
| 50 |
+
VERIFY_G1_THRESHOLD = 0.08
|
| 51 |
+
VERIFY_G2_TOLERANCE = 1e-3
|
| 52 |
+
DEDUCE_ADAM_STEPS = 25
|
| 53 |
+
N_MPRT_EXPLORE = 1000
|
| 54 |
+
PROJECTION_CACHE: Dict = {}
|
| 55 |
+
|
| 56 |
+
MAX_TOTAL_RAYS = 1_500_000
|
| 57 |
+
RAY_BATCH_SIZE = 12288
|
| 58 |
+
CE_EARN_RATIO = 0.90
|
| 59 |
+
SCOUT_CE_THRESHOLD = 0.5
|
| 60 |
+
BRANCH_WIDTH = 12
|
| 61 |
+
BRANCH_WIDTH_SYSTEMIC = 16
|
| 62 |
+
BATON_REGISTRY_THRESHOLD = 2.0
|
| 63 |
+
BATON_REGISTRY_MAX = 5000
|
| 64 |
+
|
| 65 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 66 |
+
# SECTION 2: INTERVAL ARITHMETIC (HC4)
|
| 67 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 68 |
+
class IV:
|
| 69 |
+
__slots__ = ("lo","hi")
|
| 70 |
+
def __init__(self,lo,hi): self.lo=float(lo); self.hi=float(hi)
|
| 71 |
+
def __add__(self,o): return IV(self.lo+o,self.hi+o) if isinstance(o,(int,float)) else IV(self.lo+o.lo,self.hi+o.hi)
|
| 72 |
+
__radd__=__add__
|
| 73 |
+
def __sub__(self,o): return IV(self.lo-o,self.hi-o) if isinstance(o,(int,float)) else IV(self.lo-o.hi,self.hi-o.lo)
|
| 74 |
+
def __rsub__(self,o): return IV(o-self.hi,o-self.lo) if isinstance(o,(int,float)) else o.__sub__(self)
|
| 75 |
+
def __mul__(self,o):
|
| 76 |
+
if isinstance(o,(int,float)): a,b=self.lo*o,self.hi*o; return IV(min(a,b),max(a,b))
|
| 77 |
+
p=(self.lo*o.lo,self.lo*o.hi,self.hi*o.lo,self.hi*o.hi); return IV(min(p),max(p))
|
| 78 |
+
__rmul__=__mul__
|
| 79 |
+
def __truediv__(self,o):
|
| 80 |
+
if isinstance(o,(int,float)):
|
| 81 |
+
if abs(o)<1e-15: return IV(-1e18,1e18)
|
| 82 |
+
a,b=self.lo/o,self.hi/o; return IV(min(a,b),max(a,b))
|
| 83 |
+
if o.lo<=0<=o.hi: return IV(-1e18,1e18)
|
| 84 |
+
return self*IV(1.0/o.hi,1.0/o.lo)
|
| 85 |
+
def __rtruediv__(self,o):
|
| 86 |
+
if self.lo<=0<=self.hi: return IV(-1e18,1e18)
|
| 87 |
+
return IV(o/self.hi,o/self.lo) if o>=0 else IV(o/self.lo,o/self.hi)
|
| 88 |
+
def __neg__(self): return IV(-self.hi,-self.lo)
|
| 89 |
+
def __pow__(self,n):
|
| 90 |
+
if isinstance(n,int):
|
| 91 |
+
if n==0: return IV(1.0,1.0)
|
| 92 |
+
if n%2==0:
|
| 93 |
+
if self.lo>=0: return IV(self.lo**n,self.hi**n)
|
| 94 |
+
if self.hi<=0: return IV(self.hi**n,self.lo**n)
|
| 95 |
+
return IV(0.0,max(abs(self.lo)**n,abs(self.hi)**n))
|
| 96 |
+
return IV(self.lo**n if self.lo>=0 else -((-self.lo)**n),
|
| 97 |
+
self.hi**n if self.hi>=0 else -((-self.hi)**n))
|
| 98 |
+
if self.lo<0: return IV(0.0,max(abs(self.lo)**n,self.hi**n))
|
| 99 |
+
return IV(self.lo**n,self.hi**n)
|
| 100 |
+
def contains_zero(self): return self.lo<=0.0<=self.hi
|
| 101 |
+
def width(self): return max(0.0,self.hi-self.lo)
|
| 102 |
+
def mid(self): return (self.lo+self.hi)*0.5
|
| 103 |
+
|
| 104 |
+
def compile_iv(expr,variables):
|
| 105 |
+
def _c(e):
|
| 106 |
+
if e.is_Number: v=float(e); return lambda box,_v=v: IV(_v,_v)
|
| 107 |
+
if e.is_Symbol: n=str(e); return lambda box,_n=n: box.get(_n,IV(-1e18,1e18))
|
| 108 |
+
if e.is_Add: fs=[_c(a) for a in e.args]; return lambda box,_fs=fs: reduce(lambda a,b:a+b,(_f(box) for _f in _fs))
|
| 109 |
+
if e.is_Mul: fs=[_c(a) for a in e.args]; return lambda box,_fs=fs: reduce(lambda a,b:a*b,(_f(box) for _f in _fs))
|
| 110 |
+
if e.is_Pow:
|
| 111 |
+
bc=_c(e.args[0]); ex=e.args[1]
|
| 112 |
+
if ex.is_Number: return lambda box,_bc=bc,_ex=float(ex): _bc(box)**_ex
|
| 113 |
+
exc=_c(ex); return lambda box,_bc=bc,_exc=exc: _bc(box)**_exc(box).mid()
|
| 114 |
+
return lambda box: IV(-1e18,1e18)
|
| 115 |
+
return _c(expr)
|
| 116 |
+
|
| 117 |
+
def _hc4(box,constraints):
|
| 118 |
+
cur=dict(box)
|
| 119 |
+
for mc in constraints:
|
| 120 |
+
if getattr(mc,'weight',1.0)==0.0: continue
|
| 121 |
+
if mc.kind=="or_eq":
|
| 122 |
+
valid=False
|
| 123 |
+
for bmc in mc.branches:
|
| 124 |
+
if bmc.fast_iv is None: valid=True; break
|
| 125 |
+
try:
|
| 126 |
+
if bmc.fast_iv(cur).contains_zero(): valid=True; break
|
| 127 |
+
except: valid=True; break
|
| 128 |
+
if not valid: return None
|
| 129 |
+
else:
|
| 130 |
+
if mc.fast_iv is None: continue
|
| 131 |
+
try:
|
| 132 |
+
riv=mc.fast_iv(cur)
|
| 133 |
+
if ((mc.kind=="equality" and not riv.contains_zero()) or
|
| 134 |
+
(mc.kind=="inequality" and ((mc.direction=="geq" and riv.hi<-1e-10) or
|
| 135 |
+
(mc.direction=="leq" and riv.lo>1e-10)))): return None
|
| 136 |
+
except: pass
|
| 137 |
+
return cur
|
| 138 |
+
|
| 139 |
+
def _global_hc4_tighten_bounds(problem):
|
| 140 |
+
gb={v:IV(lo,hi) for v,(lo,hi) in problem.bounds.items()}
|
| 141 |
+
c=_hc4(gb,problem.compiled_constraints)
|
| 142 |
+
if c is None: return dict(problem.bounds)
|
| 143 |
+
return {v:(max(problem.bounds[v][0],c[v].lo),min(problem.bounds[v][1],c[v].hi))
|
| 144 |
+
for v in problem.bounds if v in c}
|
| 145 |
+
|
| 146 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 147 |
+
# SECTION 3: PSL PARSER & PRE-COMPILATION
|
| 148 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 149 |
+
@dataclass
|
| 150 |
+
class PSLVar: name:str; lo:float; hi:float
|
| 151 |
+
@dataclass
|
| 152 |
+
class PSLConstraint: kind:str; expr:str; scope:str="root"; weight:float=1.0; branches:List[str]=field(default_factory=list)
|
| 153 |
+
@dataclass
|
| 154 |
+
class PSLScope: name:str; order:int=0; depends:List[str]=field(default_factory=list); vars:List[PSLVar]=field(default_factory=list); links:List[str]=field(default_factory=list); constraints:List[PSLConstraint]=field(default_factory=list)
|
| 155 |
+
@dataclass
|
| 156 |
+
class PSLProgram: name:str; vars:List[PSLVar]; constraints:List[PSLConstraint]; scopes:List[PSLScope]; scope_order:List[str]; annotations:Dict[str,str]=field(default_factory=dict)
|
| 157 |
+
|
| 158 |
+
def parse_psl(text:str) -> PSLProgram:
|
| 159 |
+
lines=[l.strip() for l in text.strip().split('\n') if l.strip() and not l.strip().startswith('#')]
|
| 160 |
+
prog=PSLProgram("unnamed",[],[],[],[]); i=0
|
| 161 |
+
def advance(): nonlocal i; l=lines[i]; i+=1; return l
|
| 162 |
+
def peek(): return lines[i] if i<len(lines) else ""
|
| 163 |
+
def pvar(rest): p=rest.split(); return PSLVar(p[0],float(p[1]),float(p[2])) if len(p)>=3 else None
|
| 164 |
+
def pw(rest): return (rest.split('WEIGHT')[0].strip(),float(rest.split('WEIGHT')[1].split()[0])) if 'WEIGHT' in rest else (rest.strip(),1.0)
|
| 165 |
+
def parse_scope_body(sname,order=0,depends=None):
|
| 166 |
+
svars,scons,links=[],[],[]
|
| 167 |
+
while i<len(lines):
|
| 168 |
+
l=peek(); tk=l.split(None,1); kw=tk[0].upper() if tk else ""; rest=tk[1].strip() if len(tk)>1 else ""
|
| 169 |
+
if kw in ("END","END SCOPE"): advance(); break
|
| 170 |
+
advance()
|
| 171 |
+
if kw=="VAR": v=pvar(rest); svars.append(v) if v else None
|
| 172 |
+
elif kw=="LINK": links.extend(rest.split())
|
| 173 |
+
elif kw in ("EQ","GEQ","LEQ"): expr,wt=pw(rest); scons.append(PSLConstraint(kw.lower(),expr,scope=sname,weight=wt))
|
| 174 |
+
elif kw=="CONSERVE": expr,_=pw(rest); scons.append(PSLConstraint("eq",expr,scope=sname,weight=10.0))
|
| 175 |
+
elif kw=="BRANCH":
|
| 176 |
+
opts=[]
|
| 177 |
+
while i<len(lines) and not peek().upper().startswith("END"):
|
| 178 |
+
bl=advance(); opts.append(bl.split(None,1)[1].strip()) if bl.upper().startswith("OPTION") else None
|
| 179 |
+
advance(); scons.append(PSLConstraint("or_eq","",scope=sname,weight=1.0,branches=opts))
|
| 180 |
+
return PSLScope(sname,order,depends or [],svars,links,scons)
|
| 181 |
+
while i<len(lines):
|
| 182 |
+
l=advance(); tk=l.split(None,1); kw=tk[0].upper() if tk else ""; rest=tk[1].strip() if len(tk)>1 else ""
|
| 183 |
+
if kw=="SPACE": prog.name=rest.split()[0] if rest else "unnamed"
|
| 184 |
+
elif kw=="VAR": v=pvar(rest); prog.vars.append(v) if v else None
|
| 185 |
+
elif kw in ("EQ","GEQ","LEQ"): expr,wt=pw(rest); prog.constraints.append(PSLConstraint(kw.lower(),expr,weight=wt))
|
| 186 |
+
elif kw=="CONSERVE": expr,_=pw(rest); prog.constraints.append(PSLConstraint("eq",expr,weight=10.0))
|
| 187 |
+
elif kw=="BRANCH":
|
| 188 |
+
opts=[]
|
| 189 |
+
while i<len(lines) and not peek().upper().startswith("END"):
|
| 190 |
+
bl=advance(); opts.append(bl.split(None,1)[1].strip()) if bl.upper().startswith("OPTION") else None
|
| 191 |
+
advance(); prog.constraints.append(PSLConstraint("or_eq","",weight=1.0,branches=opts))
|
| 192 |
+
elif kw=="SCOPE":
|
| 193 |
+
parts=rest.split(); sname=parts[0] if parts else f"s{len(prog.scopes)}"; order=0; depends=[]
|
| 194 |
+
if "ORDER" in rest.upper():
|
| 195 |
+
try: order=int(rest[rest.upper().index("ORDER"):].split()[1])
|
| 196 |
+
except: pass
|
| 197 |
+
if "DEPENDS" in rest.upper(): depends=rest[rest.upper().index("DEPENDS"):].split()[1:]
|
| 198 |
+
sc=parse_scope_body(sname,order,depends); prog.scopes.append(sc); prog.scope_order.append(sname)
|
| 199 |
+
prog.scopes.sort(key=lambda s:s.order); prog.scope_order=[s.name for s in prog.scopes]
|
| 200 |
+
return prog
|
| 201 |
+
|
| 202 |
+
@dataclass
|
| 203 |
+
class ExpandedProblem:
|
| 204 |
+
variables:List[str]; bounds:Dict[str,Tuple[float,float]]; constraints:List['Constraint']
|
| 205 |
+
scope_groups:Dict[str,List[int]]; scope_vars:Dict[str,List[str]]; scope_order:List[str]
|
| 206 |
+
|
| 207 |
+
def expand_psl(prog:PSLProgram) -> ExpandedProblem:
|
| 208 |
+
variables=[]; bounds={}; constraints=[]; scope_groups=defaultdict(list); scope_vars=defaultdict(list)
|
| 209 |
+
def add_var(name,lo,hi,scope="root"):
|
| 210 |
+
if name not in bounds: variables.append(name); bounds[name]=(lo,hi)
|
| 211 |
+
if scope!="root" and name not in scope_vars[scope]: scope_vars[scope].append(name)
|
| 212 |
+
def add_con(kind,expr,direction,scope="root",weight=1.0,branches=None):
|
| 213 |
+
constraints.append(Constraint(kind,expr,direction,weight,scope,branches or []))
|
| 214 |
+
scope_groups[scope].append(len(constraints)-1)
|
| 215 |
+
try:
|
| 216 |
+
syms={v:sp.Symbol(v) for v in variables}
|
| 217 |
+
parsed=parse_expr(expr,local_dict=syms) if kind!="or_eq" else None
|
| 218 |
+
if parsed:
|
| 219 |
+
for v in variables:
|
| 220 |
+
if sp.Symbol(v) in parsed.free_symbols and scope!="root" and v not in scope_vars[scope]:
|
| 221 |
+
scope_vars[scope].append(v)
|
| 222 |
+
except: pass
|
| 223 |
+
for v in prog.vars: add_var(v.name,v.lo,v.hi)
|
| 224 |
+
for c in prog.constraints:
|
| 225 |
+
add_con("or_eq" if c.kind=="or_eq" else "equality" if c.kind=="eq" else "inequality",
|
| 226 |
+
c.expr,c.kind,"root",c.weight,c.branches)
|
| 227 |
+
for sc in prog.scopes:
|
| 228 |
+
for v in sc.vars: add_var(v.name,v.lo,v.hi,sc.name)
|
| 229 |
+
for c in sc.constraints:
|
| 230 |
+
add_con("or_eq" if c.kind=="or_eq" else "equality" if c.kind=="eq" else "inequality",
|
| 231 |
+
c.expr,c.kind,sc.name,c.weight,c.branches)
|
| 232 |
+
return ExpandedProblem(variables,bounds,constraints,dict(scope_groups),dict(scope_vars),prog.scope_order)
|
| 233 |
+
|
| 234 |
+
@dataclass
|
| 235 |
+
class AXLInvariant:
|
| 236 |
+
name:str; expr:str; tolerance:float=VERIFY_G2_TOLERANCE; mode:str="eq"
|
| 237 |
+
compiled_func: Optional[Callable] = field(default=None, repr=False)
|
| 238 |
+
syms_used: List[str] = field(default_factory=list)
|
| 239 |
+
|
| 240 |
+
def compile(self, variables: List[str]):
|
| 241 |
+
syms = {v: sp.Symbol(v) for v in variables}
|
| 242 |
+
parsed = parse_expr(self.expr, local_dict=syms)
|
| 243 |
+
self.syms_used = [str(s) for s in parsed.free_symbols if str(s) in variables]
|
| 244 |
+
self.compiled_func = sp.lambdify([sp.Symbol(v) for v in self.syms_used], parsed, modules="math")
|
| 245 |
+
|
| 246 |
+
@dataclass
|
| 247 |
+
class AXLProblemDef:
|
| 248 |
+
name:str; description:str; axioms:Set[str]; variables:List[Dict]; constraints:List[Dict]
|
| 249 |
+
scopes:List[Dict]; anchors:List[AXLInvariant]; observations:Dict[str,float]=field(default_factory=dict)
|
| 250 |
+
|
| 251 |
+
class AXLtoPSLCompiler:
|
| 252 |
+
def compile(self,prob:AXLProblemDef) -> str:
|
| 253 |
+
lines=[f"SPACE base_{prob.name}"]
|
| 254 |
+
for v in prob.variables:
|
| 255 |
+
lo,hi=v["lo"],v["hi"]
|
| 256 |
+
if v["name"] in prob.observations: val=prob.observations[v["name"]]; lo=hi=val
|
| 257 |
+
lines.append(f"VAR {v['name']} {lo} {hi}")
|
| 258 |
+
for c in prob.constraints:
|
| 259 |
+
wt=c.get("weight",1.0)
|
| 260 |
+
if c["kind"]=="CONSERVE": lines.append(f"CONSERVE {c['expr']}")
|
| 261 |
+
elif c["kind"]=="BRANCH":
|
| 262 |
+
lines.append(f"BRANCH {c.get('name','br')}")
|
| 263 |
+
for opt in c.get("options",[]): lines.append(f" OPTION {opt}")
|
| 264 |
+
lines.append("END BRANCH")
|
| 265 |
+
else: lines.append(f"{c['kind']} {c['expr']} WEIGHT {wt}")
|
| 266 |
+
for sc in prob.scopes:
|
| 267 |
+
lines.append(f"SCOPE {sc['name']} ORDER {sc.get('order',0)}")
|
| 268 |
+
for c in sc.get("constraints",[]):
|
| 269 |
+
lines.append(f" {c['kind']} {c['expr']} WEIGHT {c.get('weight',1.0)}")
|
| 270 |
+
lines.append("END")
|
| 271 |
+
return "\n".join(lines)
|
| 272 |
+
|
| 273 |
+
AXL_COMPILER=AXLtoPSLCompiler()
|
| 274 |
+
|
| 275 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 276 |
+
# SECTION 4: PROBLEM COMPILATION
|
| 277 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 278 |
+
@dataclass
|
| 279 |
+
class Constraint:
|
| 280 |
+
kind:str; expr:str; direction:str; weight:float=1.0; scope:str="root"
|
| 281 |
+
branches:List[str]=field(default_factory=list)
|
| 282 |
+
|
| 283 |
+
@dataclass
|
| 284 |
+
class MathConstraint:
|
| 285 |
+
kind:str; expr_str:str; direction:str; weight:float=1.0
|
| 286 |
+
fast_iv:Optional[Callable]=field(default=None,repr=False)
|
| 287 |
+
fast_pt:Optional[Callable]=field(default=None,repr=False)
|
| 288 |
+
torch_func:Optional[Callable]=field(default=None,repr=False)
|
| 289 |
+
syms_used:List[str]=field(default_factory=list)
|
| 290 |
+
parsed:Optional[sp.Expr]=field(default=None,repr=False)
|
| 291 |
+
scope:str="root"
|
| 292 |
+
branches:List['MathConstraint']=field(default_factory=list)
|
| 293 |
+
projections:Dict[str,List[Dict]]=field(default_factory=dict)
|
| 294 |
+
|
| 295 |
+
def compile_mc(kind,expr_str,direction,variables,weight=1.0,scope="root",branches=None):
|
| 296 |
+
mc=MathConstraint(kind=kind,expr_str=expr_str,direction=direction,weight=weight,scope=scope)
|
| 297 |
+
if kind=="or_eq" and branches:
|
| 298 |
+
for b_str in branches:
|
| 299 |
+
b_mc=compile_mc("equality",b_str,"eq",variables,weight,scope)
|
| 300 |
+
mc.branches.append(b_mc); mc.syms_used.extend(b_mc.syms_used)
|
| 301 |
+
mc.syms_used=list(dict.fromkeys(mc.syms_used))
|
| 302 |
+
def _or_iv(box,_mcs=mc.branches):
|
| 303 |
+
rivs=[]
|
| 304 |
+
for b in _mcs:
|
| 305 |
+
if b.fast_iv:
|
| 306 |
+
try: rivs.append(b.fast_iv(box))
|
| 307 |
+
except: pass
|
| 308 |
+
if not rivs: return IV(-1e18,1e18)
|
| 309 |
+
return IV(min(r.lo for r in rivs),max(r.hi for r in rivs))
|
| 310 |
+
mc.fast_iv=_or_iv; return mc
|
| 311 |
+
syms={v:sp.Symbol(v) for v in variables}
|
| 312 |
+
try:
|
| 313 |
+
parsed=parse_expr(expr_str,local_dict=syms); mc.parsed=parsed
|
| 314 |
+
mc.syms_used=[v for v in variables if sp.Symbol(v) in parsed.free_symbols]
|
| 315 |
+
mc.fast_iv=compile_iv(parsed,variables)
|
| 316 |
+
mc.fast_pt=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules="math")
|
| 317 |
+
pt_map={'sin':torch.sin,'cos':torch.cos,'exp':torch.exp,'sqrt':torch.sqrt,'Abs':torch.abs}
|
| 318 |
+
t_func_raw=sp.lambdify([sp.Symbol(v) for v in mc.syms_used],parsed,modules=[pt_map,"math"])
|
| 319 |
+
def _t_wrapper(*args):
|
| 320 |
+
val=t_func_raw(*args)
|
| 321 |
+
return torch.tensor(val,device=DEVICE,dtype=torch.float32) if isinstance(val,(int,float)) else val
|
| 322 |
+
mc.torch_func=_t_wrapper
|
| 323 |
+
if kind=="equality":
|
| 324 |
+
if expr_str not in PROJECTION_CACHE:
|
| 325 |
+
pm={}
|
| 326 |
+
for sym in parsed.free_symbols:
|
| 327 |
+
v_str=str(sym)
|
| 328 |
+
try:
|
| 329 |
+
sols=sp.solve(parsed,sym); pm[v_str]=[]
|
| 330 |
+
for sol in sols:
|
| 331 |
+
fs=list(sol.free_symbols)
|
| 332 |
+
pm[v_str].append({"syms":[str(s) for s in fs],
|
| 333 |
+
"func":sp.lambdify(fs,sol,modules="math")})
|
| 334 |
+
except: pass
|
| 335 |
+
PROJECTION_CACHE[expr_str]=pm
|
| 336 |
+
mc.projections=PROJECTION_CACHE.get(expr_str,{})
|
| 337 |
+
except: pass
|
| 338 |
+
return mc
|
| 339 |
+
|
| 340 |
+
def _extract_ordering_pair(mc: MathConstraint, variables: List[str]) -> Optional[Tuple[str,str]]:
|
| 341 |
+
if mc.kind != "inequality" or mc.direction != "geq": return None
|
| 342 |
+
if mc.parsed is None: return None
|
| 343 |
+
parsed = mc.parsed
|
| 344 |
+
free = [str(s) for s in parsed.free_symbols if str(s) in variables]
|
| 345 |
+
if len(free) != 2: return None
|
| 346 |
+
v0, v1 = free[0], free[1]
|
| 347 |
+
try:
|
| 348 |
+
c0 = float(parsed.coeff(sp.Symbol(v0)))
|
| 349 |
+
c1 = float(parsed.coeff(sp.Symbol(v1)))
|
| 350 |
+
except Exception:
|
| 351 |
+
return None
|
| 352 |
+
if c0 > 0 and c1 < 0: return (v1, v0)
|
| 353 |
+
elif c1 > 0 and c0 < 0: return (v0, v1)
|
| 354 |
+
return None
|
| 355 |
+
|
| 356 |
+
@dataclass
|
| 357 |
+
class Problem:
|
| 358 |
+
pid:str; variables:List[str]; constraints:List[Constraint]
|
| 359 |
+
bounds:Dict[str,Tuple[float,float]]
|
| 360 |
+
scope_groups:Dict[str,List[int]]=field(default_factory=dict)
|
| 361 |
+
scope_vars:Dict[str,List[str]]=field(default_factory=dict)
|
| 362 |
+
scope_order:List[str]=field(default_factory=list)
|
| 363 |
+
|
| 364 |
+
bilinear_pairs: List[Tuple[str,str]] = field(default_factory=list)
|
| 365 |
+
monotone_targets: List[Tuple[str,str,float]] = field(default_factory=list)
|
| 366 |
+
ordering_pairs: List[Tuple[str,str]] = field(default_factory=list)
|
| 367 |
+
|
| 368 |
+
def __post_init__(self):
|
| 369 |
+
self.compiled_constraints=[
|
| 370 |
+
compile_mc(c.kind,c.expr,c.direction,self.variables,c.weight,c.scope,c.branches)
|
| 371 |
+
for c in self.constraints]
|
| 372 |
+
self.var_idx={v:i for i,v in enumerate(self.variables)}
|
| 373 |
+
|
| 374 |
+
for mc in self.compiled_constraints:
|
| 375 |
+
pair = _extract_ordering_pair(mc, self.variables)
|
| 376 |
+
if pair and pair not in self.ordering_pairs:
|
| 377 |
+
self.ordering_pairs.append(pair)
|
| 378 |
+
|
| 379 |
+
for mc in self.compiled_constraints:
|
| 380 |
+
if mc.parsed and mc.parsed.is_Add:
|
| 381 |
+
for term in mc.parsed.args:
|
| 382 |
+
if term.is_Mul:
|
| 383 |
+
syms_in = [str(s) for s in term.free_symbols if str(s) in self.variables]
|
| 384 |
+
if len(syms_in) == 2:
|
| 385 |
+
va, vb = syms_in
|
| 386 |
+
if (va, vb) not in self.bilinear_pairs and (vb, va) not in self.bilinear_pairs:
|
| 387 |
+
self.bilinear_pairs.append((va, vb))
|
| 388 |
+
|
| 389 |
+
for mc in self.compiled_constraints:
|
| 390 |
+
if mc.kind == "equality" and mc.parsed is not None:
|
| 391 |
+
for va, vb in self.bilinear_pairs:
|
| 392 |
+
sa, sb = sp.Symbol(va), sp.Symbol(vb)
|
| 393 |
+
if sa in mc.parsed.free_symbols and sb in mc.parsed.free_symbols:
|
| 394 |
+
try:
|
| 395 |
+
k_expr = -(mc.parsed - sa*sb)
|
| 396 |
+
k = float(k_expr.evalf())
|
| 397 |
+
if k > 0:
|
| 398 |
+
for vs, vl in self.ordering_pairs:
|
| 399 |
+
if set([va, vb]) == set([vs, vl]):
|
| 400 |
+
target = (vs, vl, k)
|
| 401 |
+
if target not in self.monotone_targets:
|
| 402 |
+
self.monotone_targets.append(target)
|
| 403 |
+
except Exception:
|
| 404 |
+
pass
|
| 405 |
+
|
| 406 |
+
def tensor_energy(self,X:torch.Tensor) -> torch.Tensor:
|
| 407 |
+
is_batched=(X.dim()==2)
|
| 408 |
+
total=torch.zeros(X.shape[0] if is_batched else 1,device=DEVICE,dtype=torch.float32)
|
| 409 |
+
for mc in self.compiled_constraints:
|
| 410 |
+
if getattr(mc,'weight',1.0)==0.0: continue
|
| 411 |
+
if mc.kind=="or_eq":
|
| 412 |
+
b_vals=[]
|
| 413 |
+
for bmc in mc.branches:
|
| 414 |
+
if bmc.torch_func:
|
| 415 |
+
args=[X[:,self.var_idx[v]] if is_batched else X[self.var_idx[v]] for v in bmc.syms_used]
|
| 416 |
+
b_vals.append(torch.abs(bmc.torch_func(*args)))
|
| 417 |
+
if b_vals: total+=torch.stack(b_vals,dim=0).min(dim=0)[0]*mc.weight
|
| 418 |
+
else:
|
| 419 |
+
if mc.torch_func is None: continue
|
| 420 |
+
args=[X[:,self.var_idx[v]] if is_batched else X[self.var_idx[v]] for v in mc.syms_used]
|
| 421 |
+
val=mc.torch_func(*args)
|
| 422 |
+
if mc.kind=="equality": total+=torch.abs(val)*mc.weight
|
| 423 |
+
elif mc.direction=="geq": total+=torch.relu(-val)*mc.weight
|
| 424 |
+
else: total+=torch.relu(val)*mc.weight
|
| 425 |
+
for i,v in enumerate(self.variables):
|
| 426 |
+
lo,hi=self.bounds[v]
|
| 427 |
+
col=X[:,i] if is_batched else X[i]
|
| 428 |
+
total+=torch.relu(lo-col)+torch.relu(col-hi)
|
| 429 |
+
return total.squeeze()
|
| 430 |
+
|
| 431 |
+
def scalar_energy(self,b:Dict[str,float]) -> float:
|
| 432 |
+
total=0.0
|
| 433 |
+
for mc in self.compiled_constraints:
|
| 434 |
+
if getattr(mc,'weight',1.0)==0.0: continue
|
| 435 |
+
if mc.kind=="or_eq":
|
| 436 |
+
vals=[]
|
| 437 |
+
for bmc in mc.branches:
|
| 438 |
+
if bmc.fast_pt:
|
| 439 |
+
try: vals.append(abs(float(bmc.fast_pt(*[b.get(v,0.0) for v in bmc.syms_used]))))
|
| 440 |
+
except: pass
|
| 441 |
+
total+=(min(vals)*mc.weight) if vals else 1.0
|
| 442 |
+
else:
|
| 443 |
+
if mc.fast_pt is None: total+=1.0; continue
|
| 444 |
+
try:
|
| 445 |
+
val=float(mc.fast_pt(*[b.get(v,0.0) for v in mc.syms_used]))
|
| 446 |
+
if mc.kind=="equality": total+=abs(val)*mc.weight
|
| 447 |
+
elif mc.direction=="geq": total+=max(0.0,-val)*mc.weight
|
| 448 |
+
else: total+=max(0.0,val)*mc.weight
|
| 449 |
+
except: total+=1.0
|
| 450 |
+
for v,(lo,hi) in self.bounds.items():
|
| 451 |
+
bv=b.get(v,0.0); total+=max(0.0,lo-bv)+max(0.0,bv-hi)
|
| 452 |
+
return total
|
| 453 |
+
|
| 454 |
+
def build_base_problem(axl_def:AXLProblemDef) -> Problem:
|
| 455 |
+
psl_text=AXL_COMPILER.compile(axl_def)
|
| 456 |
+
prog=parse_psl(psl_text); ep=expand_psl(prog)
|
| 457 |
+
return Problem(pid=f"base_{axl_def.name}",variables=ep.variables,
|
| 458 |
+
constraints=ep.constraints,bounds=ep.bounds,
|
| 459 |
+
scope_groups=ep.scope_groups,scope_vars=ep.scope_vars,
|
| 460 |
+
scope_order=ep.scope_order)
|
| 461 |
+
|
| 462 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 463 |
+
# SECTION 5: BATCHED MASKED DEDUCTION & ORACLE
|
| 464 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 465 |
+
@dataclass
|
| 466 |
+
class L9Certificate:
|
| 467 |
+
residual_ce:float; dominant_vars:List[str]; dominant_exprs:List[str]
|
| 468 |
+
tension_class:str="unknown"
|
| 469 |
+
|
| 470 |
+
def _batched_deduce_and_evaluate(
|
| 471 |
+
problem:Problem,
|
| 472 |
+
hyps:List['Hypothesis']) -> List[Tuple[Dict,float,List[str],str]]:
|
| 473 |
+
if not hyps: return []
|
| 474 |
+
B=len(hyps); V=len(problem.variables)
|
| 475 |
+
|
| 476 |
+
x_data = []
|
| 477 |
+
mask_data = []
|
| 478 |
+
target_data = []
|
| 479 |
+
for hyp in hyps:
|
| 480 |
+
xr, mr, tr = [], [], []
|
| 481 |
+
for v in problem.variables:
|
| 482 |
+
if v in hyp.pinned_vars:
|
| 483 |
+
val = hyp.pinned_vars[v]
|
| 484 |
+
xr.append(val); mr.append(0.0); tr.append(val)
|
| 485 |
+
else:
|
| 486 |
+
val = hyp.binding.get(v, (problem.bounds[v][0]+problem.bounds[v][1])/2)
|
| 487 |
+
xr.append(val); mr.append(1.0); tr.append(0.0)
|
| 488 |
+
x_data.append(xr); mask_data.append(mr); target_data.append(tr)
|
| 489 |
+
|
| 490 |
+
X = torch.tensor(x_data, device=DEVICE, dtype=torch.float32, requires_grad=True)
|
| 491 |
+
mask = torch.tensor(mask_data, device=DEVICE, dtype=torch.float32)
|
| 492 |
+
target = torch.tensor(target_data, device=DEVICE, dtype=torch.float32)
|
| 493 |
+
|
| 494 |
+
optimizer=torch.optim.Adam([X],lr=0.05)
|
| 495 |
+
|
| 496 |
+
for _ in range(DEDUCE_ADAM_STEPS):
|
| 497 |
+
optimizer.zero_grad()
|
| 498 |
+
ce=problem.tensor_energy(X)
|
| 499 |
+
if isinstance(ce,torch.Tensor) and (ce<SOLVE_THRESHOLD).all(): break
|
| 500 |
+
loss=ce.sum(); loss.backward()
|
| 501 |
+
with torch.no_grad():
|
| 502 |
+
X.grad*=mask
|
| 503 |
+
optimizer.step()
|
| 504 |
+
X.data=torch.where(mask==0.0,target,X.data)
|
| 505 |
+
for j,v in enumerate(problem.variables):
|
| 506 |
+
lo,hi=problem.bounds[v]
|
| 507 |
+
X.data[:,j]=torch.clamp(X.data[:,j],lo,hi)
|
| 508 |
+
|
| 509 |
+
optimizer.zero_grad()
|
| 510 |
+
final_ce=problem.tensor_energy(X)
|
| 511 |
+
final_ce.sum().backward()
|
| 512 |
+
|
| 513 |
+
ce_vals = final_ce.detach().cpu().numpy()
|
| 514 |
+
X_vals = X.detach().cpu().numpy()
|
| 515 |
+
|
| 516 |
+
grads_abs = X.grad.abs()
|
| 517 |
+
_, topk_idx = torch.topk(grads_abs, k=min(3, V), dim=1)
|
| 518 |
+
topk_idx_np = topk_idx.cpu().numpy()
|
| 519 |
+
grads_np = grads_abs.cpu().numpy()
|
| 520 |
+
|
| 521 |
+
results=[]
|
| 522 |
+
for i in range(B):
|
| 523 |
+
final_b={problem.variables[j]:float(X_vals[i,j]) for j in range(V)}
|
| 524 |
+
dom_vars = [problem.variables[idx] for idx in topk_idx_np[i] if float(grads_np[i, idx]) > 1e-4]
|
| 525 |
+
n_dom = len(dom_vars)
|
| 526 |
+
if n_dom<=1: tension_class="isolated"
|
| 527 |
+
elif n_dom==2: tension_class="relational"
|
| 528 |
+
else: tension_class="systemic"
|
| 529 |
+
results.append((final_b,float(ce_vals[i]),dom_vars,tension_class))
|
| 530 |
+
return results
|
| 531 |
+
|
| 532 |
+
def _mprt_sample(problem,work_box,N):
|
| 533 |
+
var_list=problem.variables; V=len(var_list)
|
| 534 |
+
lo_t=torch.tensor([max(problem.bounds.get(v,(-10.0,10.0))[0],
|
| 535 |
+
work_box[v].lo if v in work_box else problem.bounds.get(v,(-10,10))[0])
|
| 536 |
+
for v in var_list],device=DEVICE,dtype=torch.float32)
|
| 537 |
+
hi_t=torch.tensor([min(problem.bounds.get(v,(-10.0,10.0))[1],
|
| 538 |
+
work_box[v].hi if v in work_box else problem.bounds.get(v,(-10,10))[1])
|
| 539 |
+
for v in var_list],device=DEVICE,dtype=torch.float32)
|
| 540 |
+
for i in range(V):
|
| 541 |
+
if lo_t[i]>=hi_t[i]: m=(lo_t[i]+hi_t[i])/2; lo_t[i]=m-1e-6; hi_t[i]=m+1e-6
|
| 542 |
+
X=lo_t.unsqueeze(0)+(hi_t-lo_t).unsqueeze(0)*torch.rand((N,V),device=DEVICE)
|
| 543 |
+
ce_batch=problem.tensor_energy(X)
|
| 544 |
+
best_idx=torch.argmin(ce_batch).item()
|
| 545 |
+
return {v:float(X[best_idx,i].item()) for i,v in enumerate(var_list)},ce_batch[best_idx].item()
|
| 546 |
+
|
| 547 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 548 |
+
# SECTION 6: AXIOMS
|
| 549 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 550 |
+
class Axiom:
|
| 551 |
+
CONTINUOUS="CONTINUOUS"; DISCRETE="DISCRETE"; QUADRATIC="QUADRATIC"
|
| 552 |
+
BILINEAR="BILINEAR"; METRIC="METRIC"; SYMMETRIC="SYMMETRIC"
|
| 553 |
+
ORDERED="ORDERED"; MONOTONE="MONOTONE"; CONVEX="CONVEX"
|
| 554 |
+
MONOTONE_PRODUCT="MONOTONE_PRODUCT"
|
| 555 |
+
CONSERVED="CONSERVED"; MUTABLE="MUTABLE"; NETWORK="NETWORK"
|
| 556 |
+
EQUILIBRIUM="EQUILIBRIUM"; SUPERPOSITION="SUPERPOSITION"
|
| 557 |
+
LOCALITY="LOCALITY"; EXTREMAL="EXTREMAL"; ENTROPY="ENTROPY"
|
| 558 |
+
INJECTIVE="INJECTIVE"; SURJECTIVE="SURJECTIVE"; TRANSITIVE="TRANSITIVE"
|
| 559 |
+
ATOMIC="ATOMIC"; IMPLICATION="IMPLICATION"; NEGATION="NEGATION"
|
| 560 |
+
COMPOSITE="COMPOSITE"; HOLISM="HOLISM"; PARSIMONY="PARSIMONY"; DUALITY="DUALITY"
|
| 561 |
+
|
| 562 |
+
ALL_AXIOMS=[getattr(Axiom,a) for a in dir(Axiom) if not a.startswith("__")]
|
| 563 |
+
AXIOM_ABBR={a:a[:3] for a in ALL_AXIOMS}; AXIOM_ABBR[Axiom.MONOTONE_PRODUCT]="MPR"
|
| 564 |
+
|
| 565 |
+
ADJACENT_CONTRADICTIONS:List[Tuple[str,str]]=[
|
| 566 |
+
(Axiom.DISCRETE,Axiom.CONTINUOUS),(Axiom.INJECTIVE,Axiom.SURJECTIVE),
|
| 567 |
+
(Axiom.ORDERED,Axiom.SYMMETRIC),(Axiom.ATOMIC,Axiom.COMPOSITE),
|
| 568 |
+
(Axiom.CONSERVED,Axiom.MUTABLE),(Axiom.PARSIMONY,Axiom.ENTROPY),
|
| 569 |
+
(Axiom.HOLISM,Axiom.LOCALITY),(Axiom.EXTREMAL,Axiom.ENTROPY),
|
| 570 |
+
(Axiom.NEGATION,Axiom.IMPLICATION),(Axiom.MONOTONE_PRODUCT,Axiom.SYMMETRIC),
|
| 571 |
+
]
|
| 572 |
+
_CONTRA_SET={frozenset(p) for p in ADJACENT_CONTRADICTIONS}
|
| 573 |
+
|
| 574 |
+
def sequence_valid(seq:List[str]) -> bool:
|
| 575 |
+
for i in range(len(seq)-1):
|
| 576 |
+
if seq[i]==seq[i+1]: return False
|
| 577 |
+
if frozenset([seq[i],seq[i+1]]) in _CONTRA_SET: return False
|
| 578 |
+
return True
|
| 579 |
+
|
| 580 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 581 |
+
# SECTION 7: SHARED DATACLASSES
|
| 582 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 583 |
+
RAY_TYPES=["seed","branch","tension","scout","recombine","invert","target"]
|
| 584 |
+
|
| 585 |
+
@dataclass
|
| 586 |
+
class Hypothesis:
|
| 587 |
+
hid:str; binding:Dict[str,float]; h_type:str; claim:str
|
| 588 |
+
derivation:List[str]; pinned_vars:Dict[str,float]; free_vars:List[str]
|
| 589 |
+
confidence:float; ce:float=float('inf')
|
| 590 |
+
|
| 591 |
+
@dataclass
|
| 592 |
+
class Baton:
|
| 593 |
+
binding:Dict[str,float]; ce:float
|
| 594 |
+
ray_id:str=""; depth:int=0; l9:Optional[L9Certificate]=None
|
| 595 |
+
|
| 596 |
+
@dataclass
|
| 597 |
+
class AxiomRay:
|
| 598 |
+
sequence:List[str]; ray_id:str; ray_type:str="seed"
|
| 599 |
+
depth:int=0; parent_id:Optional[str]=None
|
| 600 |
+
baton:Optional[Baton]=None; ce_prior:float=float('inf')
|
| 601 |
+
|
| 602 |
+
@property
|
| 603 |
+
def name(self) -> str:
|
| 604 |
+
return "β".join(AXIOM_ABBR.get(a,a[:3]) for a in self.sequence)
|
| 605 |
+
|
| 606 |
+
def extend(self,axiom:str,rtype:str,new_prior:float=float('inf')) -> Optional['AxiomRay']:
|
| 607 |
+
new_seq=self.sequence+[axiom]
|
| 608 |
+
if sequence_valid(new_seq):
|
| 609 |
+
return AxiomRay(sequence=new_seq,
|
| 610 |
+
ray_id=f"{self.ray_id}+{AXIOM_ABBR.get(axiom,'?')}",
|
| 611 |
+
ray_type=rtype,depth=self.depth+1,
|
| 612 |
+
parent_id=self.ray_id,ce_prior=new_prior)
|
| 613 |
+
return None
|
| 614 |
+
|
| 615 |
+
@dataclass
|
| 616 |
+
class HypothesisTrace:
|
| 617 |
+
hid:str; round_idx:int; ray_name:str; ray_type:str
|
| 618 |
+
ce_before:float; ce_after:float
|
| 619 |
+
survived:bool; elapsed_ms:float
|
| 620 |
+
g1_pass:bool=False; g2_pass:bool=False
|
| 621 |
+
binding:Dict[str,float]=field(default_factory=dict)
|
| 622 |
+
invariant_results:Dict[str,Tuple[float,bool]]=field(default_factory=dict)
|
| 623 |
+
tension_class:str="unknown"
|
| 624 |
+
depth:int=0
|
| 625 |
+
|
| 626 |
+
@dataclass
|
| 627 |
+
class StructuralModel:
|
| 628 |
+
best_binding:Dict[str,float]; best_ce:float
|
| 629 |
+
failure_patterns:Set[str]=field(default_factory=set)
|
| 630 |
+
resonant_pairs:List[Tuple[str,str]]=field(default_factory=list)
|
| 631 |
+
|
| 632 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 633 |
+
# SECTION 8: HYPOTHESIS CONSTRUCTORS
|
| 634 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 635 |
+
def _make_hyp(hid,binding,h_type,claim,derivation,pinned,free,conf):
|
| 636 |
+
return Hypothesis(hid=hid,binding=binding,h_type=h_type,claim=claim,
|
| 637 |
+
derivation=derivation,pinned_vars=pinned,free_vars=free,confidence=conf)
|
| 638 |
+
|
| 639 |
+
def _hyp_continuous(p,bt,a,m): return [_make_hyp("cnt",bt.binding,"continuous","Manifold",[],{},p.variables,0.45)]
|
| 640 |
+
def _hyp_discrete(p,bt,a,m):
|
| 641 |
+
b=dict(bt.binding); pinned={}
|
| 642 |
+
for sn in p.scope_order:
|
| 643 |
+
svars=p.scope_vars.get(sn,[]); smcs=[p.compiled_constraints[i] for i in p.scope_groups.get(sn,[])]
|
| 644 |
+
if svars and smcs:
|
| 645 |
+
box={v:IV(b.get(v,0)-1,b.get(v,0)+1) for v in svars if v in p.bounds}
|
| 646 |
+
cont=_hc4(box,smcs)
|
| 647 |
+
if cont:
|
| 648 |
+
for v,iv in cont.items(): b[v]=iv.mid(); (pinned.update({v:b[v]}) if iv.width()<1e-2 else None)
|
| 649 |
+
return [_make_hyp("dis",b,"discrete","TopoScope",[],pinned,p.variables,0.80)]
|
| 650 |
+
def _hyp_quadratic(p,bt,a,m):
|
| 651 |
+
hyps=[]; b=dict(bt.binding)
|
| 652 |
+
if bt.l9 and bt.l9.dominant_vars:
|
| 653 |
+
for v in bt.l9.dominant_vars[:2]: hyps.append(_make_hyp(f"qua_{v}",b,"quadratic",f"Root({v})",[],{v:b[v]},p.variables,0.70))
|
| 654 |
+
return hyps
|
| 655 |
+
def _hyp_bilinear(p,bt,a,m):
|
| 656 |
+
hyps=[]; b=dict(bt.binding)
|
| 657 |
+
for vi, vj in p.bilinear_pairs:
|
| 658 |
+
lo_i,hi_i=p.bounds.get(vi,(0,10)); lo_j,hi_j=p.bounds.get(vj,(0,10))
|
| 659 |
+
product=abs(b.get(vi,1)*b.get(vj,1))
|
| 660 |
+
if product<=0: continue
|
| 661 |
+
gm=math.sqrt(product)
|
| 662 |
+
if lo_i<=gm<=hi_i and lo_j<=gm<=hi_j:
|
| 663 |
+
nb=dict(b); nb[vi]=nb[vj]=gm
|
| 664 |
+
hyps.append(_make_hyp(f"bil_eq_{vi}_{vj}",nb,"bilinear",f"GeoMean({vi}={vj}={gm:.3f})",["bilinear"],{vi:gm,vj:gm},[v for v in p.variables if v not in [vi,vj]],0.70))
|
| 665 |
+
for ratio in [0.4, 0.7071, 0.9]:
|
| 666 |
+
vs=gm*ratio; vl=product/(vs+1e-9)
|
| 667 |
+
if lo_i<=vs<=hi_i and lo_j<=vl<=hi_j and vs<vl:
|
| 668 |
+
nb2=dict(b); nb2[vi]=vs; nb2[vj]=vl
|
| 669 |
+
hyps.append(_make_hyp(f"bil_asc_{int(ratio*100)}_{vi}_{vj}",nb2,"bilinear",f"BilAsc({vi}={vs:.3f}<{vj}={vl:.3f})",["bilinear"],{vi:vs,vj:vl},[v for v in p.variables if v not in [vi,vj]],0.75))
|
| 670 |
+
if lo_i<=vl<=hi_i and lo_j<=vs<=hi_j and vl>vs:
|
| 671 |
+
nb3=dict(b); nb3[vi]=vl; nb3[vj]=vs
|
| 672 |
+
hyps.append(_make_hyp(f"bil_desc_{int(ratio*100)}_{vi}_{vj}",nb3,"bilinear",f"BilDesc({vi}={vl:.3f}>{vj}={vs:.3f})",["bilinear"],{vi:vl,vj:vs},[v for v in p.variables if v not in [vi,vj]],0.75))
|
| 673 |
+
return hyps
|
| 674 |
+
def _hyp_monotone_product(p,bt,a,m):
|
| 675 |
+
hyps=[]; b=dict(bt.binding)
|
| 676 |
+
if not p.monotone_targets: return hyps
|
| 677 |
+
tb=_global_hc4_tighten_bounds(p)
|
| 678 |
+
for v_small, v_large, k in p.monotone_targets:
|
| 679 |
+
lo_s,hi_s=p.bounds.get(v_small,(-1e18,1e18)); lo_l,hi_l=p.bounds.get(v_large,(-1e18,1e18))
|
| 680 |
+
for ratio in [0.1,0.2,0.3,0.4,0.5,0.6,0.707,0.8,0.9,0.95]:
|
| 681 |
+
vs_sq=k*ratio
|
| 682 |
+
if vs_sq<=0: continue
|
| 683 |
+
vs=math.sqrt(vs_sq); vl=k/vs
|
| 684 |
+
if not(lo_s<=vs<=hi_s and lo_l<=vl<=hi_l and vs<=vl): continue
|
| 685 |
+
nb=dict(b); nb[v_small]=vs; nb[v_large]=vl
|
| 686 |
+
box={u:IV(*tb.get(u,p.bounds.get(u,(-10,10)))) for u in p.variables}
|
| 687 |
+
box[v_small]=IV(vs-1e-6,vs+1e-6); box[v_large]=IV(vl-1e-6,vl+1e-6)
|
| 688 |
+
cont=_hc4(box,p.compiled_constraints)
|
| 689 |
+
pinned={v_small:vs,v_large:vl}
|
| 690 |
+
if cont:
|
| 691 |
+
for u,iv in cont.items():
|
| 692 |
+
if u in p.bounds: nb[u]=iv.mid()
|
| 693 |
+
pinned={u:nb[u] for u in p.variables if cont[u].width()<1e-2}
|
| 694 |
+
hyps.append(_make_hyp(f"mpr_{v_small}_{v_large}_r{int(ratio*100)}",nb,"monotone_product",f"MonoProd({v_small}={vs:.4f}β€{v_large}={vl:.4f})",["ordered","hc4"],pinned,[u for u in p.variables if u not in pinned],0.88))
|
| 695 |
+
return hyps
|
| 696 |
+
def _hyp_metric(p,bt,a,m): return [_make_hyp("met",bt.binding,"metric","Radial",[],{},p.variables,0.75)]
|
| 697 |
+
def _hyp_symmetric(p,bt,a,m):
|
| 698 |
+
hyps=[]; b=dict(bt.binding); vl=p.variables
|
| 699 |
+
if len(vl)>=2:
|
| 700 |
+
mid=(b.get(vl[0],0)+b.get(vl[1],0))/2; nb=dict(b); nb[vl[0]]=nb[vl[1]]=mid
|
| 701 |
+
hyps.append(_make_hyp("sym",nb,"symmetric","AveragePin",[],{vl[0]:mid,vl[1]:mid},p.variables,0.60))
|
| 702 |
+
return hyps
|
| 703 |
+
def _hyp_ordered(p,bt,a,m): return [_make_hyp("ord",bt.binding,"ordered","OrderBal",[],{},p.variables,0.67)]
|
| 704 |
+
def _hyp_monotone(p,bt,a,m): return [_make_hyp("mon",bt.binding,"monotone","MonoPath",[],{},p.variables,0.67)]
|
| 705 |
+
def _hyp_convex(p,bt,a,m): return [_make_hyp("cvx",bt.binding,"convex","ConvMix",[],{},p.variables,0.63)]
|
| 706 |
+
def _hyp_conserved(p,bt,a,m): return [_make_hyp("csv",bt.binding,"conserved","Conserve",[],{},p.variables,0.82)]
|
| 707 |
+
def _hyp_mutable(p,bt,a,m):
|
| 708 |
+
b=dict(bt.binding)
|
| 709 |
+
if bt.l9 and bt.l9.dominant_vars:
|
| 710 |
+
v=bt.l9.dominant_vars[0]; lo,hi=p.bounds.get(v,(-10,10))
|
| 711 |
+
b[v]=max(lo,min(hi,b.get(v,0)+random.gauss(0,(hi-lo)*0.05)))
|
| 712 |
+
return [_make_hyp("mut",b,"mutable","Perturb",[],{},p.variables,0.40)]
|
| 713 |
+
def _hyp_network(p,bt,a,m): return [_make_hyp("net",bt.binding,"network","HubProp",[],{},p.variables,0.65)]
|
| 714 |
+
def _hyp_equilibrium(p,bt,a,m): return [_make_hyp("eql",bt.binding,"equilibrium","GradBal",[],{},p.variables,0.72)]
|
| 715 |
+
def _hyp_superposition(p,bt,a,m):
|
| 716 |
+
if a and len(a)>0:
|
| 717 |
+
other=random.choice(a); lam=0.5
|
| 718 |
+
nb={v:lam*bt.binding.get(v,0)+(1-lam)*other.binding.get(v,0) for v in p.variables}
|
| 719 |
+
return [_make_hyp("sup",nb,"superposition","Superpose",[],{},p.variables,0.58)]
|
| 720 |
+
return []
|
| 721 |
+
def _hyp_locality(p,bt,a,m): return [_make_hyp("loc",bt.binding,"locality","LocalFirst",[],{},p.variables,0.72)]
|
| 722 |
+
def _hyp_extremal(p,bt,a,m):
|
| 723 |
+
hyps=[]; b=dict(bt.binding)
|
| 724 |
+
if bt.l9 and bt.l9.dominant_vars:
|
| 725 |
+
v=bt.l9.dominant_vars[0]; lo,hi=p.bounds.get(v,(0,1))
|
| 726 |
+
for val in [lo,hi,(lo+hi)/2]:
|
| 727 |
+
nb=dict(b); nb[v]=val
|
| 728 |
+
hyps.append(_make_hyp(f"ext_{v}_{val:.3f}",nb,"extremal",f"PinBound({v}={val:.3f})",[],{v:val},p.variables,0.65))
|
| 729 |
+
return hyps
|
| 730 |
+
def _hyp_entropy(p,bt,a,m): return [_make_hyp("ent",{v:random.uniform(*p.bounds[v]) for v in p.variables},"entropy","MaxEnt",[],{},p.variables,0.48)]
|
| 731 |
+
def _hyp_injective(p,bt,a,m): return [_make_hyp("inj",bt.binding,"injective","Distinct",[],{},p.variables,0.50)]
|
| 732 |
+
def _hyp_surjective(p,bt,a,m): return [_make_hyp("sur",bt.binding,"surjective","Sweep",[],{},p.variables,0.45)]
|
| 733 |
+
def _hyp_transitive(p,bt,a,m): return [_make_hyp("tra",bt.binding,"transitive","TransSnap",[],{},p.variables,0.77)]
|
| 734 |
+
def _hyp_atomic(p,bt,a,m):
|
| 735 |
+
hyps=[]; b=dict(bt.binding)
|
| 736 |
+
for mc in p.compiled_constraints:
|
| 737 |
+
if not mc.projections: continue
|
| 738 |
+
for v,projs in mc.projections.items():
|
| 739 |
+
for proj in projs:
|
| 740 |
+
try:
|
| 741 |
+
val=float(proj["func"](*[b.get(s,0.0) for s in proj["syms"]]))
|
| 742 |
+
lo,hi=p.bounds.get(v,(-1e18,1e18))
|
| 743 |
+
if math.isfinite(val) and lo<=val<=hi:
|
| 744 |
+
nb=dict(b); nb[v]=val
|
| 745 |
+
hyps.append(_make_hyp(f"ato_{v}",nb,"atomic",f"Solve({v})",[],{v:val},p.variables,0.70))
|
| 746 |
+
break
|
| 747 |
+
except: pass
|
| 748 |
+
if hyps: break
|
| 749 |
+
return hyps
|
| 750 |
+
def _hyp_implication(p,bt,a,m): return [_make_hyp("imp",bt.binding,"implication","Imply",[],{},p.variables,0.78)]
|
| 751 |
+
def _hyp_negation(p,bt,a,m):
|
| 752 |
+
nb={v:max(p.bounds[v][0],min(p.bounds[v][1],(p.bounds[v][0]+p.bounds[v][1])-bt.binding.get(v,(p.bounds[v][0]+p.bounds[v][1])/2))) for v in p.variables}
|
| 753 |
+
return [_make_hyp("neg",nb,"negation","Mirror",[],{},p.variables,0.42)]
|
| 754 |
+
def _hyp_composite(p,bt,a,m): return [_make_hyp("cmp",bt.binding,"composite","Enz",[],{},p.variables,0.90)]
|
| 755 |
+
def _hyp_holism(p,bt,a,m): return [_make_hyp("hol",bt.binding,"holism","Global",[],{},p.variables,0.73)]
|
| 756 |
+
def _hyp_parsimony(p,bt,a,m):
|
| 757 |
+
b=dict(bt.binding); nb={}
|
| 758 |
+
max_val=max((abs(v) for v in b.values()),default=1.0)
|
| 759 |
+
for v in p.variables:
|
| 760 |
+
val=b[v]; lo,hi=p.bounds[v]
|
| 761 |
+
if abs(val)<0.1*max_val and lo<=0.0<=hi: nb[v]=0.0
|
| 762 |
+
else:
|
| 763 |
+
candidates=[round(val*m)/m for m in [1,2,4] if lo<=round(val*m)/m<=hi]
|
| 764 |
+
nb[v]=min(candidates,key=lambda c:abs(c-val)) if candidates else val
|
| 765 |
+
return [_make_hyp("par",nb,"parsimony","Occam",[],{},p.variables,0.58)]
|
| 766 |
+
def _hyp_duality(p,bt,a,m): return [_make_hyp("dua",bt.binding,"duality","Tight",[],{},p.variables,0.68)]
|
| 767 |
+
|
| 768 |
+
AXIOM_CONSTRUCTORS={
|
| 769 |
+
Axiom.CONTINUOUS:_hyp_continuous, Axiom.DISCRETE:_hyp_discrete,
|
| 770 |
+
Axiom.QUADRATIC:_hyp_quadratic, Axiom.BILINEAR:_hyp_bilinear,
|
| 771 |
+
Axiom.METRIC:_hyp_metric, Axiom.SYMMETRIC:_hyp_symmetric,
|
| 772 |
+
Axiom.ORDERED:_hyp_ordered, Axiom.MONOTONE:_hyp_monotone,
|
| 773 |
+
Axiom.CONVEX:_hyp_convex, Axiom.MONOTONE_PRODUCT:_hyp_monotone_product,
|
| 774 |
+
Axiom.CONSERVED:_hyp_conserved, Axiom.MUTABLE:_hyp_mutable,
|
| 775 |
+
Axiom.NETWORK:_hyp_network, Axiom.EQUILIBRIUM:_hyp_equilibrium,
|
| 776 |
+
Axiom.SUPERPOSITION:_hyp_superposition, Axiom.LOCALITY:_hyp_locality,
|
| 777 |
+
Axiom.EXTREMAL:_hyp_extremal, Axiom.ENTROPY:_hyp_entropy,
|
| 778 |
+
Axiom.INJECTIVE:_hyp_injective, Axiom.SURJECTIVE:_hyp_surjective,
|
| 779 |
+
Axiom.TRANSITIVE:_hyp_transitive, Axiom.ATOMIC:_hyp_atomic,
|
| 780 |
+
Axiom.IMPLICATION:_hyp_implication, Axiom.NEGATION:_hyp_negation,
|
| 781 |
+
Axiom.COMPOSITE:_hyp_composite, Axiom.HOLISM:_hyp_holism,
|
| 782 |
+
Axiom.PARSIMONY:_hyp_parsimony, Axiom.DUALITY:_hyp_duality,
|
| 783 |
+
}
|
| 784 |
+
|
| 785 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 786 |
+
# SECTION 9: CREATIVE SEEDER
|
| 787 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 788 |
+
class CreativeSeeder:
|
| 789 |
+
def target_seeds(self,anchors:List[AXLInvariant]) -> List[AxiomRay]:
|
| 790 |
+
seeds=[]
|
| 791 |
+
for a in anchors:
|
| 792 |
+
if "*" in a.expr and "**" not in a.expr:
|
| 793 |
+
seeds.append(AxiomRay([Axiom.BILINEAR],f"TGT_BIL_{a.name}","target"))
|
| 794 |
+
seeds.append(AxiomRay([Axiom.MONOTONE_PRODUCT],f"TGT_MPR_{a.name}","target"))
|
| 795 |
+
elif "**2" in a.expr:
|
| 796 |
+
seeds.append(AxiomRay([Axiom.QUADRATIC],f"TGT_QUA_{a.name}","target"))
|
| 797 |
+
seeds.append(AxiomRay([Axiom.METRIC],f"TGT_MET_{a.name}","target"))
|
| 798 |
+
elif "+" in a.expr:
|
| 799 |
+
seeds.append(AxiomRay([Axiom.CONSERVED],f"TGT_CSV_{a.name}","target"))
|
| 800 |
+
return seeds
|
| 801 |
+
|
| 802 |
+
def intelligent_branch(self,ray,out_baton,remaining_axioms,branch_width) -> List[AxiomRay]:
|
| 803 |
+
dom_vars=out_baton.l9.dominant_vars if out_baton.l9 else []
|
| 804 |
+
tension_class=out_baton.l9.tension_class if out_baton.l9 else "unknown"
|
| 805 |
+
n_dom=len(dom_vars)
|
| 806 |
+
effective_width = branch_width
|
| 807 |
+
if tension_class == "systemic" and out_baton.ce > 0.1:
|
| 808 |
+
effective_width = BRANCH_WIDTH_SYSTEMIC
|
| 809 |
+
|
| 810 |
+
isolated ={Axiom.ATOMIC,Axiom.EXTREMAL,Axiom.MUTABLE,Axiom.DUALITY,Axiom.LOCALITY}
|
| 811 |
+
relational ={Axiom.SYMMETRIC,Axiom.BILINEAR,Axiom.MONOTONE_PRODUCT,
|
| 812 |
+
Axiom.ORDERED,Axiom.METRIC,Axiom.INJECTIVE}
|
| 813 |
+
systemic ={Axiom.CONSERVED,Axiom.NETWORK,Axiom.HOLISM,Axiom.COMPOSITE,Axiom.ENTROPY}
|
| 814 |
+
|
| 815 |
+
scored=[]
|
| 816 |
+
for ax in remaining_axioms:
|
| 817 |
+
w=1.0
|
| 818 |
+
if n_dom==1 and ax in isolated: w+=5.0
|
| 819 |
+
elif n_dom==2 and ax in relational: w+=5.0
|
| 820 |
+
elif n_dom>=3 and ax in systemic: w+=5.0
|
| 821 |
+
scored.append((w,ax))
|
| 822 |
+
scored.sort(key=lambda x:x[0]*random.random(),reverse=True)
|
| 823 |
+
|
| 824 |
+
children=[]
|
| 825 |
+
for _,axiom in scored[:effective_width]:
|
| 826 |
+
child=ray.extend(axiom,"branch",new_prior=out_baton.ce)
|
| 827 |
+
if child: child.baton=out_baton; children.append(child)
|
| 828 |
+
return children
|
| 829 |
+
|
| 830 |
+
def tension_seeds(self,max_n:int=3000) -> List[AxiomRay]:
|
| 831 |
+
results=[]
|
| 832 |
+
for (a,c) in ADJACENT_CONTRADICTIONS:
|
| 833 |
+
for b1 in ALL_AXIOMS:
|
| 834 |
+
if b1 in (a,c) or frozenset([a,b1]) in _CONTRA_SET: continue
|
| 835 |
+
for mid in ALL_AXIOMS:
|
| 836 |
+
if mid in (a,b1,c) or frozenset([b1,mid]) in _CONTRA_SET: continue
|
| 837 |
+
for b2 in ALL_AXIOMS:
|
| 838 |
+
if b2 in (a,b1,mid,c): continue
|
| 839 |
+
if frozenset([mid,b2]) not in _CONTRA_SET and frozenset([b2,c]) not in _CONTRA_SET:
|
| 840 |
+
results.append(AxiomRay([a,b1,mid,b2,c],
|
| 841 |
+
f"T5_{AXIOM_ABBR.get(a,'?')}_{AXIOM_ABBR.get(c,'?')}","tension"))
|
| 842 |
+
random.shuffle(results); return results[:max_n]
|
| 843 |
+
|
| 844 |
+
def random_scouts(self,n:int=600) -> List[AxiomRay]:
|
| 845 |
+
scouts=[]; attempts=0
|
| 846 |
+
while len(scouts)<n and attempts<n*30:
|
| 847 |
+
attempts+=1; length=random.randint(3,12)
|
| 848 |
+
pool=list(ALL_AXIOMS); random.shuffle(pool); seq=[pool[0]]
|
| 849 |
+
for ax in pool[1:]:
|
| 850 |
+
if len(seq)>=length: break
|
| 851 |
+
if ax!=seq[-1] and frozenset([seq[-1],ax]) not in _CONTRA_SET: seq.append(ax)
|
| 852 |
+
if len(seq)>=3:
|
| 853 |
+
scouts.append(AxiomRay(seq,f"SC_{''.join(AXIOM_ABBR.get(a,a[:2]) for a in seq)}","scout"))
|
| 854 |
+
return scouts
|
| 855 |
+
|
| 856 |
+
def recombine(self,ray_a,ray_b,ba,bb) -> List[AxiomRay]:
|
| 857 |
+
results=[]
|
| 858 |
+
for lam in [0.25,0.5,0.75]:
|
| 859 |
+
ib={v:lam*ba.binding.get(v,0)+(1-lam)*bb.binding.get(v,0)
|
| 860 |
+
for v in set(list(ba.binding.keys())+list(bb.binding.keys()))}
|
| 861 |
+
ib_ce=lam*ba.ce+(1-lam)*bb.ce
|
| 862 |
+
ib_baton=Baton(binding=ib,ce=ib_ce,ray_id="INTERP")
|
| 863 |
+
cut=min(len(ray_a.sequence),len(ray_b.sequence))//2
|
| 864 |
+
if cut>0:
|
| 865 |
+
seq=ray_a.sequence[:cut]+[x for x in ray_b.sequence[cut:] if x not in ray_a.sequence[:cut]]
|
| 866 |
+
if sequence_valid(seq):
|
| 867 |
+
results.append(AxiomRay(seq,f"RC_l{int(lam*100)}","recombine",
|
| 868 |
+
baton=ib_baton,ce_prior=ib_ce))
|
| 869 |
+
return results
|
| 870 |
+
|
| 871 |
+
def invert(self,ray,parent_ce) -> Optional[AxiomRay]:
|
| 872 |
+
inv=list(reversed(ray.sequence))
|
| 873 |
+
if sequence_valid(inv) and inv!=ray.sequence:
|
| 874 |
+
return AxiomRay(inv,f"INV_{ray.ray_id}","invert",parent_id=ray.ray_id,ce_prior=parent_ce)
|
| 875 |
+
return None
|
| 876 |
+
|
| 877 |
+
def resonance_extensions(self,ray,parent_ce,resonant_pairs) -> List[AxiomRay]:
|
| 878 |
+
if not ray.sequence or not resonant_pairs: return []
|
| 879 |
+
last=ray.sequence[-1]; results=[]
|
| 880 |
+
nexts=[b for (a,b) in resonant_pairs
|
| 881 |
+
if a==last and b!=last and frozenset([a,b]) not in _CONTRA_SET
|
| 882 |
+
and b not in ray.sequence]
|
| 883 |
+
for b in nexts:
|
| 884 |
+
child=ray.extend(b,"branch",new_prior=parent_ce)
|
| 885 |
+
if child: child.ray_id=f"RES_{child.ray_id}"; results.append(child)
|
| 886 |
+
return results
|
| 887 |
+
|
| 888 |
+
CREATIVE_SEEDER=CreativeSeeder()
|
| 889 |
+
|
| 890 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 891 |
+
# SECTION 10: VERIFY LAYER
|
| 892 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 893 |
+
def _verify(binding:Dict[str,float],base_problem:Problem,
|
| 894 |
+
anchors:List[AXLInvariant]) -> Tuple[bool,bool,Dict[str,Tuple[float,bool]],float]:
|
| 895 |
+
g1_ce=base_problem.scalar_energy(binding)
|
| 896 |
+
g1_pass=g1_ce<VERIFY_G1_THRESHOLD
|
| 897 |
+
g2={}
|
| 898 |
+
for inv in anchors:
|
| 899 |
+
try:
|
| 900 |
+
if inv.compiled_func: val = float(inv.compiled_func(*[binding.get(v, 0.0) for v in inv.syms_used]))
|
| 901 |
+
else: val = 999.0
|
| 902 |
+
err=abs(val)
|
| 903 |
+
if inv.mode=="eq": passed=err<inv.tolerance
|
| 904 |
+
elif inv.mode=="geq": passed=val>=-inv.tolerance
|
| 905 |
+
elif inv.mode=="leq": passed=val<=inv.tolerance
|
| 906 |
+
else: passed=err<inv.tolerance
|
| 907 |
+
g2[inv.name]=(round(val if inv.mode in ("geq","leq") else err,6),passed)
|
| 908 |
+
except: g2[inv.name]=(999.0,False)
|
| 909 |
+
g2_pass=all(v[1] for v in g2.values()) if g2 else True
|
| 910 |
+
return g1_pass,g2_pass,g2,round(g1_ce,6)
|
| 911 |
+
|
| 912 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 913 |
+
# SECTION 11: SEQUENCE RAY TRACER
|
| 914 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 915 |
+
class SequenceRayTracer:
|
| 916 |
+
def trace(self,axl_def:AXLProblemDef,base_problem:Problem):
|
| 917 |
+
hero_traces:List[HypothesisTrace]=[]
|
| 918 |
+
baton_registry:Dict[str,Baton]={}
|
| 919 |
+
baton_registry_keys:deque=deque()
|
| 920 |
+
successful_rays:List[AxiomRay]=[]
|
| 921 |
+
resonant_pairs:List[Tuple[str,str]]=[]
|
| 922 |
+
|
| 923 |
+
for inv in axl_def.anchors:
|
| 924 |
+
if not inv.compiled_func:
|
| 925 |
+
inv.compile(base_problem.variables)
|
| 926 |
+
|
| 927 |
+
init_b,init_ce=_mprt_sample(base_problem,
|
| 928 |
+
{v:IV(*base_problem.bounds[v]) for v in base_problem.variables},N_MPRT_EXPLORE)
|
| 929 |
+
seed_baton=Baton(binding=init_b,ce=init_ce,ray_id="SEED",
|
| 930 |
+
l9=L9Certificate(init_ce,base_problem.variables[:3],[],"systemic"))
|
| 931 |
+
|
| 932 |
+
queues={
|
| 933 |
+
"core":[AxiomRay([a],f"S{i}","seed",ce_prior=init_ce) for i,a in enumerate(ALL_AXIOMS)],
|
| 934 |
+
"explore":CREATIVE_SEEDER.tension_seeds(3000)+CREATIVE_SEEDER.random_scouts(600),
|
| 935 |
+
"genetic":CREATIVE_SEEDER.target_seeds(axl_def.anchors),
|
| 936 |
+
}
|
| 937 |
+
for q in queues.values(): random.shuffle(q)
|
| 938 |
+
|
| 939 |
+
queue_order=["core","explore","genetic"]; q_idx=0
|
| 940 |
+
best_ce=init_ce; best_binding=dict(init_b); total_fired=0
|
| 941 |
+
model=StructuralModel(best_binding,best_ce)
|
| 942 |
+
allosteric_counts={"isolated":0,"relational":0,"systemic":0}
|
| 943 |
+
|
| 944 |
+
while total_fired<MAX_TOTAL_RAYS:
|
| 945 |
+
batch_rays:List[AxiomRay]=[]
|
| 946 |
+
for _ in range(RAY_BATCH_SIZE):
|
| 947 |
+
start_idx=q_idx
|
| 948 |
+
while not queues[queue_order[q_idx]]:
|
| 949 |
+
q_idx=(q_idx+1)%3
|
| 950 |
+
if q_idx==start_idx: break
|
| 951 |
+
if not any(queues[k] for k in queue_order): break
|
| 952 |
+
ray=queues[queue_order[q_idx]].pop(0); q_idx=(q_idx+1)%3
|
| 953 |
+
batch_rays.append(ray); total_fired+=1
|
| 954 |
+
if not batch_rays: break
|
| 955 |
+
|
| 956 |
+
t0=time.time()
|
| 957 |
+
historical_batons=list(baton_registry.values())
|
| 958 |
+
|
| 959 |
+
batch_hyps:List[Tuple[int,Hypothesis]]=[]
|
| 960 |
+
for i,ray in enumerate(batch_rays):
|
| 961 |
+
p_baton=ray.baton or seed_baton
|
| 962 |
+
constructor=AXIOM_CONSTRUCTORS.get(ray.sequence[-1])
|
| 963 |
+
hyps=constructor(base_problem,p_baton,historical_batons,model) if constructor else []
|
| 964 |
+
if not hyps: hyps=[_make_hyp("fb",p_baton.binding,"fallback","Pass",[],{},base_problem.variables,0.1)]
|
| 965 |
+
batch_hyps.append((i,hyps[0]))
|
| 966 |
+
|
| 967 |
+
eval_results=_batched_deduce_and_evaluate(base_problem,[h for _,h in batch_hyps])
|
| 968 |
+
elapsed_ms=(time.time()-t0)*1000/max(1,len(batch_rays))
|
| 969 |
+
|
| 970 |
+
solved=False
|
| 971 |
+
for (ray_idx,hyp),(final_b,final_ce,dom_vars,tension_class) in zip(batch_hyps,eval_results):
|
| 972 |
+
ray=batch_rays[ray_idx]
|
| 973 |
+
parent_ce=(ray.baton or seed_baton).ce
|
| 974 |
+
allosteric_counts[tension_class]=allosteric_counts.get(tension_class,0)+1
|
| 975 |
+
|
| 976 |
+
g1_pass,g2_pass,inv_results,g1_ce=_verify(final_b,base_problem,axl_def.anchors)
|
| 977 |
+
overall_pass=g1_pass and g2_pass
|
| 978 |
+
|
| 979 |
+
improved=final_ce<best_ce
|
| 980 |
+
if improved or overall_pass or random.random()<0.002:
|
| 981 |
+
hero_traces.append(HypothesisTrace(
|
| 982 |
+
hid=ray.ray_id,round_idx=total_fired-len(batch_rays)+ray_idx,
|
| 983 |
+
ray_name=ray.name,ray_type=ray.ray_type,
|
| 984 |
+
ce_before=parent_ce,ce_after=final_ce,
|
| 985 |
+
survived=overall_pass,elapsed_ms=elapsed_ms,
|
| 986 |
+
g1_pass=g1_pass,g2_pass=g2_pass,
|
| 987 |
+
binding={k:round(v,5) for k,v in final_b.items()},
|
| 988 |
+
invariant_results=inv_results,
|
| 989 |
+
tension_class=tension_class,depth=ray.depth))
|
| 990 |
+
|
| 991 |
+
out_baton=Baton(binding=final_b,ce=final_ce,ray_id=ray.ray_id,
|
| 992 |
+
l9=L9Certificate(final_ce,dom_vars,[],tension_class))
|
| 993 |
+
|
| 994 |
+
if final_ce<BATON_REGISTRY_THRESHOLD:
|
| 995 |
+
if ray.ray_id in baton_registry:
|
| 996 |
+
baton_registry[ray.ray_id]=out_baton
|
| 997 |
+
else:
|
| 998 |
+
if len(baton_registry)>=BATON_REGISTRY_MAX:
|
| 999 |
+
old_key=baton_registry_keys.popleft()
|
| 1000 |
+
baton_registry.pop(old_key,None)
|
| 1001 |
+
baton_registry[ray.ray_id]=out_baton
|
| 1002 |
+
baton_registry_keys.append(ray.ray_id)
|
| 1003 |
+
|
| 1004 |
+
if improved:
|
| 1005 |
+
best_ce=final_ce; best_binding=dict(final_b)
|
| 1006 |
+
successful_rays.append(ray)
|
| 1007 |
+
model.best_binding=best_binding; model.best_ce=best_ce
|
| 1008 |
+
|
| 1009 |
+
if overall_pass: solved=True
|
| 1010 |
+
|
| 1011 |
+
if improved and len(ray.sequence)>=2:
|
| 1012 |
+
pair=(ray.sequence[-2],ray.sequence[-1])
|
| 1013 |
+
if pair not in resonant_pairs: resonant_pairs.append(pair)
|
| 1014 |
+
model.resonant_pairs=resonant_pairs
|
| 1015 |
+
|
| 1016 |
+
earned=(final_ce<parent_ce*CE_EARN_RATIO or ray.depth<1)
|
| 1017 |
+
scout_earned=(ray.ray_type=="scout" and final_ce<SCOUT_CE_THRESHOLD)
|
| 1018 |
+
|
| 1019 |
+
if earned or scout_earned:
|
| 1020 |
+
remaining=[a for a in ALL_AXIOMS
|
| 1021 |
+
if a!=ray.sequence[-1]
|
| 1022 |
+
and frozenset([ray.sequence[-1],a]) not in _CONTRA_SET
|
| 1023 |
+
and a not in ray.sequence]
|
| 1024 |
+
|
| 1025 |
+
new_children:List[AxiomRay]=[]
|
| 1026 |
+
new_children.extend(
|
| 1027 |
+
CREATIVE_SEEDER.intelligent_branch(ray,out_baton,remaining,BRANCH_WIDTH))
|
| 1028 |
+
inv=CREATIVE_SEEDER.invert(ray,final_ce)
|
| 1029 |
+
if inv: new_children.append(inv)
|
| 1030 |
+
|
| 1031 |
+
if len(successful_rays)>=2:
|
| 1032 |
+
candidates=[r for r in successful_rays if r.ray_id!=ray.ray_id]
|
| 1033 |
+
if candidates:
|
| 1034 |
+
partner=random.choice(candidates)
|
| 1035 |
+
ba=baton_registry.get(ray.ray_id,out_baton)
|
| 1036 |
+
bb=baton_registry.get(partner.ray_id)
|
| 1037 |
+
if bb is not None:
|
| 1038 |
+
new_children.extend(CREATIVE_SEEDER.recombine(ray,partner,ba,bb)[:3])
|
| 1039 |
+
|
| 1040 |
+
new_children.extend(
|
| 1041 |
+
CREATIVE_SEEDER.resonance_extensions(ray,final_ce,resonant_pairs))
|
| 1042 |
+
|
| 1043 |
+
for child in new_children:
|
| 1044 |
+
if child.ray_type in ("seed","branch","target"): queues["core"].append(child)
|
| 1045 |
+
elif child.ray_type in ("scout","tension"): queues["explore"].append(child)
|
| 1046 |
+
elif child.ray_type in ("recombine","invert"): queues["genetic"].append(child)
|
| 1047 |
+
|
| 1048 |
+
queues["core"].sort(key=lambda r:r.ce_prior+(r.depth*0.0001))
|
| 1049 |
+
queues["genetic"].sort(key=lambda r:r.ce_prior+(r.depth*0.0001))
|
| 1050 |
+
if solved: break
|
| 1051 |
+
|
| 1052 |
+
return best_binding,best_ce,hero_traces,total_fired,allosteric_counts
|
| 1053 |
+
|
| 1054 |
+
|
| 1055 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1056 |
+
# SECTION 12: LLM BRIDGE (GOOGLE GENAI 3.1)
|
| 1057 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1058 |
+
|
| 1059 |
+
ENCODER_SYSTEM_PROMPT = """You are the Encoder for the Practicality Wisdom Layer β a neuro-symbolic
|
| 1060 |
+
constraint satisfaction engine. Your job is STRICT SLOT-FILLING, not reasoning.
|
| 1061 |
+
You read a natural language problem description and produce a structured
|
| 1062 |
+
JSON object that represents its mathematical constraints.
|
| 1063 |
+
|
| 1064 |
+
You do NOT solve the problem. You do NOT guess the structural topology. You only translate.
|
| 1065 |
+
|
| 1066 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1067 |
+
PART 1: THE AXL STRUCTURE
|
| 1068 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1069 |
+
|
| 1070 |
+
You must output a single JSON object matching this schema exactly:
|
| 1071 |
+
|
| 1072 |
+
{
|
| 1073 |
+
"name": string, // CamelCase, no spaces
|
| 1074 |
+
"description": string, // one sentence
|
| 1075 |
+
"variables": [
|
| 1076 |
+
{
|
| 1077 |
+
"name": string, // short lowercase identifier (x, v1, theta, etc.)
|
| 1078 |
+
"lo": number, // OPTIONAL: ONLY provide if the problem explicitly limits this (e.g., > 0)
|
| 1079 |
+
"hi": number // OPTIONAL: ONLY provide if the problem explicitly limits this
|
| 1080 |
+
}
|
| 1081 |
+
],
|
| 1082 |
+
"constraints": [
|
| 1083 |
+
{
|
| 1084 |
+
"kind": "EQ" | "GEQ" | "LEQ" | "CONSERVE",
|
| 1085 |
+
"expr": string, // valid Python/sympy expression using variable names
|
| 1086 |
+
"weight": number // 1.0 default; 5.0 for hard constraints; 10.0 for conservation laws
|
| 1087 |
+
}
|
| 1088 |
+
],
|
| 1089 |
+
"anchors": [
|
| 1090 |
+
{
|
| 1091 |
+
"name": string, // snake_case identifier for this condition
|
| 1092 |
+
"expr": string, // sympy expression that equals 0 when satisfied
|
| 1093 |
+
"tolerance": number, // how close to 0 counts as solved (default 0.001)
|
| 1094 |
+
"mode": "eq" | "geq" | "leq"
|
| 1095 |
+
}
|
| 1096 |
+
],
|
| 1097 |
+
"observations": {
|
| 1098 |
+
"variable_name": number // variables whose values are STRICTLY KNOWN/FIXED in the prompt
|
| 1099 |
+
}
|
| 1100 |
+
}
|
| 1101 |
+
|
| 1102 |
+
CRITICAL: Output ONLY the JSON object. No preamble, no markdown fences.
|
| 1103 |
+
|
| 1104 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1105 |
+
PART 2: ENCODING RULES
|
| 1106 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1107 |
+
|
| 1108 |
+
VARIABLES & BOUNDS:
|
| 1109 |
+
β’ Use short names: x, y, v, w1, cb
|
| 1110 |
+
β’ DO NOT INVENT BOUNDS. If the text says "probabilities", set lo=0.0, hi=1.0.
|
| 1111 |
+
If the text says nothing, omit "lo" and "hi" entirely.
|
| 1112 |
+
β’ Fixed values go in "observations", not as tight bounds.
|
| 1113 |
+
β’ DO include observed variables in the variables list. The engine handles pinning.
|
| 1114 |
+
|
| 1115 |
+
CONSTRAINTS & ANCHORS:
|
| 1116 |
+
β’ EQ: expr = 0 (e.g., "x**2 + y**2 - 1.0")
|
| 1117 |
+
β’ GEQ: expr β₯ 0
|
| 1118 |
+
β’ LEQ: expr β€ 0
|
| 1119 |
+
β’ CONSERVE: inviolable laws (weight 10.0 automatically)
|
| 1120 |
+
β’ Expressions must be valid Python/sympy: use **, *, +, -, /
|
| 1121 |
+
β’ Do NOT use =, >=, <= in expressions β only the expression that evaluates against 0.
|
| 1122 |
+
β’ Anchors are the verification conditions. What MUST be true to accept the solution?
|
| 1123 |
+
|
| 1124 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1125 |
+
PART 3: FAILURE HANDLING
|
| 1126 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1127 |
+
|
| 1128 |
+
If a previous solve attempt is included in the user message showing
|
| 1129 |
+
failed invariants, revise the encoding to better capture those conditions.
|
| 1130 |
+
Common fixes:
|
| 1131 |
+
β’ Invariant fails with large value β increase constraint weight
|
| 1132 |
+
β’ Wrong sign β swap GEQ to LEQ or negate expr
|
| 1133 |
+
β’ Missing constraint β add missing physical law as EQ or CONSERVE
|
| 1134 |
+
|
| 1135 |
+
Output ONLY the JSON. No preamble.
|
| 1136 |
+
"""
|
| 1137 |
+
|
| 1138 |
+
COLLAPSER_SYSTEM_PROMPT = """You are the Collapser for the Practicality Wisdom Layer. You receive
|
| 1139 |
+
a JSON object containing:
|
| 1140 |
+
β’ The original natural language problem
|
| 1141 |
+
β’ The problem name and description
|
| 1142 |
+
β’ Whether the solver succeeded (solved/partial/unsolved)
|
| 1143 |
+
β’ The verified variable binding (the numerical solution)
|
| 1144 |
+
β’ Gate 1 result: global constraint energy (mathematical validity)
|
| 1145 |
+
β’ Gate 2 results: named invariant checks (domain correctness)
|
| 1146 |
+
β’ The best axiom ray sequence that found the solution
|
| 1147 |
+
β’ Total rays fired
|
| 1148 |
+
|
| 1149 |
+
Your job is to translate this back into clear, domain-appropriate
|
| 1150 |
+
natural language that a human expert in the problem domain would
|
| 1151 |
+
recognize as correct and useful.
|
| 1152 |
+
|
| 1153 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1154 |
+
TONE AND FORMAT RULES
|
| 1155 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1156 |
+
β’ Write for a domain expert, not a general audience
|
| 1157 |
+
β’ Use domain-appropriate variable names and units if discernible
|
| 1158 |
+
β’ Lead with the status: SOLVED / PARTIAL / UNSOLVED
|
| 1159 |
+
β’ Report the binding values in the order they appear in the problem
|
| 1160 |
+
β’ For each Gate 2 invariant:
|
| 1161 |
+
- If passed: briefly confirm what physical condition is satisfied
|
| 1162 |
+
- If failed: explain specifically what condition was NOT met
|
| 1163 |
+
and by how much (the numerical value is provided)
|
| 1164 |
+
β’ Keep it under 250 words
|
| 1165 |
+
β’ End with the axiom sequence that found the solution, formatted
|
| 1166 |
+
as a one-line note
|
| 1167 |
+
"""
|
| 1168 |
+
|
| 1169 |
+
def encode_problem_to_axl(problem_text: str, api_key: str) -> AXLProblemDef:
|
| 1170 |
+
"""Uses Google GenAI to slot-fill the text into the AXL structural JSON."""
|
| 1171 |
+
if not api_key:
|
| 1172 |
+
raise ValueError("Gemini API Key is missing. Please provide it in the UI or set it globally.")
|
| 1173 |
+
|
| 1174 |
+
client = genai.Client(api_key=api_key)
|
| 1175 |
+
|
| 1176 |
+
config = types.GenerateContentConfig(
|
| 1177 |
+
system_instruction=[types.Part.from_text(text=ENCODER_SYSTEM_PROMPT)],
|
| 1178 |
+
response_mime_type="application/json",
|
| 1179 |
+
temperature=0.1,
|
| 1180 |
+
)
|
| 1181 |
+
|
| 1182 |
+
response = client.models.generate_content(
|
| 1183 |
+
model=GEMINI_MODEL,
|
| 1184 |
+
contents=problem_text,
|
| 1185 |
+
config=config,
|
| 1186 |
+
)
|
| 1187 |
+
|
| 1188 |
+
return _parse_axl_json(response.text)
|
| 1189 |
+
|
| 1190 |
+
def _parse_axl_json(raw_json: str) -> AXLProblemDef:
|
| 1191 |
+
"""Parses Gemini JSON output into the strict AXL structural definition."""
|
| 1192 |
+
text = raw_json.strip()
|
| 1193 |
+
if text.startswith("```"):
|
| 1194 |
+
text = text.split("```")[1]
|
| 1195 |
+
if text.startswith("json"):
|
| 1196 |
+
text = text[4:]
|
| 1197 |
+
text = text.strip().rstrip("`").strip()
|
| 1198 |
+
|
| 1199 |
+
d = json.loads(text)
|
| 1200 |
+
anchors = [
|
| 1201 |
+
AXLInvariant(
|
| 1202 |
+
name=a["name"], expr=a["expr"],
|
| 1203 |
+
tolerance=a.get("tolerance", VERIFY_G2_TOLERANCE),
|
| 1204 |
+
mode=a.get("mode", "eq")
|
| 1205 |
+
)
|
| 1206 |
+
for a in d.get("anchors", [])
|
| 1207 |
+
]
|
| 1208 |
+
|
| 1209 |
+
variables = []
|
| 1210 |
+
for v in d["variables"]:
|
| 1211 |
+
variables.append({
|
| 1212 |
+
"name": v["name"],
|
| 1213 |
+
"lo": float(v.get("lo", -1e18)),
|
| 1214 |
+
"hi": float(v.get("hi", 1e18))
|
| 1215 |
+
})
|
| 1216 |
+
|
| 1217 |
+
return AXLProblemDef(
|
| 1218 |
+
name=d["name"],
|
| 1219 |
+
description=d.get("description", ""),
|
| 1220 |
+
axioms=set(), # Engine owns Axioms, not the LLM
|
| 1221 |
+
variables=variables,
|
| 1222 |
+
constraints=d.get("constraints", []),
|
| 1223 |
+
scopes=d.get("scopes", []),
|
| 1224 |
+
anchors=anchors,
|
| 1225 |
+
observations=d.get("observations", {}),
|
| 1226 |
+
)
|
| 1227 |
+
|
| 1228 |
+
def collapse_solution(
|
| 1229 |
+
problem_text: str,
|
| 1230 |
+
axl_def: AXLProblemDef,
|
| 1231 |
+
binding: Dict[str, float],
|
| 1232 |
+
g1_pass: bool,
|
| 1233 |
+
g2_pass: bool,
|
| 1234 |
+
invariant_results: Dict[str, Tuple[float, bool]],
|
| 1235 |
+
best_ray: str,
|
| 1236 |
+
total_fired: int,
|
| 1237 |
+
api_key: str
|
| 1238 |
+
) -> str:
|
| 1239 |
+
"""Uses Google GenAI with high-level thinking to collapse PyTorch findings to Natural Language."""
|
| 1240 |
+
if not api_key:
|
| 1241 |
+
return "ERROR: Gemini API Key required for semantic collapsing."
|
| 1242 |
+
|
| 1243 |
+
client = genai.Client(api_key=api_key)
|
| 1244 |
+
|
| 1245 |
+
payload = {
|
| 1246 |
+
"original_problem": problem_text,
|
| 1247 |
+
"problem_name": axl_def.name,
|
| 1248 |
+
"solved": g1_pass and g2_pass,
|
| 1249 |
+
"gate1_pass": g1_pass,
|
| 1250 |
+
"gate2_pass": g2_pass,
|
| 1251 |
+
"binding": {k: round(v, 6) for k, v in binding.items()},
|
| 1252 |
+
"invariant_results":invariant_results,
|
| 1253 |
+
"best_ray_sequence":best_ray,
|
| 1254 |
+
"rays_fired": total_fired,
|
| 1255 |
+
}
|
| 1256 |
+
|
| 1257 |
+
config = types.GenerateContentConfig(
|
| 1258 |
+
thinking_config=types.ThinkingConfig(thinking_level="HIGH"),
|
| 1259 |
+
system_instruction=[types.Part.from_text(text=COLLAPSER_SYSTEM_PROMPT)],
|
| 1260 |
+
temperature=0.2,
|
| 1261 |
+
)
|
| 1262 |
+
|
| 1263 |
+
response = client.models.generate_content(
|
| 1264 |
+
model=GEMINI_MODEL,
|
| 1265 |
+
contents=json.dumps(payload, indent=2),
|
| 1266 |
+
config=config,
|
| 1267 |
+
)
|
| 1268 |
+
|
| 1269 |
+
return response.text
|
| 1270 |
+
|
| 1271 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1272 |
+
# SECTION 13: STREAMING SOLVER RUNNER WITH FEEDBACK LOOP
|
| 1273 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1274 |
+
|
| 1275 |
+
def run_solve_streaming(original_problem_text: str, api_key: str):
|
| 1276 |
+
"""
|
| 1277 |
+
Generator that wires Encode β Compile β PyTorch Solve β Verify β Collapse.
|
| 1278 |
+
Includes the self-correcting feedback loop if Gate 2 (Invariants) fails.
|
| 1279 |
+
"""
|
| 1280 |
+
log_lines: List[str] = []
|
| 1281 |
+
answer_text = ""
|
| 1282 |
+
MAX_ATTEMPTS = 3
|
| 1283 |
+
|
| 1284 |
+
def emit(line: str):
|
| 1285 |
+
log_lines.append(line)
|
| 1286 |
+
return "\n".join(log_lines), answer_text
|
| 1287 |
+
|
| 1288 |
+
current_text = original_problem_text
|
| 1289 |
+
used_key = api_key or DEFAULT_GEMINI_API_KEY
|
| 1290 |
+
|
| 1291 |
+
for attempt in range(1, MAX_ATTEMPTS + 1):
|
| 1292 |
+
yield emit(f"π **Encoding problem (Attempt {attempt}/{MAX_ATTEMPTS})β¦**\n")
|
| 1293 |
+
t0 = time.time()
|
| 1294 |
+
|
| 1295 |
+
try:
|
| 1296 |
+
axl_def = encode_problem_to_axl(current_text, used_key)
|
| 1297 |
+
except Exception as e:
|
| 1298 |
+
yield emit(f"β Encoding failed: {e}\n")
|
| 1299 |
+
return
|
| 1300 |
+
|
| 1301 |
+
enc_ms = round((time.time() - t0) * 1000)
|
| 1302 |
+
yield emit(
|
| 1303 |
+
f"β **Encoded** as `{axl_def.name}` ({enc_ms} ms)\n"
|
| 1304 |
+
f" Variables: `{', '.join(v['name'] for v in axl_def.variables)}`\n"
|
| 1305 |
+
f" Anchors: {len(axl_def.anchors)}\n\n"
|
| 1306 |
+
f"β **Compiling AXL β PSLβ¦**\n"
|
| 1307 |
+
)
|
| 1308 |
+
|
| 1309 |
+
try:
|
| 1310 |
+
base_prob = build_base_problem(axl_def)
|
| 1311 |
+
except Exception as e:
|
| 1312 |
+
yield emit(f"β Compilation failed: {e}\n")
|
| 1313 |
+
return
|
| 1314 |
+
|
| 1315 |
+
yield emit(
|
| 1316 |
+
f"β **Compiled**: {len(base_prob.variables)} vars, "
|
| 1317 |
+
f"{len(base_prob.compiled_constraints)} constraints\n"
|
| 1318 |
+
f" Bilinear pairs: {len(base_prob.bilinear_pairs)}\n"
|
| 1319 |
+
f" Monotone targets: {len(base_prob.monotone_targets)}\n\n"
|
| 1320 |
+
f"π **Running wisdom layer (PyTorch Search)β¦**\n"
|
| 1321 |
+
)
|
| 1322 |
+
|
| 1323 |
+
result_q: queue.Queue = queue.Queue()
|
| 1324 |
+
|
| 1325 |
+
def _worker():
|
| 1326 |
+
try:
|
| 1327 |
+
tracer = SequenceRayTracer()
|
| 1328 |
+
binding, ce, traces, total_fired, allosteric = tracer.trace(axl_def, base_prob)
|
| 1329 |
+
result_q.put(("ok", binding, ce, traces, total_fired, allosteric))
|
| 1330 |
+
except Exception as e:
|
| 1331 |
+
result_q.put(("err", str(e)))
|
| 1332 |
+
|
| 1333 |
+
worker_thread = threading.Thread(target=_worker, daemon=True)
|
| 1334 |
+
worker_thread.start()
|
| 1335 |
+
|
| 1336 |
+
dots = 0
|
| 1337 |
+
while worker_thread.is_alive():
|
| 1338 |
+
worker_thread.join(timeout=3.0)
|
| 1339 |
+
if worker_thread.is_alive():
|
| 1340 |
+
dots += 1
|
| 1341 |
+
yield emit(f" Β·{'Β·' * dots} still searching ({dots * 3}s)β¦\n")
|
| 1342 |
+
|
| 1343 |
+
if result_q.empty():
|
| 1344 |
+
yield emit("β Solver returned no result.\n")
|
| 1345 |
+
return
|
| 1346 |
+
|
| 1347 |
+
result = result_q.get()
|
| 1348 |
+
if result[0] == "err":
|
| 1349 |
+
yield emit(f"β Solver error: {result[1]}\n")
|
| 1350 |
+
return
|
| 1351 |
+
|
| 1352 |
+
_, binding, ce, traces, total_fired, allosteric = result
|
| 1353 |
+
survived = [t for t in traces if t.survived]
|
| 1354 |
+
best_t = min(survived, key=lambda t: t.ce_after, default=None)
|
| 1355 |
+
|
| 1356 |
+
for inv in axl_def.anchors:
|
| 1357 |
+
if not inv.compiled_func:
|
| 1358 |
+
inv.compile(base_prob.variables)
|
| 1359 |
+
|
| 1360 |
+
g1_ce = base_prob.scalar_energy(binding)
|
| 1361 |
+
g1_pass = g1_ce < VERIFY_G1_THRESHOLD
|
| 1362 |
+
inv_results: Dict[str, Tuple[float, bool]] = {}
|
| 1363 |
+
for inv in axl_def.anchors:
|
| 1364 |
+
try:
|
| 1365 |
+
val = float(inv.compiled_func(*[binding.get(v, 0.0) for v in inv.syms_used]))
|
| 1366 |
+
err = abs(val)
|
| 1367 |
+
if inv.mode == "eq": passed = err < inv.tolerance
|
| 1368 |
+
elif inv.mode == "geq": passed = val >= -inv.tolerance
|
| 1369 |
+
else: passed = val <= inv.tolerance
|
| 1370 |
+
inv_results[inv.name] = (round(val, 6), passed)
|
| 1371 |
+
except:
|
| 1372 |
+
inv_results[inv.name] = (999.0, False)
|
| 1373 |
+
g2_pass = all(v[1] for v in inv_results.values())
|
| 1374 |
+
|
| 1375 |
+
solved_str = "β SOLVED" if (g1_pass and g2_pass) else "~ PARTIAL" if g1_pass else "β UNSOLVED"
|
| 1376 |
+
g2_detail = " | ".join(f"{'β' if ok else 'β'} {k}" for k, (_, ok) in inv_results.items())
|
| 1377 |
+
best_ray = best_t.ray_name if best_t else "β"
|
| 1378 |
+
|
| 1379 |
+
yield emit(
|
| 1380 |
+
f"\n{solved_str}\n"
|
| 1381 |
+
f" CE = {ce:.6f} | Gate 1: {'β' if g1_pass else 'β'} | Gate 2: {'β' if g2_pass else 'β'}\n"
|
| 1382 |
+
f" {g2_detail}\n"
|
| 1383 |
+
f" Best ray: `{best_ray}`\n"
|
| 1384 |
+
f" Rays fired: {total_fired:,} | Survived: {len(survived)}\n"
|
| 1385 |
+
f" Allosteric: isolated {allosteric.get('isolated',0):,} | relational {allosteric.get('relational',0):,} | systemic {allosteric.get('systemic',0):,}\n\n"
|
| 1386 |
+
)
|
| 1387 |
+
|
| 1388 |
+
# ββ Self-Correcting Feedback Loop (Gate 2 Failure) βββββββββββββββ
|
| 1389 |
+
if not g2_pass and attempt < MAX_ATTEMPTS:
|
| 1390 |
+
failed_invs = {k: v for k, (v, ok) in inv_results.items() if not ok}
|
| 1391 |
+
current_text = original_problem_text + f"\n\nPREVIOUS ATTEMPT FAILED:\nGate 2 invariants that did not pass:\n{json.dumps(failed_invs, indent=2)}\n\nPlease revise the constraint encoding to better capture these conditions."
|
| 1392 |
+
yield emit(f"β **Gate 2 Failed.** Retrying ({len(failed_invs)} invariants failed) with structural feedback...\n\n---\n")
|
| 1393 |
+
continue
|
| 1394 |
+
|
| 1395 |
+
yield emit(f"π€ **Collapsing to domain languageβ¦**\n")
|
| 1396 |
+
try:
|
| 1397 |
+
answer_text = collapse_solution(
|
| 1398 |
+
original_problem_text, axl_def, binding,
|
| 1399 |
+
g1_pass, g2_pass, inv_results,
|
| 1400 |
+
best_ray, total_fired, used_key
|
| 1401 |
+
)
|
| 1402 |
+
yield emit("β Done.\n")
|
| 1403 |
+
except Exception as e:
|
| 1404 |
+
answer_text = f"Error during collapsing: {e}"
|
| 1405 |
+
yield emit("β Collapsing failed.\n")
|
| 1406 |
+
|
| 1407 |
+
break
|
| 1408 |
+
|
| 1409 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1410 |
+
# SECTION 14: GRADIO INTERFACE
|
| 1411 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1412 |
+
|
| 1413 |
+
CSS = """
|
| 1414 |
+
body { background: #090909; color: #e0e0e0; font-family: monospace; }
|
| 1415 |
+
.solver-log { font-size: 0.75em; background: #0d0d0d; border: 1px solid #181818; }
|
| 1416 |
+
.answer-box { font-size: 0.85em; background: #0d0d0d; border: 1px solid #222; }
|
| 1417 |
+
.status-bar { font-size: 0.70em; color: #555; padding: 4px 0; }
|
| 1418 |
+
"""
|
| 1419 |
+
|
| 1420 |
+
PLACEHOLDER_HINT = textwrap.dedent("""
|
| 1421 |
+
Try any problem description. Gemini will extract the mathematical structure.
|
| 1422 |
+
|
| 1423 |
+
Example inputs:
|
| 1424 |
+
β’ "Find points on the elliptic curve yΒ²=xΒ³-x+1 where x=0"
|
| 1425 |
+
β’ "Solve a DC power flow problem with 3 buses where p1+p2+p3=1"
|
| 1426 |
+
β’ "Markowitz portfolio with 4 assets, returns [10%, 12%, 8%, 15%], target return 11%"
|
| 1427 |
+
""").strip()
|
| 1428 |
+
|
| 1429 |
+
with gr.Blocks(css=CSS, title="Practicality Unified Server") as demo:
|
| 1430 |
+
|
| 1431 |
+
gr.Markdown(
|
| 1432 |
+
"## β Practicality Unified Server\n"
|
| 1433 |
+
"`[Gemini 3.1 Encoder] β AXL/PSL β [PyTorch Ray Solver] β [Gemini 3.1 Collapser]`"
|
| 1434 |
+
)
|
| 1435 |
+
|
| 1436 |
+
with gr.Row():
|
| 1437 |
+
with gr.Column(scale=2):
|
| 1438 |
+
api_key_input = gr.Textbox(
|
| 1439 |
+
label="Gemini API Key",
|
| 1440 |
+
type="password",
|
| 1441 |
+
placeholder="AIza...",
|
| 1442 |
+
value=DEFAULT_GEMINI_API_KEY,
|
| 1443 |
+
elem_classes=["solver-log"]
|
| 1444 |
+
)
|
| 1445 |
+
problem_input = gr.Textbox(
|
| 1446 |
+
label="Problem Description",
|
| 1447 |
+
placeholder=PLACEHOLDER_HINT,
|
| 1448 |
+
lines=5,
|
| 1449 |
+
elem_classes=["solver-log"],
|
| 1450 |
+
)
|
| 1451 |
+
with gr.Row():
|
| 1452 |
+
solve_btn = gr.Button("β Solve", variant="primary")
|
| 1453 |
+
clear_btn = gr.Button("Clear")
|
| 1454 |
+
|
| 1455 |
+
with gr.Column(scale=1):
|
| 1456 |
+
gr.Markdown("**Device & Mode**")
|
| 1457 |
+
gr.Textbox(
|
| 1458 |
+
value=f"Compute: {DEVICE.type.upper()}\n"
|
| 1459 |
+
f"Axioms: {len(ALL_AXIOMS)}\n"
|
| 1460 |
+
f"Max rays: {MAX_TOTAL_RAYS:,}\n"
|
| 1461 |
+
f"Batch: {RAY_BATCH_SIZE:,}\n"
|
| 1462 |
+
f"LLM: {GEMINI_MODEL}",
|
| 1463 |
+
lines=5, interactive=False,
|
| 1464 |
+
elem_classes=["solver-log"],
|
| 1465 |
+
)
|
| 1466 |
+
|
| 1467 |
+
with gr.Row():
|
| 1468 |
+
solver_log = gr.Textbox(
|
| 1469 |
+
label="Solver Log",
|
| 1470 |
+
lines=18,
|
| 1471 |
+
interactive=False,
|
| 1472 |
+
elem_classes=["solver-log"],
|
| 1473 |
+
)
|
| 1474 |
+
answer_box = gr.Textbox(
|
| 1475 |
+
label="Answer",
|
| 1476 |
+
lines=18,
|
| 1477 |
+
interactive=False,
|
| 1478 |
+
elem_classes=["answer-box"],
|
| 1479 |
+
)
|
| 1480 |
+
|
| 1481 |
+
def _solve(problem_text: str, api_key: str):
|
| 1482 |
+
if not problem_text.strip():
|
| 1483 |
+
yield "No problem text provided.", ""
|
| 1484 |
+
return
|
| 1485 |
+
for log_chunk, answer_chunk in run_solve_streaming(problem_text, api_key):
|
| 1486 |
+
yield log_chunk, answer_chunk
|
| 1487 |
+
|
| 1488 |
+
solve_btn.click(
|
| 1489 |
+
fn=_solve,
|
| 1490 |
+
inputs=[problem_input, api_key_input],
|
| 1491 |
+
outputs=[solver_log, answer_box],
|
| 1492 |
+
show_progress=False,
|
| 1493 |
+
)
|
| 1494 |
+
|
| 1495 |
+
clear_btn.click(
|
| 1496 |
+
fn=lambda: ("", "", ""),
|
| 1497 |
+
inputs=[],
|
| 1498 |
+
outputs=[problem_input, solver_log, answer_box],
|
| 1499 |
+
)
|
| 1500 |
+
|
| 1501 |
+
gr.Markdown(
|
| 1502 |
+
"Built with Gradio Β· Practicality 22.1 Β· Strict Neuro-Symbolic Boundary",
|
| 1503 |
+
elem_classes=["status-bar"]
|
| 1504 |
+
)
|
| 1505 |
+
|
| 1506 |
+
demo.queue()
|
| 1507 |
+
|
| 1508 |
+
if __name__ == "__main__":
|
| 1509 |
+
print(f"[UNIFIED SERVER] Compute: {DEVICE.type.upper()}")
|
| 1510 |
+
print(f"[UNIFIED SERVER] System 22.1: GenAI SDK wired. Model: {GEMINI_MODEL}")
|
| 1511 |
+
demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
|