clm-kernel 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- clm/__init__.py +230 -0
- clm/__main__.py +25 -0
- clm/cross_cutting/__init__.py +50 -0
- clm/cross_cutting/_test_runner.py +36 -0
- clm/cross_cutting/async_utils.py +25 -0
- clm/cross_cutting/cel_builder.py +123 -0
- clm/cross_cutting/cel_converter.py +697 -0
- clm/cross_cutting/cel_engine.py +252 -0
- clm/cross_cutting/cel_ops.py +72 -0
- clm/cross_cutting/cel_ops_db.py +157 -0
- clm/cross_cutting/cel_ops_files.py +99 -0
- clm/cross_cutting/cel_ops_state.py +71 -0
- clm/cross_cutting/cel_ops_strings.py +64 -0
- clm/cross_cutting/cel_resolver.py +63 -0
- clm/cross_cutting/cel_slices.py +56 -0
- clm/cross_cutting/cel_store.py +330 -0
- clm/cross_cutting/clm_logger.py +140 -0
- clm/cross_cutting/config/__init__.py +155 -0
- clm/cross_cutting/config/config_constants.py +392 -0
- clm/cross_cutting/config/env_parameters.py +90 -0
- clm/cross_cutting/config/logging.py +378 -0
- clm/cross_cutting/config/settings.py +504 -0
- clm/cross_cutting/context.py +5 -0
- clm/cross_cutting/domain_models.py +55 -0
- clm/cross_cutting/domain_types.py +61 -0
- clm/cross_cutting/effects.py +67 -0
- clm/cross_cutting/errors.py +153 -0
- clm/cross_cutting/governance.py +331 -0
- clm/cross_cutting/guards.py +41 -0
- clm/cross_cutting/mcard.py +321 -0
- clm/cross_cutting/native.py +185 -0
- clm/cross_cutting/observability.py +289 -0
- clm/cross_cutting/pcard.py +434 -0
- clm/cross_cutting/registry.py +71 -0
- clm/cross_cutting/resources.py +84 -0
- clm/cross_cutting/secrets.py +44 -0
- clm/cross_cutting/subprocess_utils.py +118 -0
- clm/cross_cutting/telemetry.py +164 -0
- clm/cross_cutting/testing/__init__.py +8 -0
- clm/cross_cutting/testing/comparator.py +187 -0
- clm/cross_cutting/testing/datasets.py +87 -0
- clm/cross_cutting/timeutil.py +20 -0
- clm/cross_cutting/types/new_type_check.py +0 -0
- clm/cross_cutting/utils/__init__.py +1 -0
- clm/cross_cutting/utils/url_safety.py +50 -0
- clm/cross_cutting/vcard.py +560 -0
- clm/cross_cutting/writer.py +118 -0
- clm/layer0/README.md +50 -0
- clm/layer0/__init__.py +223 -0
- clm/layer0/algebra.py +60 -0
- clm/layer0/algebra_baldwin.py +133 -0
- clm/layer0/algebra_baldwin_generators.py +36 -0
- clm/layer0/algebra_equivalence.py +262 -0
- clm/layer0/algebra_ingest.py +306 -0
- clm/layer0/algebra_manifest_io.py +156 -0
- clm/layer0/assembler.py +481 -0
- clm/layer0/cel/__init__.py +23 -0
- clm/layer0/cel_eval.py +491 -0
- clm/layer0/codec.py +106 -0
- clm/layer0/context.py +122 -0
- clm/layer0/db.py +160 -0
- clm/layer0/exceptions.py +73 -0
- clm/layer0/fibration.py +106 -0
- clm/layer0/gates.py +50 -0
- clm/layer0/hash.py +80 -0
- clm/layer0/loader.py +826 -0
- clm/layer0/math_utils.py +163 -0
- clm/layer0/mcard.py +96 -0
- clm/layer0/model/__init__.py +0 -0
- clm/layer0/model/action.py +223 -0
- clm/layer0/model/card.py +280 -0
- clm/layer0/model/card_triad.py +427 -0
- clm/layer0/model/compatibility.py +60 -0
- clm/layer0/model/dictionary.py +46 -0
- clm/layer0/model/dots.py +331 -0
- clm/layer0/model/event_producer.py +88 -0
- clm/layer0/model/g_time.py +89 -0
- clm/layer0/model/handle.py +163 -0
- clm/layer0/model/hash/__init__.py +6 -0
- clm/layer0/model/hash/algorithms/__init__.py +3 -0
- clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
- clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
- clm/layer0/model/hash/constants.py +53 -0
- clm/layer0/model/hash/enums.py +60 -0
- clm/layer0/model/hash/validator.py +307 -0
- clm/layer0/model/interpreter.py +275 -0
- clm/layer0/model/pagination.py +45 -0
- clm/layer0/model/pcard.py +716 -0
- clm/layer0/model/schema.py +44 -0
- clm/layer0/model/utils/__init__.py +1 -0
- clm/layer0/model/utils/content_analyzer.py +152 -0
- clm/layer0/model/validators/__init__.py +1 -0
- clm/layer0/model/validators/base_validator.py +44 -0
- clm/layer0/model/validators/binary_validator.py +66 -0
- clm/layer0/model/validators/text_validator.py +82 -0
- clm/layer0/model/validators/validation_registry.py +49 -0
- clm/layer0/model/vcard.py +823 -0
- clm/layer0/model/vcard_ext/__init__.py +39 -0
- clm/layer0/model/vcard_ext/core.py +60 -0
- clm/layer0/model/vcard_ext/network.py +23 -0
- clm/layer0/model/vcard_ext/observability.py +63 -0
- clm/layer0/model/vcard_ext/storage.py +60 -0
- clm/layer0/model/vcard_ext/vendors.py +211 -0
- clm/layer0/model/vcard_sandwich.py +119 -0
- clm/layer0/model/vcard_vocabulary.py +463 -0
- clm/layer0/model/workflow.py +274 -0
- clm/layer0/narrative.py +431 -0
- clm/layer0/ontology/collection.py +125 -0
- clm/layer0/parser.py +429 -0
- clm/layer0/relations.py +73 -0
- clm/layer0/rule_evaluator.py +251 -0
- clm/layer0/schema/__init__.py +38 -0
- clm/layer0/schema/ddl.py +97 -0
- clm/layer0/templates/abstract.yaml +99 -0
- clm/layer0/templates/balanced.yaml +253 -0
- clm/layer0/templates/concrete.yaml +172 -0
- clm/layer0/type_registry.py +223 -0
- clm/layer0/types.py +204 -0
- clm/layer0/utils.py +886 -0
- clm/layer0/verifier_core.py +262 -0
- clm/layer1/__init__.py +31 -0
- clm/layer1/action_dispatcher.py +275 -0
- clm/layer1/adapter_registry.py +233 -0
- clm/layer1/algebra_baldwin_generators.py +192 -0
- clm/layer1/arrow_compiler.py +525 -0
- clm/layer1/batch_processor.py +152 -0
- clm/layer1/cel/__init__.py +27 -0
- clm/layer1/cel/builder.py +173 -0
- clm/layer1/engine.py +759 -0
- clm/layer1/engine_async.py +89 -0
- clm/layer1/eval_callback.py +49 -0
- clm/layer1/gate_composer.py +229 -0
- clm/layer1/hoare_sandwich.py +298 -0
- clm/layer1/identity_guard.py +348 -0
- clm/layer1/matrix.py +138 -0
- clm/layer1/neural_runtime.py +221 -0
- clm/layer1/operations/__init__.py +123 -0
- clm/layer1/operations/augment.py +156 -0
- clm/layer1/operations/base.py +109 -0
- clm/layer1/operations/builtins.py +338 -0
- clm/layer1/operations/dispatch.py +144 -0
- clm/layer1/operations/exclude.py +107 -0
- clm/layer1/operations/handle.py +197 -0
- clm/layer1/operations/identity.py +139 -0
- clm/layer1/operations/invert.py +66 -0
- clm/layer1/operations/loader.py +116 -0
- clm/layer1/operations/manipulators/__init__.py +23 -0
- clm/layer1/operations/manipulators/document.py +211 -0
- clm/layer1/operations/manipulators/media.py +134 -0
- clm/layer1/operations/manipulators/structured.py +159 -0
- clm/layer1/operations/manipulators/tabular.py +334 -0
- clm/layer1/operations/port.py +65 -0
- clm/layer1/operations/services.py +529 -0
- clm/layer1/operations/split.py +117 -0
- clm/layer1/operations/substitute.py +75 -0
- clm/layer1/petri.py +1052 -0
- clm/layer1/pocketflow_orchestrator.py +408 -0
- clm/layer1/pocketflow_scheduler.py +519 -0
- clm/layer1/protocols.py +197 -0
- clm/layer1/runtime_adapter.py +718 -0
- clm/layer1/runtimes/__init__.py +71 -0
- clm/layer1/runtimes/_generated_assertions.py +125 -0
- clm/layer1/runtimes/_generated_types.py +42 -0
- clm/layer1/runtimes/base.py +265 -0
- clm/layer1/runtimes/binary.py +101 -0
- clm/layer1/runtimes/factory.py +203 -0
- clm/layer1/runtimes/javascript.py +321 -0
- clm/layer1/runtimes/javascript_runtime.js +33 -0
- clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
- clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
- clm/layer1/runtimes/lambda_calc.py +126 -0
- clm/layer1/runtimes/python.py +288 -0
- clm/layer1/runtimes/python_runtime.py +41 -0
- clm/layer1/runtimes/script.py +114 -0
- clm/layer1/sandbox.py +334 -0
- clm/layer1/savepoint_guard.py +136 -0
- clm/layer1/sparse_net.py +122 -0
- clm/layer1/spec_tester.py +165 -0
- clm/layer1/store.py +295 -0
- clm/layer1/test_runner.py +498 -0
- clm/layer1/type_inspector.py +166 -0
- clm/layer1/verifier.py +772 -0
- clm/layer1/vm.py +91 -0
- clm/layer2/__init__.py +78 -0
- clm/layer2/card_collection.py +678 -0
- clm/layer2/classifier.py +479 -0
- clm/layer2/engine/__init__.py +1 -0
- clm/layer2/engine/abstract_sql_engine.py +320 -0
- clm/layer2/engine/base.py +193 -0
- clm/layer2/engine/duckdb_engine.py +576 -0
- clm/layer2/engine/sqlite_engine.py +518 -0
- clm/layer2/fiber/__init__.py +31 -0
- clm/layer2/fiber/context.py +89 -0
- clm/layer2/fiber/disposable.py +163 -0
- clm/layer2/fiber/fiber.py +125 -0
- clm/layer2/fiber/hoare.py +191 -0
- clm/layer2/file_io.py +376 -0
- clm/layer2/file_registrar.py +77 -0
- clm/layer2/ingest.py +202 -0
- clm/layer2/lineage.py +322 -0
- clm/layer2/mcard_fs.py +262 -0
- clm/layer2/merkle.py +11 -0
- clm/layer2/mime.py +244 -0
- clm/layer2/mime_detector.py +245 -0
- clm/layer2/private_collection.py +118 -0
- clm/layer2/shared_store.py +11 -0
- clm/layer2/storage/__init__.py +190 -0
- clm/layer2/storage/db_bitemporal.py +141 -0
- clm/layer2/storage/db_driver.py +331 -0
- clm/layer2/storage/db_hyperlinks.py +214 -0
- clm/layer2/storage/db_pool.py +139 -0
- clm/layer2/storage/db_repository.py +229 -0
- clm/layer2/storage/savepoint.py +78 -0
- clm/layer2/storage/sqlite_wal.py +74 -0
- clm/layer2/tridb.py +245 -0
- clm/layer2/vcard_receipt.py +71 -0
- clm/layer2/vfs.py +17 -0
- clm/layer3/__init__.py +141 -0
- clm/layer3/agency/__init__.py +19 -0
- clm/layer3/agency/config.py +220 -0
- clm/layer3/agency/providers/__init__.py +15 -0
- clm/layer3/agency/providers/base.py +91 -0
- clm/layer3/agency/providers/mlc_llm.py +169 -0
- clm/layer3/agency/providers/ollama.py +203 -0
- clm/layer3/agency/router.py +71 -0
- clm/layer3/agency/runtime.py +524 -0
- clm/layer3/gateway.py +353 -0
- clm/layer3/open_interpreter_adapter.py +159 -0
- clm/layer3/rag/__init__.py +70 -0
- clm/layer3/rag/cli.py +228 -0
- clm/layer3/rag/config.py +153 -0
- clm/layer3/rag/embeddings/__init__.py +16 -0
- clm/layer3/rag/embeddings/base.py +79 -0
- clm/layer3/rag/embeddings/ollama.py +211 -0
- clm/layer3/rag/embeddings/vision.py +290 -0
- clm/layer3/rag/engine.py +276 -0
- clm/layer3/rag/graph/__init__.py +22 -0
- clm/layer3/rag/graph/community.py +198 -0
- clm/layer3/rag/graph/engine.py +433 -0
- clm/layer3/rag/graph/extractor.py +255 -0
- clm/layer3/rag/graph/schema.py +115 -0
- clm/layer3/rag/graph/store.py +641 -0
- clm/layer3/rag/indexer.py +333 -0
- clm/layer3/rag/llm_providers.json +52 -0
- clm/layer3/rag/semantic_versioning.py +313 -0
- clm/layer3/rag/vector/__init__.py +28 -0
- clm/layer3/rag/vector/handle_vector_store.py +749 -0
- clm/layer3/rag/vector/schema.py +151 -0
- clm/layer3/rag/vector/store.py +632 -0
- clm/layer3/reticulum/__init__.py +55 -0
- clm/layer3/reticulum/bridge.py +74 -0
- clm/layer3/reticulum/daemon.py +88 -0
- clm/layer3/reticulum/discovery.py +164 -0
- clm/layer3/reticulum/identity.py +107 -0
- clm/layer3/reticulum/media.py +148 -0
- clm/layer3/reticulum/transport.py +133 -0
- clm/layer3/satori/__init__.py +209 -0
- clm/layer3/satori/alpha_conversion.py +168 -0
- clm/layer3/satori/beta_reduction.py +359 -0
- clm/layer3/satori/continuation.py +38 -0
- clm/layer3/satori/eta_conversion.py +205 -0
- clm/layer3/satori/free_variables.py +163 -0
- clm/layer3/satori/io_effects.py +370 -0
- clm/layer3/satori/lambda_runtime.py +726 -0
- clm/layer3/satori/lambda_term.py +230 -0
- clm/layer3/satori/sheaf_audit.py +261 -0
- clm/layer3/satori/speech_act.py +279 -0
- clm/layer4/README.md +9 -0
- clm/layer4/__init__.py +67 -0
- clm/layer4/_sovereign_executor.py +580 -0
- clm/layer4/action_commands.py +719 -0
- clm/layer4/api.py +236 -0
- clm/layer4/bootstrap/__init__.py +13 -0
- clm/layer4/bootstrap/bootstrapper.py +118 -0
- clm/layer4/bootstrap/cleanup.py +44 -0
- clm/layer4/bootstrap/clm_hooks.py +49 -0
- clm/layer4/bootstrap/genesis.py +77 -0
- clm/layer4/bootstrap/injection.py +125 -0
- clm/layer4/cli.py +549 -0
- clm/layer4/collection_manager.py +553 -0
- clm/layer4/commands/__init__.py +81 -0
- clm/layer4/commands/action_cmd.py +42 -0
- clm/layer4/commands/check_cmd.py +82 -0
- clm/layer4/commands/envelope.py +73 -0
- clm/layer4/commands/evaluate_cmd.py +224 -0
- clm/layer4/commands/mcard_cmd.py +24 -0
- clm/layer4/commands/petri_cmd.py +92 -0
- clm/layer4/commands/tridb_cmd.py +319 -0
- clm/layer4/commands/version_cmd.py +27 -0
- clm/layer4/diff_commands.py +167 -0
- clm/layer4/eoa_sponsor.py +232 -0
- clm/layer4/events.py +69 -0
- clm/layer4/flux_gateway.py +133 -0
- clm/layer4/gateway.py +91 -0
- clm/layer4/loader.py +307 -0
- clm/layer4/mcard_commands.py +166 -0
- clm/layer4/membrane.py +273 -0
- clm/layer4/navigation.py +50 -0
- clm/layer4/protocol/protocol_entry_python.py +108 -0
- clm/layer4/protocol/python_sync.py +126 -0
- clm/layer4/runner.py +150 -0
- clm/layer4/satori.py +262 -0
- clm/layer4/server/python_sync_client.py +150 -0
- clm/layer4/server/python_sync_server.py +139 -0
- clm/layer4/server/service_api.py +315 -0
- clm/layer4/server/start_servers.py +107 -0
- clm/layer4/server/stop_servers.py +24 -0
- clm/layer4/simulated_optimization_fix.py +0 -0
- clm/layer4/storage.py +463 -0
- clm/layer4/tridb_admin.py +268 -0
- clm/layer4/websocket/test_ws_client.py +104 -0
- clm/layer4/websocket/test_ws_server.py +138 -0
- clm/layer4/websocket/ws_server_local.py +160 -0
- clm/layer5/__init__.py +51 -0
- clm/layer5/async_batch_writer.py +133 -0
- clm/layer5/learning.py +276 -0
- clm/layer5/telemetry_hook.py +107 -0
- clm/layer5/type_lattice.py +238 -0
- clm/py.typed +0 -0
- clm/schemas/__init__.py +652 -0
- clm/schemas/abstract_face_schema.yaml +77 -0
- clm/schemas/cel_schema.yaml +416 -0
- clm/schemas/lifecycle/__init__.py +45 -0
- clm/schemas/lifecycle/models.py +123 -0
- clm/schemas/lifecycle/service.py +364 -0
- clm/schemas/mcard_schema.sql +192 -0
- clm/schemas/mcard_vector_schema.sql +283 -0
- clm/schemas/mlp-context-v1.json +91 -0
- clm_kernel-0.0.0.dist-info/METADATA +121 -0
- clm_kernel-0.0.0.dist-info/RECORD +333 -0
- clm_kernel-0.0.0.dist-info/WHEEL +4 -0
- clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
- clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
clm/layer1/sandbox.py
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Sandbox execution environment for PCards with multi-language runtime support.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from clm.layer0.model.card import MCard
|
|
12
|
+
from clm.layer0.narrative import IO, Either, Left, Right
|
|
13
|
+
from clm.layer1.runtimes import RuntimeFactory
|
|
14
|
+
from clm.layer1.store import PTRSharedStore
|
|
15
|
+
|
|
16
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
# Configuration
|
|
18
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
DEFAULT_RUNTIME = "python"
|
|
21
|
+
|
|
22
|
+
# Error message patterns for system errors (these should raise exceptions)
|
|
23
|
+
SYSTEM_ERROR_PATTERNS = (
|
|
24
|
+
"Failed to parse PCard",
|
|
25
|
+
"Sandbox execution failed",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Fallback resolution order
|
|
29
|
+
FALLBACK_SOURCES = ("mcard", "file", "url")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
33
|
+
# Code Resolution Strategies
|
|
34
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CodeResolver:
|
|
38
|
+
"""Resolves code from multiple sources with fallback chain."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, collection=None, logger=None):
|
|
41
|
+
self.collection = collection
|
|
42
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
def resolve(
|
|
45
|
+
self, concrete_impl: dict, context: dict | PTRSharedStore
|
|
46
|
+
) -> Either[str, str | None]:
|
|
47
|
+
"""
|
|
48
|
+
Resolve code from available sources in priority order.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Right(code) on success, Left(error) on failure
|
|
52
|
+
"""
|
|
53
|
+
# Already have code
|
|
54
|
+
if concrete_impl.get("code"):
|
|
55
|
+
return Right(concrete_impl["code"])
|
|
56
|
+
|
|
57
|
+
# No code hash means no content-addressed lookup needed
|
|
58
|
+
code_hash = concrete_impl.get("code_hash")
|
|
59
|
+
|
|
60
|
+
# If no code source is specified, return None (success, no code needed)
|
|
61
|
+
if not any(
|
|
62
|
+
[
|
|
63
|
+
code_hash,
|
|
64
|
+
concrete_impl.get("code_file"),
|
|
65
|
+
concrete_impl.get("fallback_file"),
|
|
66
|
+
concrete_impl.get("fallback_url"),
|
|
67
|
+
]
|
|
68
|
+
):
|
|
69
|
+
return Right(None)
|
|
70
|
+
|
|
71
|
+
# Try resolution sources in order
|
|
72
|
+
resolvers = [
|
|
73
|
+
("mcard", lambda: self._from_mcard(code_hash) if code_hash else None),
|
|
74
|
+
(
|
|
75
|
+
"file",
|
|
76
|
+
lambda: self._from_file(
|
|
77
|
+
concrete_impl.get("code_file")
|
|
78
|
+
or concrete_impl.get("fallback_file"),
|
|
79
|
+
context,
|
|
80
|
+
code_hash or "unknown",
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
(
|
|
84
|
+
"url",
|
|
85
|
+
lambda: self._from_url(
|
|
86
|
+
concrete_impl.get("fallback_url"), code_hash or "unknown"
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
for _source_name, resolver in resolvers:
|
|
92
|
+
result = resolver()
|
|
93
|
+
if result is not None:
|
|
94
|
+
return Right(result)
|
|
95
|
+
|
|
96
|
+
return Left(
|
|
97
|
+
f"Error: Code resolution failed. MCard {code_hash} not found, and fallbacks failed."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def _from_mcard(self, code_hash: str) -> str | None:
|
|
101
|
+
"""Try to resolve code from MCard collection."""
|
|
102
|
+
if not self.collection:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
code_card = self.collection.get(code_hash)
|
|
106
|
+
if code_card:
|
|
107
|
+
self.logger.info(f"Resolved code from MCard: {code_hash}")
|
|
108
|
+
return code_card.get_content().decode("utf-8")
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
def _from_file(
|
|
112
|
+
self, fallback_file: str | None, context: dict | PTRSharedStore, code_hash: str
|
|
113
|
+
) -> str | None:
|
|
114
|
+
"""Try to resolve code from fallback file."""
|
|
115
|
+
if not fallback_file:
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
path = self._resolve_file_path(fallback_file, context)
|
|
120
|
+
if path and path.exists():
|
|
121
|
+
self.logger.warning(
|
|
122
|
+
f"MCard not found for hash {code_hash}, loaded from fallback file: {fallback_file}"
|
|
123
|
+
)
|
|
124
|
+
return path.read_text()
|
|
125
|
+
self.logger.warning(f"Fallback file not found: {path}")
|
|
126
|
+
except OSError as e:
|
|
127
|
+
self.logger.error(
|
|
128
|
+
f"Failed to load fallback file {fallback_file}: {e}", exc_info=True
|
|
129
|
+
)
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
def _from_url(self, fallback_url: str | None, code_hash: str) -> str | None:
|
|
133
|
+
"""Try to resolve code from fallback URL."""
|
|
134
|
+
if not fallback_url:
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
import urllib.request
|
|
139
|
+
|
|
140
|
+
with urllib.request.urlopen(fallback_url) as response: # nosec B310
|
|
141
|
+
status = getattr(response, "status", None) or getattr(
|
|
142
|
+
response, "code", 200
|
|
143
|
+
)
|
|
144
|
+
if status == 200:
|
|
145
|
+
self.logger.warning(
|
|
146
|
+
f"Loaded code from fallback URL: {fallback_url}"
|
|
147
|
+
)
|
|
148
|
+
return response.read().decode("utf-8")
|
|
149
|
+
self.logger.error(
|
|
150
|
+
f"Failed to fetch fallback URL {fallback_url}: Status {status}"
|
|
151
|
+
)
|
|
152
|
+
except (OSError, ValueError, UnicodeDecodeError) as e:
|
|
153
|
+
self.logger.error(
|
|
154
|
+
f"Failed to fetch fallback URL {fallback_url}: {e}", exc_info=True
|
|
155
|
+
)
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def _resolve_file_path(
|
|
160
|
+
file_path: str, context: dict | PTRSharedStore
|
|
161
|
+
) -> Path | None:
|
|
162
|
+
"""Resolve a file path, handling relative paths."""
|
|
163
|
+
path = Path(file_path)
|
|
164
|
+
if path.is_absolute():
|
|
165
|
+
return path
|
|
166
|
+
|
|
167
|
+
# Try pcard_dir first, then cwd
|
|
168
|
+
context_dict = (
|
|
169
|
+
context.inputs if isinstance(context, PTRSharedStore) else context
|
|
170
|
+
)
|
|
171
|
+
pcard_dir = context_dict.get("pcard_dir") if context_dict else None
|
|
172
|
+
if pcard_dir:
|
|
173
|
+
candidate = Path(pcard_dir) / path
|
|
174
|
+
if candidate.exists():
|
|
175
|
+
return candidate
|
|
176
|
+
|
|
177
|
+
return Path.cwd() / path
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
181
|
+
# Sandbox Executor
|
|
182
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class SandboxExecutor:
|
|
186
|
+
"""
|
|
187
|
+
Executes PCard logic in a sandboxed environment with multi-language support.
|
|
188
|
+
|
|
189
|
+
The sandbox dynamically selects the appropriate runtime (Python, JavaScript,
|
|
190
|
+
Rust, C, etc.) based on the Concrete Implementation specification in the PCard.
|
|
191
|
+
|
|
192
|
+
Production deployments should use:
|
|
193
|
+
- WebAssembly (WASM) for true sandboxing
|
|
194
|
+
- Container isolation (Docker/Kubernetes)
|
|
195
|
+
- Resource limits and timeout enforcement
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
def __init__(self, collection=None):
|
|
199
|
+
self.logger = logging.getLogger(__name__)
|
|
200
|
+
self.collection = collection
|
|
201
|
+
self._code_resolver = CodeResolver(collection, self.logger)
|
|
202
|
+
|
|
203
|
+
def execute(
|
|
204
|
+
self, pcard: MCard, target: MCard, context: dict[str, Any] | PTRSharedStore
|
|
205
|
+
) -> Any:
|
|
206
|
+
"""
|
|
207
|
+
Execute PCard logic using the specified runtime.
|
|
208
|
+
|
|
209
|
+
The runtime is determined from the Concrete Implementation's 'runtime' field.
|
|
210
|
+
Defaults to 'python' if not specified.
|
|
211
|
+
"""
|
|
212
|
+
result_either = self.execute_monad(pcard, target, context).unsafe_run()
|
|
213
|
+
|
|
214
|
+
if result_either.is_left():
|
|
215
|
+
error_msg = result_either.value
|
|
216
|
+
if self._is_system_error(error_msg):
|
|
217
|
+
raise RuntimeError(error_msg)
|
|
218
|
+
return error_msg
|
|
219
|
+
return result_either.value
|
|
220
|
+
|
|
221
|
+
async def aexecute(
|
|
222
|
+
self, pcard: MCard, target: MCard, context: dict[str, Any] | PTRSharedStore
|
|
223
|
+
) -> Any:
|
|
224
|
+
"""Async wrapper for execute() using a thread pool.
|
|
225
|
+
|
|
226
|
+
This provides an awaitable interface for sandbox execution without
|
|
227
|
+
altering runtime semantics. When true async runtimes are available,
|
|
228
|
+
this method can be updated to call them directly.
|
|
229
|
+
"""
|
|
230
|
+
import asyncio
|
|
231
|
+
|
|
232
|
+
return await asyncio.to_thread(self.execute, pcard, target, context)
|
|
233
|
+
|
|
234
|
+
def execute_monad(
|
|
235
|
+
self, pcard: MCard, target: MCard, context: dict[str, Any] | PTRSharedStore
|
|
236
|
+
) -> IO[Either[str, Any]]:
|
|
237
|
+
"""Monadic execution pipeline returning IO[Either[Error, Result]]."""
|
|
238
|
+
|
|
239
|
+
def run() -> Either[str, Any]:
|
|
240
|
+
try:
|
|
241
|
+
return self._pipeline(pcard, target, context)
|
|
242
|
+
except Exception as e:
|
|
243
|
+
return Left(f"Sandbox execution failed: {e}")
|
|
244
|
+
|
|
245
|
+
return IO(run)
|
|
246
|
+
|
|
247
|
+
async def execute_monad_async(
|
|
248
|
+
self, pcard: MCard, target: MCard, context: dict[str, Any] | PTRSharedStore
|
|
249
|
+
) -> Either[str, Any]:
|
|
250
|
+
"""Async wrapper that returns Either by running the monadic pipeline in a thread pool."""
|
|
251
|
+
import asyncio
|
|
252
|
+
|
|
253
|
+
return await asyncio.to_thread(
|
|
254
|
+
lambda: self.execute_monad(pcard, target, context).unsafe_run()
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def _pipeline(
|
|
258
|
+
self, pcard: MCard, target: MCard, context: dict | PTRSharedStore
|
|
259
|
+
) -> Either[str, Any]:
|
|
260
|
+
"""Execute the main processing pipeline."""
|
|
261
|
+
# 1. Parse PCard
|
|
262
|
+
pcard_data = self._parse_pcard(pcard)
|
|
263
|
+
if pcard_data.is_left():
|
|
264
|
+
return pcard_data
|
|
265
|
+
|
|
266
|
+
clm_base = pcard_data.value.get("clm", pcard_data.value)
|
|
267
|
+
concrete_impl = clm_base.get("concrete_impl", clm_base.get("concrete", {}))
|
|
268
|
+
|
|
269
|
+
# 2. Resolve Code
|
|
270
|
+
code_result = self._code_resolver.resolve(concrete_impl, context)
|
|
271
|
+
if code_result.is_left():
|
|
272
|
+
return code_result
|
|
273
|
+
|
|
274
|
+
if code_result.value:
|
|
275
|
+
concrete_impl["code"] = code_result.value
|
|
276
|
+
|
|
277
|
+
# 3. Get Executor
|
|
278
|
+
runtime_type = concrete_impl.get("runtime", DEFAULT_RUNTIME)
|
|
279
|
+
self.logger.info(f"Executing PCard with runtime: {runtime_type}")
|
|
280
|
+
|
|
281
|
+
executor = RuntimeFactory.get_executor(runtime_type)
|
|
282
|
+
if not executor:
|
|
283
|
+
return Left(
|
|
284
|
+
f"Error: Runtime '{runtime_type}' is not available or not supported"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# 4. Execute
|
|
288
|
+
return self._execute_runtime(executor, concrete_impl, target, context)
|
|
289
|
+
|
|
290
|
+
def _parse_pcard(self, pcard: MCard) -> Either[str, dict]:
|
|
291
|
+
"""Parse PCard YAML content."""
|
|
292
|
+
try:
|
|
293
|
+
import yaml
|
|
294
|
+
|
|
295
|
+
return Right(yaml.safe_load(pcard.get_content().decode("utf-8")))
|
|
296
|
+
except (yaml.YAMLError, UnicodeDecodeError, ValueError, TypeError) as e:
|
|
297
|
+
return Left(f"Failed to parse PCard: {e}")
|
|
298
|
+
|
|
299
|
+
def _execute_runtime(
|
|
300
|
+
self,
|
|
301
|
+
executor: Any,
|
|
302
|
+
concrete_impl: dict,
|
|
303
|
+
target: MCard,
|
|
304
|
+
context: dict | PTRSharedStore,
|
|
305
|
+
) -> Either[str, Any]:
|
|
306
|
+
"""Execute using the selected runtime."""
|
|
307
|
+
try:
|
|
308
|
+
context_dict = (
|
|
309
|
+
context.inputs if isinstance(context, PTRSharedStore) else context
|
|
310
|
+
)
|
|
311
|
+
result = executor.execute(concrete_impl, target, context_dict)
|
|
312
|
+
if isinstance(result, str) and result.startswith("Error:"):
|
|
313
|
+
return Left(result)
|
|
314
|
+
return Right(result)
|
|
315
|
+
except (RuntimeError, ValueError, TypeError, OSError) as e:
|
|
316
|
+
return Left(f"Runtime execution failed: {e}")
|
|
317
|
+
|
|
318
|
+
@staticmethod
|
|
319
|
+
def _is_system_error(error_msg: str) -> bool:
|
|
320
|
+
"""Check if error message indicates a system-level error."""
|
|
321
|
+
return any(pattern in error_msg for pattern in SYSTEM_ERROR_PATTERNS)
|
|
322
|
+
|
|
323
|
+
def list_available_runtimes(self) -> dict[str, bool]:
|
|
324
|
+
"""Get list of available runtimes on this system."""
|
|
325
|
+
return RuntimeFactory.list_available_runtimes()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
__all__ = [
|
|
329
|
+
"DEFAULT_RUNTIME",
|
|
330
|
+
"SYSTEM_ERROR_PATTERNS",
|
|
331
|
+
"FALLBACK_SOURCES",
|
|
332
|
+
"CodeResolver",
|
|
333
|
+
"SandboxExecutor",
|
|
334
|
+
]
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Layer 1: ℤ₄ Savepoint Guard & Reversibility Engine (DV-373-03, INV-339-02).
|
|
3
|
+
|
|
4
|
+
Implements transactional atomicity across the 4-phase cyclic group ℤ₄ = ℤ / 4ℤ:
|
|
5
|
+
0 (Idle) -> 1 (Pre-Gate) -> 2 (Transform) -> 3 (Post-Gate) -> 0 (Committed)
|
|
6
|
+
|
|
7
|
+
On any VCard meet failure (T_valid = ⊥) or execution exception:
|
|
8
|
+
- Automatically rolls back to the named SQLite savepoint
|
|
9
|
+
- Executes all Cordis DisposableList callbacks in strict reverse-LIFO order
|
|
10
|
+
- Guarantees zero residual state in the G-Set CAS (card, handle_history, learning_event)
|
|
11
|
+
- Records safety and liveness telemetry metrics
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import sqlite3
|
|
17
|
+
import sys
|
|
18
|
+
import uuid
|
|
19
|
+
from contextlib import contextmanager
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from typing import Any, Callable, Generator
|
|
22
|
+
|
|
23
|
+
_target = "clm.layer2.fiber"
|
|
24
|
+
_mod = sys.modules.get(_target)
|
|
25
|
+
if _mod is None:
|
|
26
|
+
__import__(_target)
|
|
27
|
+
_mod = sys.modules.get(_target)
|
|
28
|
+
DisposableList = _mod.DisposableList
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class VCardMeetViolation(Exception):
|
|
32
|
+
"""Raised when pre- or post-condition meet collapses to ⊥."""
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class CPNExecutionTelemetry:
|
|
38
|
+
"""Telemetry counters for CPN transition safety and liveness."""
|
|
39
|
+
|
|
40
|
+
total_transitions: int = 0
|
|
41
|
+
successful_transitions: int = 0
|
|
42
|
+
aborted_transitions: int = 0
|
|
43
|
+
safety_violation_count: int = 0
|
|
44
|
+
liveness_metric: float = 1.0
|
|
45
|
+
|
|
46
|
+
def record_success(self) -> None:
|
|
47
|
+
self.total_transitions += 1
|
|
48
|
+
self.successful_transitions += 1
|
|
49
|
+
self._update_liveness()
|
|
50
|
+
|
|
51
|
+
def record_abort(self) -> None:
|
|
52
|
+
self.total_transitions += 1
|
|
53
|
+
self.aborted_transitions += 1
|
|
54
|
+
self.safety_violation_count += 1
|
|
55
|
+
self._update_liveness()
|
|
56
|
+
|
|
57
|
+
def _update_liveness(self) -> None:
|
|
58
|
+
if self.total_transitions == 0:
|
|
59
|
+
self.liveness_metric = 1.0
|
|
60
|
+
else:
|
|
61
|
+
self.liveness_metric = self.successful_transitions / self.total_transitions
|
|
62
|
+
|
|
63
|
+
def to_dict(self) -> dict[str, Any]:
|
|
64
|
+
return {
|
|
65
|
+
"total_transitions": self.total_transitions,
|
|
66
|
+
"successful_transitions": self.successful_transitions,
|
|
67
|
+
"aborted_transitions": self.aborted_transitions,
|
|
68
|
+
"safety_violation_count": self.safety_violation_count,
|
|
69
|
+
"liveness_metric": round(self.liveness_metric, 4),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Vault-level singleton telemetry for CPN execution
|
|
74
|
+
GLOBAL_CPN_TELEMETRY = CPNExecutionTelemetry()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Z4TransitionContext:
|
|
78
|
+
"""State manager for a single ℤ₄ transition step."""
|
|
79
|
+
|
|
80
|
+
def __init__(self, step_id: str, conn: sqlite3.Connection, disposables: DisposableList) -> None:
|
|
81
|
+
self.step_id = step_id
|
|
82
|
+
self.conn = conn
|
|
83
|
+
self.disposables = disposables
|
|
84
|
+
self.phase: int = 0 # 0: Idle, 1: Prep, 2: Exec, 3: Post
|
|
85
|
+
self.vcard_meet_satisfied: bool = True
|
|
86
|
+
|
|
87
|
+
def advance_phase(self, new_phase: int) -> None:
|
|
88
|
+
"""Transitions state: 0 -> 1 -> 2 -> 3 -> 0."""
|
|
89
|
+
self.phase = new_phase % 4
|
|
90
|
+
|
|
91
|
+
def register_disposable(self, callback: Callable[[], None]) -> None:
|
|
92
|
+
"""Registers a reverse-LIFO rollback callback."""
|
|
93
|
+
self.disposables.push(callback)
|
|
94
|
+
|
|
95
|
+
def fail_meet(self, reason: str = "State collapsed to ⊥") -> None:
|
|
96
|
+
"""Explicitly collapses transition to ⊥."""
|
|
97
|
+
self.vcard_meet_satisfied = False
|
|
98
|
+
raise VCardMeetViolation(f"Step '{self.step_id}' VCard Meet Violation: {reason}")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@contextmanager
|
|
102
|
+
def z4_savepoint_guard(
|
|
103
|
+
conn: sqlite3.Connection,
|
|
104
|
+
step_id: str | None = None,
|
|
105
|
+
telemetry: CPNExecutionTelemetry | None = None,
|
|
106
|
+
) -> Generator[Z4TransitionContext, None, None]:
|
|
107
|
+
"""
|
|
108
|
+
Executes a CPN transition in an isolated SQLite savepoint with ℤ₄ reversibility.
|
|
109
|
+
|
|
110
|
+
Guarantees zero partial writes on error.
|
|
111
|
+
"""
|
|
112
|
+
tel = telemetry or GLOBAL_CPN_TELEMETRY
|
|
113
|
+
name = step_id or f"ptr_step_{uuid.uuid4().hex[:8]}"
|
|
114
|
+
sp_name = f"sp_{name}"
|
|
115
|
+
disposables = DisposableList()
|
|
116
|
+
|
|
117
|
+
conn.execute(f"SAVEPOINT {sp_name};")
|
|
118
|
+
t_ctx = Z4TransitionContext(step_id=name, conn=conn, disposables=disposables)
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
t_ctx.advance_phase(1) # Prep
|
|
122
|
+
yield t_ctx
|
|
123
|
+
|
|
124
|
+
if not t_ctx.vcard_meet_satisfied:
|
|
125
|
+
raise VCardMeetViolation("VCard meet collapsed to ⊥")
|
|
126
|
+
|
|
127
|
+
t_ctx.advance_phase(0) # Sealed
|
|
128
|
+
conn.execute(f"RELEASE SAVEPOINT {sp_name};")
|
|
129
|
+
tel.record_success()
|
|
130
|
+
|
|
131
|
+
except Exception:
|
|
132
|
+
disposables.dispose()
|
|
133
|
+
conn.execute(f"ROLLBACK TO SAVEPOINT {sp_name};")
|
|
134
|
+
conn.execute(f"RELEASE SAVEPOINT {sp_name};")
|
|
135
|
+
tel.record_abort()
|
|
136
|
+
raise
|
clm/layer1/sparse_net.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Layer 1: Compressed Sparse Incidence Matrix & Precomputed P-Invariants (INV-419).
|
|
2
|
+
|
|
3
|
+
Sprint 419 / DEL-419-02.
|
|
4
|
+
Provides zero-allocation token transitions and O(1) Noetherian conservation verification.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
|
|
11
|
+
from clm.layer0.math_utils import solve_nullspace
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class PrecomputedInvariants:
|
|
16
|
+
"""Pre-computed nullspace basis for Noetherian P-invariants (INV-419).
|
|
17
|
+
|
|
18
|
+
Replaces dynamic Gaussian elimination during transitions with O(1) dot-product checks.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
basis_vectors: list[list[int]]
|
|
22
|
+
initial_constants: list[int]
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def compile(
|
|
26
|
+
cls,
|
|
27
|
+
matrix: list[list[int]],
|
|
28
|
+
rows: int,
|
|
29
|
+
cols: int,
|
|
30
|
+
initial_marking: list[int],
|
|
31
|
+
) -> PrecomputedInvariants:
|
|
32
|
+
"""Compile nullspace invariants from an incidence matrix D and initial marking."""
|
|
33
|
+
basis = solve_nullspace(matrix, rows, cols, transpose=True)
|
|
34
|
+
initial_constants: list[int] = []
|
|
35
|
+
for b in basis:
|
|
36
|
+
s = sum(w * initial_marking[p] for p, w in enumerate(b) if p < len(initial_marking))
|
|
37
|
+
initial_constants.append(s)
|
|
38
|
+
return cls(basis_vectors=basis, initial_constants=initial_constants)
|
|
39
|
+
|
|
40
|
+
def verify_marking(self, marking: list[int]) -> bool:
|
|
41
|
+
"""Fast-path check: returns True if current marking satisfies all basis conservation laws."""
|
|
42
|
+
for i, b in enumerate(self.basis_vectors):
|
|
43
|
+
s = sum(w * marking[p] for p, w in enumerate(b) if p < len(marking))
|
|
44
|
+
if i < len(self.initial_constants) and s != self.initial_constants[i]:
|
|
45
|
+
return False
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
def verify_delta(self, delta: list[int]) -> bool:
|
|
49
|
+
"""Fast-path check on marking delta: returns True if delta * v_k == 0 for all basis vectors."""
|
|
50
|
+
for b in self.basis_vectors:
|
|
51
|
+
s = sum(w * delta[p] for p, w in enumerate(b) if p < len(delta))
|
|
52
|
+
if s != 0:
|
|
53
|
+
return False
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class SparseIncidenceMatrix:
|
|
59
|
+
"""Compressed Sparse Incidence Matrix for high-performance CPN evaluation."""
|
|
60
|
+
|
|
61
|
+
place_count: int
|
|
62
|
+
trans_count: int
|
|
63
|
+
consumed: list[list[tuple[int, int]]] = field(default_factory=list)
|
|
64
|
+
produced: list[list[tuple[int, int]]] = field(default_factory=list)
|
|
65
|
+
deltas: list[list[tuple[int, int]]] = field(default_factory=list)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def from_dense(
|
|
69
|
+
cls,
|
|
70
|
+
d: list[list[int]],
|
|
71
|
+
place_count: int,
|
|
72
|
+
trans_count: int,
|
|
73
|
+
) -> SparseIncidenceMatrix:
|
|
74
|
+
"""Construct from dense incidence matrix D (rows=places, cols=transitions)."""
|
|
75
|
+
consumed: list[list[tuple[int, int]]] = [[] for _ in range(trans_count)]
|
|
76
|
+
produced: list[list[tuple[int, int]]] = [[] for _ in range(trans_count)]
|
|
77
|
+
deltas: list[list[tuple[int, int]]] = [[] for _ in range(trans_count)]
|
|
78
|
+
|
|
79
|
+
for t in range(trans_count):
|
|
80
|
+
for p in range(place_count):
|
|
81
|
+
val = d[p][t] if p < len(d) and t < len(d[p]) else 0
|
|
82
|
+
if val < 0:
|
|
83
|
+
consumed[t].append((p, -val))
|
|
84
|
+
deltas[t].append((p, val))
|
|
85
|
+
elif val > 0:
|
|
86
|
+
produced[t].append((p, val))
|
|
87
|
+
deltas[t].append((p, val))
|
|
88
|
+
|
|
89
|
+
return cls(
|
|
90
|
+
place_count=place_count,
|
|
91
|
+
trans_count=trans_count,
|
|
92
|
+
consumed=consumed,
|
|
93
|
+
produced=produced,
|
|
94
|
+
deltas=deltas,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def is_enabled(self, t: int, marking: list[int]) -> bool:
|
|
98
|
+
"""Check if transition t is enabled under the given marking."""
|
|
99
|
+
if t >= self.trans_count:
|
|
100
|
+
return False
|
|
101
|
+
for p, required in self.consumed[t]:
|
|
102
|
+
if p >= len(marking) or marking[p] < required:
|
|
103
|
+
return False
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
def fire(self, t: int, marking: list[int]) -> bool:
|
|
107
|
+
"""Fire transition t in-place on marking. Returns True on success, False if disabled."""
|
|
108
|
+
if not self.is_enabled(t, marking):
|
|
109
|
+
return False
|
|
110
|
+
for p, delta in self.deltas[t]:
|
|
111
|
+
if p < len(marking):
|
|
112
|
+
marking[p] += delta
|
|
113
|
+
return True
|
|
114
|
+
|
|
115
|
+
def get_delta(self, t: int) -> list[int]:
|
|
116
|
+
"""Compute flat delta vector for transition t."""
|
|
117
|
+
res = [0] * self.place_count
|
|
118
|
+
if t < self.trans_count:
|
|
119
|
+
for p, delta in self.deltas[t]:
|
|
120
|
+
if p < len(res):
|
|
121
|
+
res[p] = delta
|
|
122
|
+
return res
|