qubasic 0.16.0__tar.gz → 0.17.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. {qubasic-0.16.0 → qubasic-0.17.0}/CHANGELOG.md +10 -0
  2. {qubasic-0.16.0/qubasic.egg-info → qubasic-0.17.0}/PKG-INFO +1 -1
  3. {qubasic-0.16.0 → qubasic-0.17.0}/pyproject.toml +1 -1
  4. {qubasic-0.16.0 → qubasic-0.17.0/qubasic.egg-info}/PKG-INFO +1 -1
  5. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/__init__.py +1 -1
  6. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/algorithms.py +2 -0
  7. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/expression.py +4 -4
  8. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/qec.py +6 -1
  9. {qubasic-0.16.0 → qubasic-0.17.0}/LICENSE +0 -0
  10. {qubasic-0.16.0 → qubasic-0.17.0}/MANIFEST.in +0 -0
  11. {qubasic-0.16.0 → qubasic-0.17.0}/README.md +0 -0
  12. {qubasic-0.16.0 → qubasic-0.17.0}/examples/bell.qb +0 -0
  13. {qubasic-0.16.0 → qubasic-0.17.0}/examples/grover3.qb +0 -0
  14. {qubasic-0.16.0 → qubasic-0.17.0}/examples/locc_teleport.qb +0 -0
  15. {qubasic-0.16.0 → qubasic-0.17.0}/examples/sweep_rx.qb +0 -0
  16. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic.egg-info/SOURCES.txt +0 -0
  17. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic.egg-info/dependency_links.txt +0 -0
  18. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic.egg-info/entry_points.txt +0 -0
  19. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic.egg-info/requires.txt +0 -0
  20. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic.egg-info/top_level.txt +0 -0
  21. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/__main__.py +0 -0
  22. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/algos2.py +0 -0
  23. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/analysis.py +0 -0
  24. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/backend.py +0 -0
  25. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/benchmarking.py +0 -0
  26. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/bosonic.py +0 -0
  27. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/classic.py +0 -0
  28. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/cli.py +0 -0
  29. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/control_flow.py +0 -0
  30. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/debug.py +0 -0
  31. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/demos.py +0 -0
  32. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/display.py +0 -0
  33. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/dynamics.py +0 -0
  34. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/engine.py +0 -0
  35. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/engine_state.py +0 -0
  36. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/errors.py +0 -0
  37. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/exec_context.py +0 -0
  38. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/executor.py +0 -0
  39. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/file_io.py +0 -0
  40. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/gates.py +0 -0
  41. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/help_text.py +0 -0
  42. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/io_protocol.py +0 -0
  43. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/jupyter_kernel.py +0 -0
  44. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/locc.py +0 -0
  45. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/locc_commands.py +0 -0
  46. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/locc_display.py +0 -0
  47. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/locc_engine.py +0 -0
  48. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/locc_execution.py +0 -0
  49. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/logical.py +0 -0
  50. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/memory.py +0 -0
  51. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/mock_backend.py +0 -0
  52. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/noise_mixin.py +0 -0
  53. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/parser.py +0 -0
  54. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/patterns.py +0 -0
  55. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/pauliprop.py +0 -0
  56. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/profiler.py +0 -0
  57. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/program_mgmt.py +0 -0
  58. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/protocol.py +0 -0
  59. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/qchem.py +0 -0
  60. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/qec2.py +0 -0
  61. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/qol.py +0 -0
  62. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/qudits.py +0 -0
  63. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/resources.py +0 -0
  64. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/scope.py +0 -0
  65. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/screen.py +0 -0
  66. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/state_display.py +0 -0
  67. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/statements.py +0 -0
  68. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/strings.py +0 -0
  69. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/subs.py +0 -0
  70. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/sweep.py +0 -0
  71. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/terminal.py +0 -0
  72. {qubasic-0.16.0 → qubasic-0.17.0}/qubasic_core/web_repl.py +0 -0
  73. {qubasic-0.16.0 → qubasic-0.17.0}/setup.cfg +0 -0
  74. {qubasic-0.16.0 → qubasic-0.17.0}/tests/test_features.py +0 -0
  75. {qubasic-0.16.0 → qubasic-0.17.0}/tests/test_golden.py +0 -0
  76. {qubasic-0.16.0 → qubasic-0.17.0}/tests/test_qubasic.py +0 -0
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.0 (2026-07-02)
4
+
5
+ Hardening from an SMT-verifier triage (touchstone-prover over all 651
6
+ functions: 153 proved trap-free, 3 refutations traced, none reachable from
7
+ the language surface).
8
+
9
+ ### Changed
10
+ - `_pmul` raises a clear ValueError on a non-Pauli character and `_emit_mcz` on an empty qubit list, instead of a raw KeyError/IndexError — both are internal preconditions (every caller feeds alphabet-constrained values), now self-documenting.
11
+ - `_eval_with_vars` declares the ParameterExpression passthrough in its return annotation instead of claiming `-> float`.
12
+
3
13
  ## 0.16.0 (2026-07-02)
