qubasic 0.6.4__tar.gz → 0.6.5__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 (61) hide show
  1. {qubasic-0.6.4 → qubasic-0.6.5}/CHANGELOG.md +5 -0
  2. {qubasic-0.6.4/qubasic.egg-info → qubasic-0.6.5}/PKG-INFO +1 -1
  3. {qubasic-0.6.4 → qubasic-0.6.5}/pyproject.toml +1 -1
  4. {qubasic-0.6.4 → qubasic-0.6.5/qubasic.egg-info}/PKG-INFO +1 -1
  5. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/__init__.py +1 -1
  6. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/display.py +5 -3
  7. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/memory.py +6 -4
  8. {qubasic-0.6.4 → qubasic-0.6.5}/tests/test_features.py +25 -0
  9. {qubasic-0.6.4 → qubasic-0.6.5}/LICENSE +0 -0
  10. {qubasic-0.6.4 → qubasic-0.6.5}/MANIFEST.in +0 -0
  11. {qubasic-0.6.4 → qubasic-0.6.5}/README.md +0 -0
  12. {qubasic-0.6.4 → qubasic-0.6.5}/examples/bell.qb +0 -0
  13. {qubasic-0.6.4 → qubasic-0.6.5}/examples/grover3.qb +0 -0
  14. {qubasic-0.6.4 → qubasic-0.6.5}/examples/locc_teleport.qb +0 -0
  15. {qubasic-0.6.4 → qubasic-0.6.5}/examples/sweep_rx.qb +0 -0
  16. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic.egg-info/SOURCES.txt +0 -0
  17. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic.egg-info/dependency_links.txt +0 -0
  18. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic.egg-info/entry_points.txt +0 -0
  19. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic.egg-info/requires.txt +0 -0
  20. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic.egg-info/top_level.txt +0 -0
  21. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/__main__.py +0 -0
  22. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/analysis.py +0 -0
  23. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/backend.py +0 -0
  24. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/classic.py +0 -0
  25. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/cli.py +0 -0
  26. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/control_flow.py +0 -0
  27. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/debug.py +0 -0
  28. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/demos.py +0 -0
  29. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/engine.py +0 -0
  30. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/engine_state.py +0 -0
  31. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/errors.py +0 -0
  32. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/exec_context.py +0 -0
  33. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/executor.py +0 -0
  34. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/expression.py +0 -0
  35. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/file_io.py +0 -0
  36. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/gates.py +0 -0
  37. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/help_text.py +0 -0
  38. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/io_protocol.py +0 -0
  39. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/locc.py +0 -0
  40. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/locc_commands.py +0 -0
  41. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/locc_display.py +0 -0
  42. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/locc_engine.py +0 -0
  43. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/locc_execution.py +0 -0
  44. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/mock_backend.py +0 -0
  45. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/noise_mixin.py +0 -0
  46. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/parser.py +0 -0
  47. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/patterns.py +0 -0
  48. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/profiler.py +0 -0
  49. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/program_mgmt.py +0 -0
  50. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/protocol.py +0 -0
  51. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/qol.py +0 -0
  52. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/scope.py +0 -0
  53. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/screen.py +0 -0
  54. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/state_display.py +0 -0
  55. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/statements.py +0 -0
  56. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/strings.py +0 -0
  57. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/subs.py +0 -0
  58. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/sweep.py +0 -0
  59. {qubasic-0.6.4 → qubasic-0.6.5}/qubasic_core/terminal.py +0 -0
  60. {qubasic-0.6.4 → qubasic-0.6.5}/setup.cfg +0 -0
  61. {qubasic-0.6.4 → qubasic-0.6.5}/tests/test_qubasic.py +0 -0
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.5 (2026-06-19)
4
+
5
+ ### Fixed
6
+ - The Bloch sphere Y component had an inverted sign, so a qubit in |+i> was reported at -Y (and |-i> at +Y). `BLOCH`, `DRAW`, `PRINT QUBIT(n)`, and the memory-mapped Bloch-Y field (`$0100 + q*8 + 2`) were all affected, and `BLOCH` mislabeled |+i> as "|-i>". X and Z were already correct. Y now matches the standard convention (`<Y> = +2 Im(<1|rho|0>)`), verified against Qiskit expectation values.
7
+
3
8
  ## 0.6.4 (2026-06-19)
4
9
 
5
10
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qubasic
3
- Version: 0.6.4
3
+ Version: 0.6.5
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.6.4"
7
+ version = "0.6.5"
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.6.4
3
+ Version: 0.6.5
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.6.4'
31
+ __version__ = '0.6.5'
32
32
 
33
33
  def __getattr__(name):
34
34
  """Lazy import heavy modules on first access."""
@@ -292,10 +292,12 @@ class DisplayMixin:
292
292
 
293
293
  rho_00 = np.sum(np.abs(t0)**2)
294
294
  rho_11 = np.sum(np.abs(t1)**2)
295
- rho_01 = np.sum(np.conj(t0) * t1)
295
+ # conj(t0)*t1 sums to rho_10 = <1|rho|0>. <X> = 2 Re(rho_10) (Re is
296
+ # symmetric in rho_01/rho_10), and <Y> = -2 Im(rho_01) = +2 Im(rho_10).
297
+ rho_10 = np.sum(np.conj(t0) * t1)
296
298
 
297
- x = float(2 * rho_01.real)
298
- y = float(-2 * rho_01.imag)
299
+ x = float(2 * rho_10.real)
300
+ y = float(2 * rho_10.imag)
299
301
  z = float(rho_00 - rho_11)
300
302
 
301
303
  return x, y, z
@@ -112,17 +112,19 @@ class MemoryMixin:
112
112
  t0, t1 = t[0].flatten(), t[1].flatten()
113
113
  rho_00 = float(np.sum(np.abs(t0) ** 2))
114
114
  rho_11 = float(np.sum(np.abs(t1) ** 2))
115
- rho_01 = complex(np.sum(np.conj(t0) * t1))
115
+ # conj(t0)*t1 sums to rho_10 = <1|rho|0>. Bloch X = 2 Re(rho_10) and
116
+ # Bloch Y = -2 Im(rho_01) = +2 Im(rho_10).
117
+ rho_10 = complex(np.sum(np.conj(t0) * t1))
116
118
  # Recover amplitudes with alpha as the real phase reference; beta then
117
119
  # carries the relative phase (real and imaginary parts both exposed).
118
120
  alpha = np.sqrt(max(0.0, rho_00))
119
121
  if alpha > 1e-12:
120
- beta = rho_01 / alpha
122
+ beta = rho_10 / alpha
121
123
  else:
122
124
  beta = complex(np.sqrt(max(0.0, rho_11)))
123
125
  return [rho_11,
124
- float(2 * rho_01.real),
125
- float(-2 * rho_01.imag),
126
+ float(2 * rho_10.real),
127
+ float(2 * rho_10.imag),
126
128
  float(rho_00 - rho_11),
127
129
  float(alpha),
128
130
  0.0,
@@ -2595,6 +2595,31 @@ class TestCommandSurfaceCoverage(unittest.TestCase):
2595
2595
  self.assertIsInstance(t._status_prompt(), str)
2596
2596
  capture(t._suggest_command, 'XYZ')
2597
2597
 
2598
+ def test_bloch_vector_axes(self):
2599
+ """_bloch_vector and the $0100 By field carry the right sign on every
2600
+ axis. Regression: the Y component was inverted (|+i> read as -Y)."""
2601
+ import numpy as np
2602
+ from qubasic_core.terminal import QBasicTerminal
2603
+ t = QBasicTerminal()
2604
+ r2 = 1.0 / np.sqrt(2)
2605
+ cases = [
2606
+ (np.array([1, 0], dtype=complex), (0.0, 0.0, +1.0)), # |0> +Z
2607
+ (np.array([0, 1], dtype=complex), (0.0, 0.0, -1.0)), # |1> -Z
2608
+ (np.array([r2, r2], dtype=complex), (+1.0, 0.0, 0.0)), # |+> +X
2609
+ (np.array([r2, -r2], dtype=complex), (-1.0, 0.0, 0.0)), # |-> -X
2610
+ (np.array([r2, 1j * r2], dtype=complex), (0.0, +1.0, 0.0)), # |+i> +Y
2611
+ (np.array([r2, -1j * r2], dtype=complex), (0.0, -1.0, 0.0)), # |-i> -Y
2612
+ ]
2613
+ for sv, (ex, ey, ez) in cases:
2614
+ x, y, z = t._bloch_vector(sv, 0, 1)
2615
+ self.assertAlmostEqual(x, ex, places=6)
2616
+ self.assertAlmostEqual(y, ey, places=6)
2617
+ self.assertAlmostEqual(z, ez, places=6)
2618
+ # Memory-mapped Bloch Y for q0 ($0102) must agree for |+i>.
2619
+ t.num_qubits = 1
2620
+ t.last_sv = np.array([r2, 1j * r2], dtype=complex)
2621
+ self.assertAlmostEqual(t._peek(0x0102), +1.0, places=6)
2622
+
2598
2623
  def test_program_and_io_surface(self):
2599
2624
  import builtins
2600
2625
  t = self._ready()
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
File without changes
File without changes
File without changes
File without changes
File without changes