mplang-nightly 0.1.dev277__tar.gz → 0.1.dev279__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/PKG-INFO +1 -1
  2. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/__init__.py +30 -6
  3. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_worker/ops.py +6 -2
  4. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/__init__.py +3 -0
  5. mplang_nightly-0.1.dev279/mplang/edsl/program.py +134 -0
  6. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/runtime/interpreter.py +294 -36
  7. mplang_nightly-0.1.dev279/mplang/tool/__init__.py +46 -0
  8. mplang_nightly-0.1.dev279/mplang/tool/program.py +335 -0
  9. mplang_nightly-0.1.dev279/tests/backends/simp_worker/test_shuffle_exec_id_key.py +75 -0
  10. mplang_nightly-0.1.dev279/tests/edsl/test_compiled_program_artifact.py +191 -0
  11. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/04_ir_dump_and_analysis.py +37 -0
  12. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/.gitignore +0 -0
  13. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/LICENSE +0 -0
  14. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/README.md +0 -0
  15. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/examples/.gitkeep +0 -0
  16. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/hatch_build.py +0 -0
  17. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/__init__.py +0 -0
  18. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/bfv_impl.py +0 -0
  19. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/channel.py +0 -0
  20. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/crypto_impl.py +0 -0
  21. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/field_impl.py +0 -0
  22. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/func_impl.py +0 -0
  23. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/phe_impl.py +0 -0
  24. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_design.md +0 -0
  25. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_driver/__init__.py +0 -0
  26. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_driver/http.py +0 -0
  27. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_driver/mem.py +0 -0
  28. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_driver/ops.py +0 -0
  29. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_driver/state.py +0 -0
  30. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_driver/values.py +0 -0
  31. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_worker/__init__.py +0 -0
  32. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_worker/http.py +0 -0
  33. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_worker/mem.py +0 -0
  34. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/simp_worker/state.py +0 -0
  35. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/spu_impl.py +0 -0
  36. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/spu_state.py +0 -0
  37. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/store_impl.py +0 -0
  38. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/table_impl.py +0 -0
  39. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/tee_impl.py +0 -0
  40. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/backends/tensor_impl.py +0 -0
  41. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/cli.py +0 -0
  42. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/cli_guide.md +0 -0
  43. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/__init__.py +0 -0
  44. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/bfv.py +0 -0
  45. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/crypto.py +0 -0
  46. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/dtypes.py +0 -0
  47. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/field.py +0 -0
  48. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/func.py +0 -0
  49. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/phe.py +0 -0
  50. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/simp.py +0 -0
  51. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/spu.py +0 -0
  52. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/store.py +0 -0
  53. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/table.py +0 -0
  54. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/tee.py +0 -0
  55. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/dialects/tensor.py +0 -0
  56. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/README.md +0 -0
  57. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/context.py +0 -0
  58. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/graph.py +0 -0
  59. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/jit.py +0 -0
  60. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/object.py +0 -0
  61. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/primitive.py +0 -0
  62. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/printer.py +0 -0
  63. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/registry.py +0 -0
  64. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/serde.py +0 -0
  65. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/tracer.py +0 -0
  66. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/edsl/typing.py +0 -0
  67. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/kernels/Makefile +0 -0
  68. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/kernels/__init__.py +0 -0
  69. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/kernels/gf128.cpp +0 -0
  70. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/kernels/ldpc.cpp +0 -0
  71. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/kernels/okvs.cpp +0 -0
  72. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/kernels/okvs_opt.cpp +0 -0
  73. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/kernels/py_kernels.py +0 -0
  74. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/collective.py +0 -0
  75. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/device/__init__.py +0 -0
  76. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/device/api.py +0 -0
  77. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/device/cluster.py +0 -0
  78. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/ml/__init__.py +0 -0
  79. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/ml/sgb.py +0 -0
  80. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/__init__.py +0 -0
  81. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/_utils.py +0 -0
  82. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/analytics/__init__.py +0 -0
  83. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/analytics/aggregation.py +0 -0
  84. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/analytics/groupby.md +0 -0
  85. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/analytics/groupby.py +0 -0
  86. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/analytics/permutation.py +0 -0
  87. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/common/constants.py +0 -0
  88. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/ot/__init__.py +0 -0
  89. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/ot/base.py +0 -0
  90. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/ot/extension.py +0 -0
  91. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/ot/silent.py +0 -0
  92. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/psi/__init__.py +0 -0
  93. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/psi/cuckoo.py +0 -0
  94. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/psi/okvs.py +0 -0
  95. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/psi/okvs_gct.py +0 -0
  96. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/psi/oprf.py +0 -0
  97. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/psi/rr22.py +0 -0
  98. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/psi/unbalanced.py +0 -0
  99. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/vole/__init__.py +0 -0
  100. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/vole/gilboa.py +0 -0
  101. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/vole/ldpc.py +0 -0
  102. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/libs/mpc/vole/silver.py +0 -0
  103. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/logging_config.py +0 -0
  104. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/py.typed +0 -0
  105. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/runtime/__init__.py +0 -0
  106. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/runtime/dialect_state.py +0 -0
  107. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/runtime/object_store.py +0 -0
  108. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/runtime/value.py +0 -0
  109. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/utils/__init__.py +0 -0
  110. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/mplang/utils/func_utils.py +0 -0
  111. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/pyproject.toml +0 -0
  112. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/__init__.py +0 -0
  113. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/__init__.py +0 -0
  114. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/simp_driver/__init__.py +0 -0
  115. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/simp_driver/test_http.py +0 -0
  116. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/simp_worker/__init__.py +0 -0
  117. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/simp_worker/test_http.py +0 -0
  118. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/simp_worker/test_mem.py +0 -0
  119. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_bfv_impl.py +0 -0
  120. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_channel.py +0 -0
  121. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_crypto_impl.py +0 -0
  122. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_okvs_binding.py +0 -0
  123. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_simp_integration.py +0 -0
  124. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_simp_object_store.py +0 -0
  125. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_spu_impl.py +0 -0
  126. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_table_impl.py +0 -0
  127. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_tee_impl.py +0 -0
  128. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_tensor_impl.py +0 -0
  129. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/backends/test_verify_clean.py +0 -0
  130. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/conftest.py +0 -0
  131. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/__init__.py +0 -0
  132. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_bfv.py +0 -0
  133. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_crypto.py +0 -0
  134. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_dtypes.py +0 -0
  135. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_field.py +0 -0
  136. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_func.py +0 -0
  137. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_okvs.py +0 -0
  138. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_okvs_bench.py +0 -0
  139. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_phe.py +0 -0
  140. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_simp.py +0 -0
  141. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_simp_comm.py +0 -0
  142. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_spu.py +0 -0
  143. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_store.py +0 -0
  144. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_table.py +0 -0
  145. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_tee.py +0 -0
  146. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/dialects/test_tensor.py +0 -0
  147. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/__init__.py +0 -0
  148. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_context.py +0 -0
  149. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_graph.py +0 -0
  150. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_primitive.py +0 -0
  151. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_primitive_multi_output.py +0 -0
  152. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_printer.py +0 -0
  153. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_serde.py +0 -0
  154. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_tracer.py +0 -0
  155. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_typing.py +0 -0
  156. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/edsl/test_typing_graph_serde.py +0 -0
  157. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/device/__init__.py +0 -0
  158. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/device/conftest.py +0 -0
  159. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/device/test_device_api_errors.py +0 -0
  160. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/device/test_device_dialects.py +0 -0
  161. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/device/test_device_layouts.py +0 -0
  162. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/device/test_device_tee.py +0 -0
  163. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/ml/__init__.py +0 -0
  164. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/ml/test_sgb.py +0 -0
  165. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/ml/test_sgb_bench.py +0 -0
  166. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/__init__.py +0 -0
  167. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/analytics/__init__.py +0 -0
  168. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/analytics/test_aggregation.py +0 -0
  169. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/analytics/test_groupby.py +0 -0
  170. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/analytics/test_permutation.py +0 -0
  171. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/ot/__init__.py +0 -0
  172. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/ot/test_ot.py +0 -0
  173. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/ot/test_ot_extension.py +0 -0
  174. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/ot/test_silent_ot.py +0 -0
  175. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/psi/__init__.py +0 -0
  176. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/psi/test_okvs_gct.py +0 -0
  177. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/psi/test_oprf.py +0 -0
  178. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/psi/test_psi.py +0 -0
  179. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/psi/test_psi_bench.py +0 -0
  180. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/psi/test_rr22.py +0 -0
  181. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/psi/verify_psi_okvs_logic.py +0 -0
  182. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/test_field_gf128.py +0 -0
  183. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/test_utils.py +0 -0
  184. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/vole/__init__.py +0 -0
  185. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/vole/test_gilboa_manual.py +0 -0
  186. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/vole/test_ldpc.py +0 -0
  187. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/vole/test_silver_vole.py +0 -0
  188. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/vole/test_vole.py +0 -0
  189. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/vole/test_vole_bench.py +0 -0
  190. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/mpc/vole/verify_vole_logic.py +0 -0
  191. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/test_collective.py +0 -0
  192. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/libs/test_simple_guide.py +0 -0
  193. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/runtime/test_interpreter_async.py +0 -0
  194. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/runtime/test_object_store.py +0 -0
  195. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/runtime/test_object_store_fs.py +0 -0
  196. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/test_fetch_semantics.py +0 -0
  197. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/test_logging.py +0 -0
  198. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/test_pytree_io.py +0 -0
  199. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/utils/__init__.py +0 -0
  200. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/utils/tensor_patch.py +0 -0
  201. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tests/utils/test_func_utils.py +0 -0
  202. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/00_device_basics.py +0 -0
  203. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/01_function_decorator.py +0 -0
  204. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/02_simulation_and_driver.py +0 -0
  205. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/03_run_jax.py +0 -0
  206. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/05_run_sql.py +0 -0
  207. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/06_pipeline.py +0 -0
  208. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/07_stax_nn.py +0 -0
  209. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/08_logging.py +0 -0
  210. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/MIGRATION.md +0 -0
  211. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/README.md +0 -0
  212. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/__init__.py +0 -0
  213. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/data/alice.csv +0 -0
  214. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/data/bob.csv +0 -0
  215. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/data/prepare_vertical_iris.py +0 -0
  216. {mplang_nightly-0.1.dev277 → mplang_nightly-0.1.dev279}/tutorials/run.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mplang-nightly
3
- Version: 0.1.dev277
3
+ Version: 0.1.dev279
4
4
  Summary: Multi-Party Programming Language
5
5
  Author-email: SecretFlow Team <secretflow-contact@service.alipay.com>
6
6
  License: Apache License
@@ -34,6 +34,7 @@ except Exception:
34
34
  # Fallback for development/editable installs when package is not installed
35
35
  __version__ = "0.0.0-dev"
36
36
 
37
+ import mplang.tool as tool
37
38
  from mplang import dialects
38
39
  from mplang.backends.simp_driver.ops import DRIVER_HANDLERS
39
40
  from mplang.backends.simp_worker import SimpWorker
@@ -41,6 +42,8 @@ from mplang.backends.simp_worker.mem import LocalMesh
41
42
  from mplang.backends.simp_worker.ops import WORKER_HANDLERS
42
43
  from mplang.dialects.simp import make_driver, make_simulator
43
44
  from mplang.edsl import (
45
+ CompiledProgram,
46
+ FlatIOSignature,
44
47
  Graph,
45
48
  GraphPrinter,
46
49
  Object,
@@ -125,7 +128,7 @@ def _get_context(context: Interpreter | None) -> Interpreter:
125
128
 
126
129
 
127
130
  def evaluate(
128
- fn: Callable[..., Any] | TracedFunction,
131
+ fn: Callable[..., Any] | TracedFunction | CompiledProgram,
129
132
  *args: Any,
130
133
  context: Interpreter | None = None,
131
134
  **kwargs: Any,
@@ -158,15 +161,33 @@ def evaluate(
158
161
  return val.runtime_obj
159
162
  return val
160
163
 
164
+ def eval_graph(graph: Graph, inputs: list[Any]) -> list[InterpObject]:
165
+ runtime_inputs = [unwrap_if_interp(v) for v in inputs]
166
+ raw_result = interp.evaluate_graph(graph, runtime_inputs)
167
+ return [
168
+ InterpObject(v, graph.outputs[i].type, interp)
169
+ for i, v in enumerate(raw_result)
170
+ ]
171
+
161
172
  with interp:
173
+ if isinstance(fn, CompiledProgram):
174
+ if kwargs:
175
+ raise TypeError(
176
+ "mp.evaluate(CompiledProgram, ...) does not accept keyword arguments; "
177
+ "pass flat positional inputs only."
178
+ )
179
+ if len(args) != fn.signature.input_arity:
180
+ raise ValueError(
181
+ "CompiledProgram requires flat positional inputs matching its signature; "
182
+ f"expected {fn.signature.input_arity}, got {len(args)}."
183
+ )
184
+
185
+ return eval_graph(fn.graph, list(args))
186
+
162
187
  if isinstance(fn, TracedFunction):
163
188
  inputs = fn.prepare_inputs(*args, **kwargs)
164
189
  inputs = [unwrap_if_interp(v) for v in inputs]
165
- raw_result = interp.evaluate_graph(fn.graph, inputs)
166
- wrapped = [
167
- InterpObject(v, fn.graph.outputs[i].type, interp)
168
- for i, v in enumerate(raw_result)
169
- ]
190
+ wrapped = eval_graph(fn.graph, inputs)
170
191
  return fn.reconstruct_outputs(wrapped)
171
192
 
172
193
  return fn(*args, **kwargs)
@@ -417,6 +438,9 @@ __all__ = [ # noqa: RUF022
417
438
  "WORKER_HANDLERS",
418
439
  "make_driver",
419
440
  "make_simulator",
441
+ "tool",
442
+ "CompiledProgram",
443
+ "FlatIOSignature",
420
444
  # Dialects
421
445
  "dialects",
422
446
  "register_default_context_factory",
@@ -84,16 +84,20 @@ def _shuffle_static_worker_impl(
84
84
  my_rank = worker.rank
85
85
  data = args[0]
86
86
 
87
+ exec_id = interpreter.current_op_exec_id()
88
+ graph_key = interpreter.current_graph_exec_key()
89
+ key_prefix = f"shuffle_{graph_key}_{op.name}_{exec_id}"
90
+
87
91
  for tgt, src in routing.items():
88
92
  if src == my_rank and tgt != my_rank:
89
- key = f"shuffle_{op.name}_{tgt}"
93
+ key = f"{key_prefix}_{tgt}"
90
94
  comm.send(tgt, key, data)
91
95
 
92
96
  if my_rank in routing:
93
97
  src = routing[my_rank]
94
98
  if src == my_rank:
95
99
  return data
96
- key = f"shuffle_{op.name}_{my_rank}"
100
+ key = f"{key_prefix}_{my_rank}"
97
101
  return comm.recv(src, key)
98
102
  else:
99
103
  return None
@@ -53,6 +53,7 @@ from .jit import jit
53
53
  from .object import Object
54
54
  from .primitive import Primitive, primitive
55
55
  from .printer import GraphPrinter, format_graph
56
+ from .program import CompiledProgram, FlatIOSignature
56
57
  from .tracer import TracedFunction, TraceObject, Tracer, trace
57
58
  from .typing import MPType, ScalarType, SSType, TableType, TensorType, VectorType
58
59
 
@@ -65,7 +66,9 @@ TensorObject = Object[TensorType]
65
66
  VectorObject = Object[VectorType]
66
67
 
67
68
  __all__ = [
69
+ "CompiledProgram",
68
70
  "Context",
71
+ "FlatIOSignature",
69
72
  "Graph",
70
73
  "GraphPrinter",
71
74
  "MPObject",
@@ -0,0 +1,134 @@
1
+ # Copyright 2026 Ant Group Co., Ltd.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import hashlib
18
+ import json
19
+ from dataclasses import dataclass
20
+ from typing import Any, ClassVar
21
+
22
+ from mplang.edsl import serde
23
+ from mplang.edsl.graph import Graph
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class FlatIOSignature:
28
+ """Portable I/O signature for source-free execution.
29
+
30
+ Only supports flat positional inputs/outputs corresponding to
31
+ `graph.inputs` / `graph.outputs`.
32
+ """
33
+
34
+ kind: ClassVar[str] = "flat_list_v0"
35
+ input_arity: int
36
+ output_arity: int
37
+
38
+ def to_json(self) -> dict[str, Any]:
39
+ return {
40
+ "kind": self.kind,
41
+ "input_arity": self.input_arity,
42
+ "output_arity": self.output_arity,
43
+ }
44
+
45
+ @classmethod
46
+ def from_json(cls, data: dict[str, Any]) -> FlatIOSignature:
47
+ if data.get("kind") != cls.kind:
48
+ raise ValueError(f"Unsupported signature kind: {data.get('kind')}")
49
+ return cls(
50
+ input_arity=int(data["input_arity"]),
51
+ output_arity=int(data["output_arity"]),
52
+ )
53
+
54
+
55
+ @serde.register_class
56
+ @dataclass
57
+ class CompiledProgram:
58
+ """Executable program decoupled from user source.
59
+
60
+ This is a *logical model*; packaging (file/zip/etc.) is handled by tool layer.
61
+
62
+ Current constraints:
63
+ - signature is flat positional list I/O.
64
+ - no closure captures.
65
+ - no constant outputs (out_imms) unless future signature captures them.
66
+ """
67
+
68
+ _serde_kind: ClassVar[str] = "mplang.CompiledProgram"
69
+
70
+ graph: Graph
71
+ signature: FlatIOSignature
72
+ required_opcodes: list[str]
73
+ graph_digest: str
74
+ required_world_size: int | None = None
75
+ created_at: str | None = None
76
+ mplang_version: str | None = None
77
+ schema_version: int = 1
78
+ name: str | None = None
79
+
80
+ def to_json(self) -> dict[str, Any]:
81
+ return {
82
+ "schema_version": self.schema_version,
83
+ "name": self.name,
84
+ "graph": serde.to_json(self.graph),
85
+ "signature": self.signature.to_json(),
86
+ "required_opcodes": list(self.required_opcodes),
87
+ "graph_digest": self.graph_digest,
88
+ "required_world_size": self.required_world_size,
89
+ "created_at": self.created_at,
90
+ "mplang_version": self.mplang_version,
91
+ }
92
+
93
+ @classmethod
94
+ def from_json(cls, data: dict[str, Any]) -> CompiledProgram:
95
+ if "schema_version" not in data:
96
+ raise KeyError("Missing required field: schema_version")
97
+ schema_version = int(data["schema_version"])
98
+ if schema_version != 1:
99
+ raise ValueError(
100
+ f"Unsupported CompiledProgram schema_version: {schema_version}"
101
+ )
102
+
103
+ graph = serde.from_json(data["graph"])
104
+ if not isinstance(graph, Graph):
105
+ raise TypeError(
106
+ f"Expected graph to deserialize to Graph, got {type(graph).__name__}"
107
+ )
108
+
109
+ signature = FlatIOSignature.from_json(data["signature"])
110
+
111
+ required_world_size = data.get("required_world_size")
112
+ if required_world_size is not None:
113
+ required_world_size = int(required_world_size)
114
+ return cls(
115
+ graph=graph,
116
+ signature=signature,
117
+ required_opcodes=list(data.get("required_opcodes", [])),
118
+ graph_digest=str(data["graph_digest"]),
119
+ required_world_size=required_world_size,
120
+ created_at=data.get("created_at"),
121
+ mplang_version=data.get("mplang_version"),
122
+ schema_version=schema_version,
123
+ name=data.get("name"),
124
+ )
125
+
126
+
127
+ def compute_graph_digest(graph: Graph) -> str:
128
+ """Compute a deterministic digest for a Graph.
129
+
130
+ We intentionally avoid `serde.dumps()` because it doesn't sort keys.
131
+ """
132
+
133
+ canonical = json.dumps(serde.to_json(graph), sort_keys=True, separators=(",", ":"))
134
+ return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
@@ -24,17 +24,19 @@ from __future__ import annotations
24
24
 
25
25
  import collections
26
26
  import concurrent.futures
27
+ import contextlib
28
+ import hashlib
27
29
  import json
28
30
  import os
29
31
  import pathlib
30
32
  import queue
31
33
  import threading
32
34
  import time
33
- from collections.abc import Callable
35
+ from collections.abc import Callable, Iterator
34
36
  from typing import TYPE_CHECKING, Any, cast
35
37
 
36
38
  from mplang.edsl.context import AbstractInterpreter
37
- from mplang.edsl.graph import Graph
39
+ from mplang.edsl.graph import Graph, Value
38
40
  from mplang.edsl.object import Object
39
41
  from mplang.edsl.registry import get_impl
40
42
  from mplang.edsl.typing import BaseType
@@ -364,12 +366,201 @@ class Interpreter(AbstractInterpreter):
364
366
  # 2. MIMO Optimization: When one output of a multi-output op is computed,
365
367
  # all sibling outputs are cached here to avoid re-execution.
366
368
  self._execution_cache: dict[Any, InterpObject] = {}
369
+
370
+ # -----------------------------------------------------------------
371
+ # Graph-local op execution ids (for deterministic communication tags)
372
+ # -----------------------------------------------------------------
373
+ # We assign a monotonically increasing exec_id to each op execution
374
+ # within a graph namespace, and keep it deterministic across parties.
375
+ #
376
+ # IMPORTANT:
377
+ # - We intentionally make exec_id grow across repeated executions of the
378
+ # same region graph (e.g., while_loop iterations) to avoid tag/key reuse.
379
+ #
380
+ # Implementation:
381
+ # - Each evaluate_graph(graph, ...) reserves a contiguous exec_id range
382
+ # [base, base + len(graph.operations)).
383
+ # - Op exec_id = base + op_index_in_graph.
384
+ # - Reservation is persisted per graph_exec_key (structural hash).
385
+ # - We forbid concurrent execution of the same graph_hash to avoid
386
+ # message tag confusion when a backend uses only per-op tags.
387
+ self._exec_id_lock = threading.Lock()
388
+ self._graph_next_exec_base: dict[str, int] = {}
389
+ self._active_graph_exec_keys: set[str] = set()
390
+ self._tls = threading.local()
367
391
  self.executor = executor
368
392
  self.async_ops: set[str] = set()
369
393
  self.name = name
370
394
  self.trace_pid = trace_pid
371
395
  self.store: ObjectStore | None = store
372
396
 
397
+ @contextlib.contextmanager
398
+ def _tls_exec_context(
399
+ self,
400
+ *,
401
+ graph_exec_key: str | None = None,
402
+ op_exec_id: int | None = None,
403
+ ) -> Iterator[None]:
404
+ """Temporarily set execution context in thread-local storage."""
405
+
406
+ prev_graph_key = getattr(self._tls, "current_graph_exec_key", None)
407
+ prev_exec_id = getattr(self._tls, "current_op_exec_id", None)
408
+
409
+ if graph_exec_key is not None:
410
+ self._tls.current_graph_exec_key = graph_exec_key
411
+ if op_exec_id is not None:
412
+ self._tls.current_op_exec_id = op_exec_id
413
+
414
+ try:
415
+ yield
416
+ finally:
417
+ if graph_exec_key is not None:
418
+ if prev_graph_key is None:
419
+ delattr(self._tls, "current_graph_exec_key")
420
+ else:
421
+ self._tls.current_graph_exec_key = prev_graph_key
422
+
423
+ if op_exec_id is not None:
424
+ if prev_exec_id is None:
425
+ delattr(self._tls, "current_op_exec_id")
426
+ else:
427
+ self._tls.current_op_exec_id = prev_exec_id
428
+
429
+ def _graph_exec_key(self, graph: Graph) -> str:
430
+ """Return a deterministic, structural hash for a graph.
431
+
432
+ Used for:
433
+ - Namespacing per-graph exec_id counters
434
+ - Communication tag disambiguation (worker ops may include this key)
435
+
436
+ Note: we cache on the Graph object assuming graphs are immutable during
437
+ execution (finalized graphs / regions).
438
+ """
439
+
440
+ cached = getattr(graph, "_exec_key", None)
441
+ if cached is not None:
442
+ return cast(str, cached)
443
+
444
+ # NOTE: We intentionally do NOT use graph.to_json() here.
445
+ # graph.to_json() requires all attrs to be JSON-serializable via serde,
446
+ # but graphs may legitimately contain runtime-only objects (e.g. JAX
447
+ # PyTreeDef used by func.func). For communication tag namespaces we use
448
+ # a simple structural fingerprint that is deterministic across parties.
449
+
450
+ def _stable_attr_value(obj: Any) -> Any | None:
451
+ """Return a JSON-compatible stable value or None if unsupported.
452
+
453
+ We include only values that are likely deterministic across parties.
454
+ Unknown runtime objects are skipped (e.g. PyTreeDef, callables, etc.).
455
+ """
456
+
457
+ if obj is None or isinstance(obj, (bool, int, float, str)):
458
+ return obj
459
+
460
+ if isinstance(obj, (bytes, bytearray, memoryview)):
461
+ b = bytes(obj)
462
+ return {
463
+ "_kind": "bytes",
464
+ "len": len(b),
465
+ "sha256": hashlib.sha256(b).hexdigest(),
466
+ }
467
+
468
+ try:
469
+ import numpy as np # type: ignore
470
+
471
+ if isinstance(obj, np.ndarray):
472
+ b = obj.tobytes(order="C")
473
+ return {
474
+ "_kind": "ndarray",
475
+ "dtype": str(obj.dtype),
476
+ "shape": list(obj.shape),
477
+ "sha256": hashlib.sha256(b).hexdigest(),
478
+ }
479
+ if isinstance(obj, (np.integer, np.floating)):
480
+ return obj.item()
481
+ except Exception:
482
+ pass
483
+
484
+ if isinstance(obj, (list, tuple)):
485
+ items: list[Any] = []
486
+ for x in obj:
487
+ sx = _stable_attr_value(x)
488
+ if sx is None:
489
+ return None
490
+ items.append(sx)
491
+ return items
492
+
493
+ if isinstance(obj, dict):
494
+ stable_items: list[tuple[Any, Any]] = []
495
+ for k, v in obj.items():
496
+ sk = _stable_attr_value(k)
497
+ sv = _stable_attr_value(v)
498
+ if sk is None or sv is None:
499
+ return None
500
+ stable_items.append((sk, sv))
501
+ stable_items.sort(
502
+ key=lambda kv: json.dumps(
503
+ kv[0], sort_keys=True, separators=(",", ":"), ensure_ascii=False
504
+ )
505
+ )
506
+ return {"_kind": "dict", "items": stable_items}
507
+
508
+ return None
509
+
510
+ def _graph_fingerprint(g: Graph) -> Any:
511
+ # Map SSA Values to stable indices independent of their textual names.
512
+ value_to_index: dict[Value, int] = {}
513
+
514
+ def _index(v: Value) -> int:
515
+ if v in value_to_index:
516
+ return value_to_index[v]
517
+ value_to_index[v] = len(value_to_index)
518
+ return value_to_index[v]
519
+
520
+ for v in g.inputs:
521
+ _index(v)
522
+ for op in g.operations:
523
+ for out in op.outputs:
524
+ _index(out)
525
+
526
+ ops_fp: list[dict[str, Any]] = []
527
+ for op in g.operations:
528
+ attr_keys = sorted(op.attrs.keys())
529
+ stable_attr_items: list[tuple[str, Any]] = []
530
+ for k in attr_keys:
531
+ attr_val = op.attrs.get(k)
532
+ sv = _stable_attr_value(attr_val)
533
+ if sv is not None:
534
+ stable_attr_items.append((k, sv))
535
+
536
+ ops_fp.append({
537
+ "opcode": op.opcode,
538
+ "inputs": [_index(v) for v in op.inputs],
539
+ "outputs": [str(v.type) for v in op.outputs],
540
+ "attrs": {"keys": attr_keys, "stable": stable_attr_items},
541
+ "regions": [_graph_fingerprint(r) for r in op.regions],
542
+ })
543
+
544
+ return {
545
+ "inputs": [str(v.type) for v in g.inputs],
546
+ "ops": ops_fp,
547
+ "outputs": [_index(v) for v in g.outputs],
548
+ }
549
+
550
+ fingerprint = _graph_fingerprint(graph)
551
+
552
+ payload = json.dumps(
553
+ fingerprint,
554
+ sort_keys=True,
555
+ separators=(",", ":"),
556
+ ensure_ascii=False,
557
+ ).encode("utf-8")
558
+ key = hashlib.sha256(payload).hexdigest()
559
+
560
+ # Store on graph to avoid id(graph) reuse pitfalls.
561
+ graph._exec_key = key # type: ignore[attr-defined]
562
+ return key
563
+
373
564
  def shutdown(self) -> None:
374
565
  """Shutdown the interpreter and release resources.
375
566
 
@@ -641,18 +832,70 @@ class Interpreter(AbstractInterpreter):
641
832
  Returns:
642
833
  List of runtime execution results corresponding to graph.outputs.
643
834
  """
644
- logger.debug(
645
- "Evaluating graph: %d inputs, %d ops, %d outputs (job_id=%s, async=%s)",
646
- len(inputs),
647
- len(graph.operations),
648
- len(graph.outputs),
649
- job_id,
650
- self.executor is not None,
651
- )
652
- if self.executor:
653
- return self._evaluate_graph_async(graph, inputs, job_id)
654
- else:
655
- return self._evaluate_graph_sync(graph, inputs, job_id)
835
+ graph_exec_key = self._graph_exec_key(graph)
836
+
837
+ # Prevent concurrent execution of the same graph hash.
838
+ with self._exec_id_lock:
839
+ if graph_exec_key in self._active_graph_exec_keys:
840
+ raise RuntimeError(
841
+ "Concurrent execution of the same graph is not allowed. "
842
+ f"graph_exec_key={graph_exec_key}"
843
+ )
844
+ self._active_graph_exec_keys.add(graph_exec_key)
845
+
846
+ try:
847
+ with self._tls_exec_context(graph_exec_key=graph_exec_key):
848
+ logger.debug(
849
+ "Evaluating graph: %d inputs, %d ops, %d outputs (job_id=%s, async=%s, graph_key=%s)",
850
+ len(inputs),
851
+ len(graph.operations),
852
+ len(graph.outputs),
853
+ job_id,
854
+ self.executor is not None,
855
+ graph_exec_key,
856
+ )
857
+ if self.executor:
858
+ return self._evaluate_graph_async(graph, inputs, job_id)
859
+ else:
860
+ return self._evaluate_graph_sync(graph, inputs, job_id)
861
+ finally:
862
+ with self._exec_id_lock:
863
+ self._active_graph_exec_keys.discard(graph_exec_key)
864
+
865
+ def _reserve_op_exec_base(self, graph: Graph) -> int:
866
+ """Reserve a contiguous exec_id range for a single evaluate_graph call.
867
+
868
+ Counter is namespaced by the current graph_exec_key.
869
+ """
870
+ key = self.current_graph_exec_key()
871
+ with self._exec_id_lock:
872
+ base = self._graph_next_exec_base.get(key, 0)
873
+ self._graph_next_exec_base[key] = base + len(graph.operations)
874
+ return base
875
+
876
+ def current_graph_exec_key(self) -> str:
877
+ """Return current graph execution key during evaluate_graph execution."""
878
+
879
+ key = getattr(self._tls, "current_graph_exec_key", None)
880
+ if key is None:
881
+ raise RuntimeError(
882
+ "current_graph_exec_key() called outside of evaluate_graph execution"
883
+ )
884
+ return cast(str, key)
885
+
886
+ def current_op_exec_id(self) -> int:
887
+ """Return current op exec_id during graph execution.
888
+
889
+ Worker-side implementations can use this to build deterministic,
890
+ unique communication tags without coupling to any specific op.
891
+ """
892
+
893
+ exec_id = getattr(self._tls, "current_op_exec_id", None)
894
+ if exec_id is None:
895
+ raise RuntimeError(
896
+ "current_op_exec_id() called outside of evaluate_graph execution"
897
+ )
898
+ return cast(int, exec_id)
656
899
 
657
900
  def _evaluate_graph_sync(
658
901
  self, graph: Graph, inputs: list[Any], job_id: str | None = None
@@ -661,7 +904,10 @@ class Interpreter(AbstractInterpreter):
661
904
  # Local environment: Value -> Runtime Object
662
905
  env = dict(zip(graph.inputs, inputs, strict=True))
663
906
 
664
- for op in graph.operations:
907
+ op_exec_base = self._reserve_op_exec_base(graph)
908
+
909
+ for op_index, op in enumerate(graph.operations):
910
+ exec_id = op_exec_base + op_index
665
911
  # Resolve inputs
666
912
  try:
667
913
  args = [env[val] for val in op.inputs]
@@ -685,15 +931,16 @@ class Interpreter(AbstractInterpreter):
685
931
  if not handler:
686
932
  handler = get_impl(op.opcode)
687
933
 
688
- if handler:
689
- # Pass interpreter to support recursive execution (HOFs)
690
- # Pass op to access attributes and regions
691
- # Pass args as runtime values
692
- results = handler(self, op, *args)
693
- else:
694
- raise NotImplementedError(
695
- f"No implementation registered for opcode: {op.opcode}"
696
- )
934
+ with self._tls_exec_context(op_exec_id=exec_id):
935
+ if handler:
936
+ # Pass interpreter to support recursive execution (HOFs)
937
+ # Pass op to access attributes and regions
938
+ # Pass args as runtime values
939
+ results = handler(self, op, *args)
940
+ else:
941
+ raise NotImplementedError(
942
+ f"No implementation registered for opcode: {op.opcode}"
943
+ )
697
944
 
698
945
  # Update environment with outputs
699
946
  # Handler should return a single value or a tuple/list of values
@@ -719,6 +966,9 @@ class Interpreter(AbstractInterpreter):
719
966
  self, graph: Graph, inputs: list[Any], job_id: str | None = None
720
967
  ) -> list[Any]:
721
968
  """Asynchronous execution with non-blocking DAG scheduling."""
969
+ graph_exec_key = self.current_graph_exec_key()
970
+ op_exec_base = self._reserve_op_exec_base(graph)
971
+ op_to_index = {op: i for i, op in enumerate(graph.operations)}
722
972
  # Tracer setup (if not provided, use a disabled stub)
723
973
  tracer: ExecutionTracer | _NullTracer
724
974
  if self.tracer:
@@ -817,6 +1067,8 @@ class Interpreter(AbstractInterpreter):
817
1067
  # Extract args from env (must be ready)
818
1068
  args = [env[val] for val in op.inputs]
819
1069
 
1070
+ exec_id = op_exec_base + op_to_index[op]
1071
+
820
1072
  handler = self.handlers.get(op.opcode)
821
1073
  if not handler:
822
1074
  handler = get_impl(op.opcode)
@@ -833,12 +1085,15 @@ class Interpreter(AbstractInterpreter):
833
1085
 
834
1086
  # Submit to executor
835
1087
  def task() -> Any:
836
- start_ts = tracer.log_start(
837
- op, pid=self.trace_pid, namespace=self.trace_pid
838
- )
839
- res = handler(self, op, *args)
840
- tracer.log_end(op, start_ts, pid=self.trace_pid)
841
- return res
1088
+ with self._tls_exec_context(
1089
+ graph_exec_key=graph_exec_key, op_exec_id=exec_id
1090
+ ):
1091
+ start_ts = tracer.log_start(
1092
+ op, pid=self.trace_pid, namespace=self.trace_pid
1093
+ )
1094
+ res = handler(self, op, *args)
1095
+ tracer.log_end(op, start_ts, pid=self.trace_pid)
1096
+ return res
842
1097
 
843
1098
  def callback(fut: Any) -> None:
844
1099
  try:
@@ -852,12 +1107,15 @@ class Interpreter(AbstractInterpreter):
852
1107
  else:
853
1108
  # Sync execution (run immediately)
854
1109
  try:
855
- start_ts = tracer.log_start(
856
- op, pid=self.trace_pid, namespace=self.trace_pid
857
- )
858
- res = handler(self, op, *args)
859
- tracer.log_end(op, start_ts, pid=self.trace_pid)
860
- on_op_done(op, res)
1110
+ with self._tls_exec_context(
1111
+ graph_exec_key=graph_exec_key, op_exec_id=exec_id
1112
+ ):
1113
+ start_ts = tracer.log_start(
1114
+ op, pid=self.trace_pid, namespace=self.trace_pid
1115
+ )
1116
+ res = handler(self, op, *args)
1117
+ tracer.log_end(op, start_ts, pid=self.trace_pid)
1118
+ on_op_done(op, res)
861
1119
  except Exception as e:
862
1120
  on_op_done(op, None, error=e)
863
1121