4
14
 
5
15
  Research-grade error correction, per-method scale, parametric variational
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qubasic
3
- Version: 0.16.0
3
+ Version: 0.17.0
4
4
  Summary: Quantum BASIC Interactive Terminal
5
5
  Author-email: "Charles C. Norton" <machineelv@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qubasic"
7
- version = "0.16.0"
7
+ version = "0.17.0"
8
8
  description = "Quantum BASIC Interactive Terminal"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qubasic
3
- Version: 0.16.0
3
+ Version: 0.17.0
4
4
  Summary: Quantum BASIC Interactive Terminal
5
5
  Author-email: "Charles C. Norton" <machineelv@gmail.com>
6
6
  License-Expression: MIT
@@ -28,7 +28,7 @@ __all__ = [
28
28
  'GATE_TABLE', 'GATE_ALIASES',
29
29
  ]
30
30
 
31
- __version__ = '0.16.0'
31
+ __version__ = '0.17.0'
32
32
 
33
33
  def __getattr__(name):
34
34
  """Lazy import heavy modules on first access."""
@@ -119,6 +119,8 @@ class AlgorithmsMixin:
119
119
 
120
120
  def _emit_mcz(self, qc, qs: list[int]) -> None:
121
121
  """Multi-controlled Z over qs (phase flip of the all-ones state)."""
122
+ if not qs:
123
+ raise ValueError("MCZ needs at least one qubit")
122
124
  if len(qs) == 1:
123
125
  qc.z(qs[0])
124
126
  elif len(qs) == 2:
@@ -420,12 +420,12 @@ class ExpressionMixin:
420
420
  else self._safe_eval(expr)
421
421
  return int(val)
422
422
 
423
- def _eval_with_vars(self, expr: str, run_vars: dict[str, Any]) -> float:
423
+ def _eval_with_vars(self, expr: str, run_vars: dict[str, Any]) -> float | Any:
424
424
  """Evaluate expression with runtime variables.
425
425
 
426
- A qiskit ParameterExpression (present only during a parametric
427
- MINIMIZE/GRADIENT/SWEEP build) passes through unfloated so it lands
428
- in the circuit as a bindable parameter.
426
+ Returns a float, except that a qiskit ParameterExpression (present
427
+ only during a parametric MINIMIZE/GRADIENT/SWEEP build) passes
428
+ through unfloated so it lands in the circuit as a bindable parameter.
429
429
  """
430
430
  val = self._safe_eval(expr, extra_ns=run_vars)
431
431
  try:
@@ -38,7 +38,12 @@ def _anticommute(a: str, b: str) -> int:
38
38
 
39
39
 
40
40
  def _pmul(a: str, b: str) -> str:
41
- return ''.join(_PMUL[(pa, pb)] for pa, pb in zip(a, b))
41
+ # Precondition: both strings over the IXYZ alphabet. The try keeps the
42
+ # per-trial hot path free of per-character validation.
43
+ try:
44
+ return ''.join(_PMUL[(pa, pb)] for pa, pb in zip(a, b))
45
+ except KeyError as e:
46
+ raise ValueError(f"Pauli strings must be over IXYZ (got {e.args[0]})") from None
42
47
 
43
48
 
44
49
  def _weight(p: str) -> int:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes