sia-script 1.0.3__tar.gz → 1.3.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.
- {sia_script-1.0.3/sia_script.egg-info → sia_script-1.3.0}/PKG-INFO +1 -1
- {sia_script-1.0.3 → sia_script-1.3.0}/pyproject.toml +5 -4
- {sia_script-1.0.3 → sia_script-1.3.0}/sia/__init__.py +6 -8
- {sia_script-1.0.3 → sia_script-1.3.0}/sia/hdqs_engine.py +116 -112
- {sia_script-1.0.3 → sia_script-1.3.0/sia_script.egg-info}/PKG-INFO +1 -1
- {sia_script-1.0.3 → sia_script-1.3.0}/sia_script.egg-info/SOURCES.txt +1 -5
- sia_script-1.0.3/MANIFEST.in +0 -3
- sia_script-1.0.3/setup.py +0 -60
- sia_script-1.0.3/sia_script.egg-info/not-zip-safe +0 -1
- sia_script-1.0.3/src/hdqs_core.cpp +0 -786
- {sia_script-1.0.3 → sia_script-1.3.0}/LICENSE.md +0 -0
- {sia_script-1.0.3 → sia_script-1.3.0}/README.md +0 -0
- {sia_script-1.0.3 → sia_script-1.3.0}/setup.cfg +0 -0
- {sia_script-1.0.3 → sia_script-1.3.0}/sia/els.py +0 -0
- {sia_script-1.0.3 → sia_script-1.3.0}/sia/hdqs.py +0 -0
- {sia_script-1.0.3 → sia_script-1.3.0}/sia/ntt.py +0 -0
- {sia_script-1.0.3 → sia_script-1.3.0}/sia_script.egg-info/dependency_links.txt +0 -0
- {sia_script-1.0.3 → sia_script-1.3.0}/sia_script.egg-info/requires.txt +0 -0
- {sia_script-1.0.3 → sia_script-1.3.0}/sia_script.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sia-script
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: SIA — Simplified Integrated Architecture: SiaNTT, SIA-ELS (ABAP interpreter), the HDQS server client, and hdqs_engine (offline statevector quantum simulator — no server or API token required)
|
|
5
5
|
Author-email: Sia Software Innovations Private Limited <info@siasoftwareinnovations.com>
|
|
6
6
|
License: # PROPRIETARY SOFTWARE LICENSE
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=61.0", "wheel"
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sia-script"
|
|
7
|
-
version = "1.0
|
|
7
|
+
version = "1.3.0"
|
|
8
8
|
description = "SIA — Simplified Integrated Architecture: SiaNTT, SIA-ELS (ABAP interpreter), the HDQS server client, and hdqs_engine (offline statevector quantum simulator — no server or API token required)"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Sia Software Innovations Private Limited", email = "info@siasoftwareinnovations.com" }
|
|
@@ -62,7 +62,8 @@ client = []
|
|
|
62
62
|
# numpy is now a base dependency; kept as an empty extra for backward
|
|
63
63
|
# compatibility with anyone depending on sia-script[engine].
|
|
64
64
|
engine = []
|
|
65
|
-
# sia.hdqs_engine.torch_layer — trainable QuantumLayer
|
|
65
|
+
# sia.hdqs_engine.torch_layer — trainable QuantumLayer/BackpropQuantumLayer/
|
|
66
|
+
# MidCircuitQuantumLayer/QASMQuantumLayer (nn.Module).
|
|
66
67
|
torch = [
|
|
67
68
|
"torch>=2.0",
|
|
68
69
|
]
|
|
@@ -80,4 +81,4 @@ full = [
|
|
|
80
81
|
packages = ["sia"]
|
|
81
82
|
|
|
82
83
|
[tool.setuptools.package-data]
|
|
83
|
-
"sia" = ["*.py"]
|
|
84
|
+
"sia" = ["*.py"]
|
|
@@ -36,13 +36,8 @@ Client-side package. Loads:
|
|
|
36
36
|
aliases, register/slice broadcasting, reset/barrier/end,
|
|
37
37
|
and if/else and switch branching on measured bits.
|
|
38
38
|
Requires the `openqasm3[parser]` extra.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
AVX-512 kernels where available and automatic fallback to
|
|
42
|
-
pure NumPy on platforms without a compiled extension for
|
|
43
|
-
this build (see `sia.hdqs_engine._NATIVE_AVAILABLE`) --
|
|
44
|
-
a pure performance path, validated to produce identical
|
|
45
|
-
results to the NumPy path.
|
|
39
|
+
Pure Python (NumPy + optional PyTorch) — no compiled
|
|
40
|
+
extension, no build step, works on any platform.
|
|
46
41
|
MIT-licensed (see the MIT_LICENSE string in sia/hdqs_engine.py),
|
|
47
42
|
distinct from the proprietary license covering the rest of
|
|
48
43
|
this package.
|
|
@@ -53,7 +48,7 @@ this client package. They live in the server deployment only. What you get
|
|
|
53
48
|
here for local, offline quantum simulation is sia.hdqs_engine.
|
|
54
49
|
"""
|
|
55
50
|
|
|
56
|
-
__version__ = "1.
|
|
51
|
+
__version__ = "1.3.0"
|
|
57
52
|
__author__ = "Sia Software Innovations Private Limited"
|
|
58
53
|
# NOTE: hardcoding a bare IP couples every released client to one host. If
|
|
59
54
|
# the server ever moves, every installed copy breaks. Prefer a DNS name
|
|
@@ -194,6 +189,7 @@ try:
|
|
|
194
189
|
from .hdqs_engine import (
|
|
195
190
|
# Core class (renamed from StatevectorEngine)
|
|
196
191
|
HDQS,
|
|
192
|
+
PI,
|
|
197
193
|
GATES,
|
|
198
194
|
controlled,
|
|
199
195
|
random_state,
|
|
@@ -265,6 +261,7 @@ except ImportError as _engine_import_error:
|
|
|
265
261
|
_warnings.warn(f"sia.hdqs_engine unavailable: {_engine_import_error}",
|
|
266
262
|
ImportWarning)
|
|
267
263
|
HDQS = None
|
|
264
|
+
PI = None
|
|
268
265
|
StatevectorEngine = None
|
|
269
266
|
GATES = None
|
|
270
267
|
controlled = None
|
|
@@ -371,6 +368,7 @@ __all__ = [
|
|
|
371
368
|
# HDQS-Engine (offline, no server/token required)
|
|
372
369
|
# Core class (renamed for cleaner API)
|
|
373
370
|
"HDQS",
|
|
371
|
+
"PI",
|
|
374
372
|
"StatevectorEngine", # Legacy alias for backward compatibility
|
|
375
373
|
"GATES",
|
|
376
374
|
"controlled",
|
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
sia.hdqs_engine — offline quantum simulator with automatic GPU support
|
|
3
3
|
================================================================================
|
|
4
4
|
|
|
5
|
-
Version 1.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
flushes), hand-vectorized with AVX2 and AVX-512 (FMA-based complex
|
|
9
|
-
multiply) where available, with automatic fallback to portable NumPy if
|
|
10
|
-
the compiled extension isn't present for a given platform -- see
|
|
11
|
-
``_NATIVE_AVAILABLE``. Also adds OpenQASM 3 import/export
|
|
5
|
+
Version 1.1.0 — pure Python (NumPy + optional PyTorch) implementation.
|
|
6
|
+
No compiled extension, no build step -- `pip install`/`import` and it
|
|
7
|
+
works, on any platform. Adds OpenQASM 3 import/export
|
|
12
8
|
(``HDQS.from_qasm3``/``to_qasm3``, tier 1+2: constants, for-loops, custom
|
|
13
9
|
gate inlining, gate modifiers, switch/case, aliasing), a
|
|
14
10
|
mid-circuit-measurement-aware batched trainable layer
|
|
@@ -33,13 +29,6 @@ Conventions
|
|
|
33
29
|
unavailable); ``use_gpu=False`` forces the NumPy backend. ``device=``
|
|
34
30
|
overrides for power users (e.g. device='cpu' runs the PyTorch backend on
|
|
35
31
|
CPU — same code path as CUDA, useful for testing).
|
|
36
|
-
* Native acceleration (optional): when the compiled ``hdqs_core`` extension
|
|
37
|
-
is importable, statevector gate application routes through it instead of
|
|
38
|
-
NumPy's einsum-based path. This is a pure performance path -- results
|
|
39
|
-
are validated identical to the NumPy path across hundreds of randomized
|
|
40
|
-
circuits per release, covering every gate family including asymmetric
|
|
41
|
-
multi-qubit matrices (cswap). No behavior changes based on whether the
|
|
42
|
-
extension is present; only speed does.
|
|
43
32
|
|
|
44
33
|
Changelog v0.4.0 (from v0.3.0)
|
|
45
34
|
------------------------------
|
|
@@ -85,7 +74,9 @@ from typing import (Any, Callable, Dict, Iterable, List, Optional, Sequence,
|
|
|
85
74
|
|
|
86
75
|
import numpy as np
|
|
87
76
|
|
|
88
|
-
__version__ = "1.
|
|
77
|
+
__version__ = "1.3.0"
|
|
78
|
+
|
|
79
|
+
PI = np.pi # convenience alias — HDQS(1).rx(0, PI/2) instead of np.pi/2
|
|
89
80
|
|
|
90
81
|
# ==========================================================================
|
|
91
82
|
|
|
@@ -175,28 +166,6 @@ except ImportError:
|
|
|
175
166
|
pass
|
|
176
167
|
|
|
177
168
|
|
|
178
|
-
# --------------------------------------------------------------------------
|
|
179
|
-
# Native (C++) kernel detection. Optional: this module works fully on pure
|
|
180
|
-
# NumPy without it. When present, the statevector gate-application and
|
|
181
|
-
# single-qubit-Kraus-channel hot paths route through compiled kernels
|
|
182
|
-
# instead of NumPy's einsum/moveaxis dispatch (which dominates per-gate
|
|
183
|
-
# overhead — profiling showed ~70% of time in bookkeeping, not arithmetic).
|
|
184
|
-
# Measured: ~5-25x faster on statevector gates, ~4-12x faster on Kraus
|
|
185
|
-
# channels, scaling up with qubit count, single-threaded.
|
|
186
|
-
# --------------------------------------------------------------------------
|
|
187
|
-
_NATIVE_AVAILABLE = False
|
|
188
|
-
_hdqs_native = None
|
|
189
|
-
try:
|
|
190
|
-
from . import hdqs_core as _hdqs_native # noqa: F401
|
|
191
|
-
_NATIVE_AVAILABLE = True
|
|
192
|
-
except ImportError:
|
|
193
|
-
try:
|
|
194
|
-
import hdqs_core as _hdqs_native # noqa: F401 (standalone/dev use)
|
|
195
|
-
_NATIVE_AVAILABLE = True
|
|
196
|
-
except ImportError:
|
|
197
|
-
pass
|
|
198
|
-
|
|
199
|
-
|
|
200
169
|
def is_tensor(arr: Any) -> bool:
|
|
201
170
|
"""True iff *arr* is a PyTorch tensor. Safe when torch is not installed
|
|
202
171
|
(v0.3.0 raised NameError here, making the module unusable without torch)."""
|
|
@@ -613,6 +582,15 @@ class HDQS:
|
|
|
613
582
|
"readout_error": readout_error_channel,
|
|
614
583
|
}
|
|
615
584
|
|
|
585
|
+
@classmethod
|
|
586
|
+
def dm(cls, num_qubits: int, **kwargs) -> "HDQS":
|
|
587
|
+
"""Alternative constructor for density-matrix mode: behaviorally
|
|
588
|
+
identical to HDQS(num_qubits, use_density_matrix=True, **kwargs),
|
|
589
|
+
including every other constructor argument (seed, use_gpu,
|
|
590
|
+
device). The use_density_matrix=True keyword form remains fully
|
|
591
|
+
supported -- this is an additional spelling, not a replacement."""
|
|
592
|
+
return cls(num_qubits, use_density_matrix=True, **kwargs)
|
|
593
|
+
|
|
616
594
|
# -- backend helpers ------------------------------------------------------
|
|
617
595
|
|
|
618
596
|
def _to_backend(self, arr):
|
|
@@ -802,27 +780,6 @@ class HDQS:
|
|
|
802
780
|
raise ValueError(
|
|
803
781
|
f"unitary shape {U.shape} does not match {k} target(s)")
|
|
804
782
|
n = self.num_qubits
|
|
805
|
-
if (not self._use_density_matrix and _NATIVE_AVAILABLE
|
|
806
|
-
and not self._backend_torch):
|
|
807
|
-
bitpos = [n - 1 - t for t in targets]
|
|
808
|
-
if k == 1:
|
|
809
|
-
sv = np.ascontiguousarray(self._sv, dtype=complex)
|
|
810
|
-
_hdqs_native.apply_1q(sv, bitpos[0],
|
|
811
|
-
U[0, 0], U[0, 1], U[1, 0], U[1, 1])
|
|
812
|
-
self._sv = sv
|
|
813
|
-
return self
|
|
814
|
-
if k == 2 and np.array_equal(U, _SWAP_MATRIX):
|
|
815
|
-
sv = np.ascontiguousarray(self._sv, dtype=complex)
|
|
816
|
-
_hdqs_native.apply_swap(sv, bitpos[0], bitpos[1])
|
|
817
|
-
self._sv = sv
|
|
818
|
-
return self
|
|
819
|
-
sv = np.ascontiguousarray(self._sv, dtype=complex)
|
|
820
|
-
if k <= 4:
|
|
821
|
-
_hdqs_native.apply_gate_fast(sv, bitpos, U)
|
|
822
|
-
else:
|
|
823
|
-
_hdqs_native.apply_gate(sv, bitpos, U, [])
|
|
824
|
-
self._sv = sv
|
|
825
|
-
return self
|
|
826
783
|
Ub = self._to_backend(U)
|
|
827
784
|
xp = self._xp
|
|
828
785
|
if not self._use_density_matrix:
|
|
@@ -843,37 +800,6 @@ class HDQS:
|
|
|
843
800
|
target: int) -> "HDQS":
|
|
844
801
|
"""Apply single-qubit U to *target* conditioned on all controls = 1."""
|
|
845
802
|
controls = list(controls)
|
|
846
|
-
if (not self._use_density_matrix and _NATIVE_AVAILABLE
|
|
847
|
-
and not self._backend_torch):
|
|
848
|
-
U = np.asarray(U, dtype=complex)
|
|
849
|
-
# The native kernel takes an explicit control list, so a
|
|
850
|
-
# multi-controlled gate (mcx with many controls, crz, ...)
|
|
851
|
-
# never needs the 2^(k+1)x2^(k+1) padded-identity matrix the
|
|
852
|
-
# NumPy path below has to materialize -- this is an
|
|
853
|
-
# algorithmic win, not just a constant-factor one.
|
|
854
|
-
n = self.num_qubits
|
|
855
|
-
bit_target = [n - 1 - target]
|
|
856
|
-
bit_controls = [n - 1 - c for c in controls]
|
|
857
|
-
if len(controls) == 1 and np.array_equal(U, _X):
|
|
858
|
-
# pure index-swap, no arithmetic -- fastest path for the
|
|
859
|
-
# single most common 2-qubit gate
|
|
860
|
-
sv = np.ascontiguousarray(self._sv, dtype=complex)
|
|
861
|
-
_hdqs_native.apply_cnot_fast(sv, bit_controls[0], bit_target[0])
|
|
862
|
-
self._sv = sv
|
|
863
|
-
return self
|
|
864
|
-
if len(controls) == 1:
|
|
865
|
-
# covers cy/cz/cp/crx/cry/crz -- any single-control,
|
|
866
|
-
# single-target gate with an arbitrary 2x2 U
|
|
867
|
-
sv = np.ascontiguousarray(self._sv, dtype=complex)
|
|
868
|
-
_hdqs_native.apply_controlled_1q(
|
|
869
|
-
sv, bit_controls[0], bit_target[0],
|
|
870
|
-
U[0, 0], U[0, 1], U[1, 0], U[1, 1])
|
|
871
|
-
self._sv = sv
|
|
872
|
-
return self
|
|
873
|
-
sv = np.ascontiguousarray(self._sv, dtype=complex)
|
|
874
|
-
_hdqs_native.apply_gate(sv, bit_target, U, bit_controls)
|
|
875
|
-
self._sv = sv
|
|
876
|
-
return self
|
|
877
803
|
full = np.eye(2 ** (len(controls) + 1), dtype=complex)
|
|
878
804
|
full[-2:, -2:] = np.asarray(U, dtype=complex)
|
|
879
805
|
# target=LSB, controls=upper bits (see _apply_matrix_on_axes's
|
|
@@ -904,13 +830,6 @@ class HDQS:
|
|
|
904
830
|
f"{name}: Kraus dimension {ks[0].shape[0]} does not match "
|
|
905
831
|
f"{k} target qubit(s)")
|
|
906
832
|
n = self.num_qubits
|
|
907
|
-
if k == 1 and _NATIVE_AVAILABLE and not self._backend_torch:
|
|
908
|
-
bitpos = n - 1 - targets[0]
|
|
909
|
-
rho = np.ascontiguousarray(self._rho, dtype=complex)
|
|
910
|
-
_hdqs_native.apply_kraus_1q(rho, n, bitpos, ks)
|
|
911
|
-
self._rho = _renormalize_rho(rho)
|
|
912
|
-
self._is_pure = self._check_purity()
|
|
913
|
-
return self
|
|
914
833
|
xp = self._xp
|
|
915
834
|
T = self._rho.reshape((2,) * (2 * n))
|
|
916
835
|
col_axes = [n + t for t in targets]
|
|
@@ -938,8 +857,22 @@ class HDQS:
|
|
|
938
857
|
|
|
939
858
|
# -- single-qubit gates ---------------------------------------------------
|
|
940
859
|
|
|
941
|
-
def
|
|
942
|
-
|
|
860
|
+
def _resolve_optional_qubit(self, q: Optional[int]) -> int:
|
|
861
|
+
"""Resolve an omitted qubit index for the zero-parameter single-qubit
|
|
862
|
+
gates (h/x/y/z/s/sdg/t/tdg) to the sole qubit of a 1-qubit register.
|
|
863
|
+
Deliberately not extended to angle-parameter gates (rx/ry/rz/p/u3):
|
|
864
|
+
making their qubit index optional would require either reordering
|
|
865
|
+
their argument list or making the angle keyword-only, either of
|
|
866
|
+
which breaks existing call sites. Ambiguity always raises rather
|
|
867
|
+
than silently defaulting."""
|
|
868
|
+
if q is not None:
|
|
869
|
+
return q
|
|
870
|
+
if self.num_qubits != 1:
|
|
871
|
+
raise ValueError(
|
|
872
|
+
"qubit index is required when the register has more than "
|
|
873
|
+
f"one qubit ({self.num_qubits} qubits here); only a "
|
|
874
|
+
"1-qubit register can omit it")
|
|
875
|
+
return 0
|
|
943
876
|
|
|
944
877
|
# -- circuit logging --------------------------------------------------
|
|
945
878
|
|
|
@@ -955,42 +888,50 @@ class HDQS:
|
|
|
955
888
|
|
|
956
889
|
# -- single-qubit gates -----------------------------------------------
|
|
957
890
|
|
|
958
|
-
def h(self, q: int) -> "HDQS":
|
|
891
|
+
def h(self, q: Optional[int] = None) -> "HDQS":
|
|
892
|
+
q = self._resolve_optional_qubit(q)
|
|
959
893
|
self._log(f"H({q})")
|
|
960
894
|
self._ops.append(("h", [q], []))
|
|
961
895
|
return self._apply_unitary(_H, [q])
|
|
962
896
|
|
|
963
|
-
def x(self, q: int) -> "HDQS":
|
|
897
|
+
def x(self, q: Optional[int] = None) -> "HDQS":
|
|
898
|
+
q = self._resolve_optional_qubit(q)
|
|
964
899
|
self._log(f"X({q})")
|
|
965
900
|
self._ops.append(("x", [q], []))
|
|
966
901
|
return self._apply_unitary(_X, [q])
|
|
967
902
|
|
|
968
|
-
def y(self, q: int) -> "HDQS":
|
|
903
|
+
def y(self, q: Optional[int] = None) -> "HDQS":
|
|
904
|
+
q = self._resolve_optional_qubit(q)
|
|
969
905
|
self._log(f"Y({q})")
|
|
970
906
|
self._ops.append(("y", [q], []))
|
|
971
907
|
return self._apply_unitary(_Y, [q])
|
|
972
908
|
|
|
973
|
-
def z(self, q: int) -> "HDQS":
|
|
909
|
+
def z(self, q: Optional[int] = None) -> "HDQS":
|
|
910
|
+
q = self._resolve_optional_qubit(q)
|
|
974
911
|
self._log(f"Z({q})")
|
|
975
912
|
self._ops.append(("z", [q], []))
|
|
976
913
|
return self._apply_unitary(_Z, [q])
|
|
977
914
|
|
|
978
|
-
def s(self, q: int) -> "HDQS":
|
|
915
|
+
def s(self, q: Optional[int] = None) -> "HDQS":
|
|
916
|
+
q = self._resolve_optional_qubit(q)
|
|
979
917
|
self._log(f"S({q})")
|
|
980
918
|
self._ops.append(("s", [q], []))
|
|
981
919
|
return self._apply_unitary(_S, [q])
|
|
982
920
|
|
|
983
|
-
def sdg(self, q: int) -> "HDQS":
|
|
921
|
+
def sdg(self, q: Optional[int] = None) -> "HDQS":
|
|
922
|
+
q = self._resolve_optional_qubit(q)
|
|
984
923
|
self._log(f"Sdg({q})")
|
|
985
924
|
self._ops.append(("sdg", [q], []))
|
|
986
925
|
return self._apply_unitary(GATES["SDG"], [q])
|
|
987
926
|
|
|
988
|
-
def t(self, q: int) -> "HDQS":
|
|
927
|
+
def t(self, q: Optional[int] = None) -> "HDQS":
|
|
928
|
+
q = self._resolve_optional_qubit(q)
|
|
989
929
|
self._log(f"T({q})")
|
|
990
930
|
self._ops.append(("t", [q], []))
|
|
991
931
|
return self._apply_unitary(_T, [q])
|
|
992
932
|
|
|
993
|
-
def tdg(self, q: int) -> "HDQS":
|
|
933
|
+
def tdg(self, q: Optional[int] = None) -> "HDQS":
|
|
934
|
+
q = self._resolve_optional_qubit(q)
|
|
994
935
|
self._log(f"Tdg({q})")
|
|
995
936
|
self._ops.append(("tdg", [q], []))
|
|
996
937
|
return self._apply_unitary(GATES["TDG"], [q])
|
|
@@ -1130,6 +1071,46 @@ class HDQS:
|
|
|
1130
1071
|
q, generalized_amplitude_damping_channel(p, gamma),
|
|
1131
1072
|
"generalized_amplitude_damping")
|
|
1132
1073
|
|
|
1074
|
+
_NOISE_CHANNEL_METHODS = frozenset({
|
|
1075
|
+
"bit_flip", "phase_flip", "depolarizing",
|
|
1076
|
+
"amplitude_damping", "phase_damping",
|
|
1077
|
+
})
|
|
1078
|
+
|
|
1079
|
+
def with_noise(self, channel: str, *params) -> "HDQS":
|
|
1080
|
+
"""Return a NEW HDQS instance (this one is left untouched) that
|
|
1081
|
+
replays every gate applied so far, inserting `channel` on every
|
|
1082
|
+
qubit each gate touches, right after that gate. One-liner analog
|
|
1083
|
+
of Cirq's `circuit.with_noise(cirq.depolarize(0.01))`; scoped to
|
|
1084
|
+
the single-qubit channels already on this class (`channel` is one
|
|
1085
|
+
of "bit_flip"/"phase_flip"/"depolarizing"/"amplitude_damping"/
|
|
1086
|
+
"phase_damping", `*params` are that channel's own arguments, e.g.
|
|
1087
|
+
`with_noise("depolarizing", 0.01)`).
|
|
1088
|
+
|
|
1089
|
+
Because HDQS executes eagerly rather than building a circuit
|
|
1090
|
+
object first, "insert noise after the fact" means replaying the
|
|
1091
|
+
recorded gate history (self._ops) onto a fresh density-matrix
|
|
1092
|
+
instance rather than mutating a DAG in place -- the result is the
|
|
1093
|
+
same circuit, but noise-affected, and this instance's own state
|
|
1094
|
+
is unchanged. For gates that don't record every touched qubit
|
|
1095
|
+
symmetrically (e.g. mcx's controls), noise is applied to every
|
|
1096
|
+
qubit index the gate's own _ops entry lists, matching the common
|
|
1097
|
+
"apply a gate-error channel per qubit the gate acts on"
|
|
1098
|
+
convention used by most noisy-simulation tooling.
|
|
1099
|
+
"""
|
|
1100
|
+
if channel not in self._NOISE_CHANNEL_METHODS:
|
|
1101
|
+
raise ValueError(
|
|
1102
|
+
f"with_noise: unknown channel {channel!r}; must be one "
|
|
1103
|
+
f"of {sorted(self._NOISE_CHANNEL_METHODS)}")
|
|
1104
|
+
noisy = HDQS(self.num_qubits, use_density_matrix=True)
|
|
1105
|
+
channel_method = getattr(noisy, channel)
|
|
1106
|
+
for name, qubits, gate_params in self._ops:
|
|
1107
|
+
if name in ("measure", "reset", "barrier"):
|
|
1108
|
+
continue # non-unitary/no-op entries, nothing to replay
|
|
1109
|
+
_qasm3_dispatch_gate(noisy, name, qubits, gate_params)
|
|
1110
|
+
for q in qubits:
|
|
1111
|
+
channel_method(q, *params)
|
|
1112
|
+
return noisy
|
|
1113
|
+
|
|
1133
1114
|
def thermal_noise(self, q: int, p: float, T: float = 1.0) -> "HDQS":
|
|
1134
1115
|
self._log(f"Thermal({q},p={p:.3g},T={T:.3g})")
|
|
1135
1116
|
return self._apply_noise(q, thermal_noise_channel(p, T), "thermal")
|
|
@@ -1334,21 +1315,42 @@ class HDQS:
|
|
|
1334
1315
|
|
|
1335
1316
|
# -- analysis -------------------------------------------------------------
|
|
1336
1317
|
|
|
1337
|
-
def expectation(self, pauli_string: str
|
|
1338
|
-
|
|
1318
|
+
def expectation(self, pauli_string: str,
|
|
1319
|
+
on: Optional[Sequence[int]] = None) -> float:
|
|
1320
|
+
"""Expectation value of a Pauli string.
|
|
1321
|
+
|
|
1322
|
+
Two forms:
|
|
1323
|
+
q.expectation("ZZI") # full string, one letter per qubit
|
|
1324
|
+
q.expectation("Z", on=1) # short form: Pauli letters at the
|
|
1325
|
+
q.expectation("ZZ", on=[0,2]) # given qubit(s), identity elsewhere
|
|
1326
|
+
`on` accepts a single int or a sequence the same length as
|
|
1327
|
+
`pauli_string`; every other qubit is treated as I. The short form
|
|
1328
|
+
exists so a Pauli acting on a handful of qubits out of a large
|
|
1329
|
+
register doesn't require hand-writing an identity-padded string
|
|
1330
|
+
the length of the whole register.
|
|
1339
1331
|
|
|
1340
1332
|
Computed by axis-contraction in both modes and both backends —
|
|
1341
1333
|
v0.3.0 built a dense 2^n x 2^n operator (and raised
|
|
1342
1334
|
NotImplementedError on the GPU pure-state path)."""
|
|
1343
|
-
|
|
1344
|
-
if
|
|
1335
|
+
n = self.num_qubits
|
|
1336
|
+
if on is not None:
|
|
1337
|
+
on = [on] if isinstance(on, int) else list(on)
|
|
1338
|
+
if len(on) != len(pauli_string):
|
|
1339
|
+
raise ValueError(
|
|
1340
|
+
f"pauli_string has {len(pauli_string)} letter(s) but "
|
|
1341
|
+
f"on= gives {len(on)} qubit(s) -- these must match")
|
|
1342
|
+
letters = dict(zip(on, pauli_string.upper()))
|
|
1343
|
+
s = "".join(letters.get(q, "I") for q in range(n))
|
|
1344
|
+
else:
|
|
1345
|
+
s = pauli_string.upper()
|
|
1346
|
+
if len(s) != n:
|
|
1345
1347
|
raise ValueError(
|
|
1346
|
-
f"pauli string must have length {
|
|
1348
|
+
f"pauli string must have length {n} qubits "
|
|
1349
|
+
"(or pass on= to place a short string on specific qubits)")
|
|
1347
1350
|
for ch in s:
|
|
1348
1351
|
if ch not in "IXYZ":
|
|
1349
1352
|
raise ValueError(f"invalid Pauli letter: {ch}")
|
|
1350
1353
|
xp = self._xp
|
|
1351
|
-
n = self.num_qubits
|
|
1352
1354
|
if not self._use_density_matrix:
|
|
1353
1355
|
T = self._sv.reshape((2,) * n)
|
|
1354
1356
|
for q, ch in enumerate(s):
|
|
@@ -2092,6 +2094,8 @@ def _qasm3_dispatch_gate(q: "HDQS", name: str, idx: List[int],
|
|
|
2092
2094
|
q.ccx(idx[0], idx[1], idx[2])
|
|
2093
2095
|
elif name == "cswap":
|
|
2094
2096
|
q.cswap(idx[0], idx[1], idx[2])
|
|
2097
|
+
elif name == "mcx":
|
|
2098
|
+
q.mcx(idx[:-1], idx[-1])
|
|
2095
2099
|
else:
|
|
2096
2100
|
raise NotImplementedError(f"from_qasm3: gate {name!r}")
|
|
2097
2101
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sia-script
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: SIA — Simplified Integrated Architecture: SiaNTT, SIA-ELS (ABAP interpreter), the HDQS server client, and hdqs_engine (offline statevector quantum simulator — no server or API token required)
|
|
5
5
|
Author-email: Sia Software Innovations Private Limited <info@siasoftwareinnovations.com>
|
|
6
6
|
License: # PROPRIETARY SOFTWARE LICENSE
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
LICENSE.md
|
|
2
|
-
MANIFEST.in
|
|
3
2
|
README.md
|
|
4
3
|
pyproject.toml
|
|
5
|
-
setup.py
|
|
6
4
|
sia/__init__.py
|
|
7
5
|
sia/els.py
|
|
8
6
|
sia/hdqs.py
|
|
@@ -11,7 +9,5 @@ sia/ntt.py
|
|
|
11
9
|
sia_script.egg-info/PKG-INFO
|
|
12
10
|
sia_script.egg-info/SOURCES.txt
|
|
13
11
|
sia_script.egg-info/dependency_links.txt
|
|
14
|
-
sia_script.egg-info/not-zip-safe
|
|
15
12
|
sia_script.egg-info/requires.txt
|
|
16
|
-
sia_script.egg-info/top_level.txt
|
|
17
|
-
src/hdqs_core.cpp
|
|
13
|
+
sia_script.egg-info/top_level.txt
|
sia_script-1.0.3/MANIFEST.in
DELETED
sia_script-1.0.3/setup.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Build script for the sia-script native extension (hdqs_core).
|
|
3
|
-
|
|
4
|
-
This compiles src/hdqs_core.cpp into sia/hdqs_core.<platform-tag>.so via
|
|
5
|
-
pybind11 -- the same mechanism Qiskit Aer and PennyLane's lightning.qubit
|
|
6
|
-
use (a thin Python package wrapping a compiled kernel).
|
|
7
|
-
|
|
8
|
-
Local development build:
|
|
9
|
-
pip install pybind11
|
|
10
|
-
python setup.py build_ext --inplace
|
|
11
|
-
|
|
12
|
-
This places the compiled module directly in sia/, where hdqs_engine.py's
|
|
13
|
-
`from . import hdqs_core` picks it up automatically. If the build fails or
|
|
14
|
-
is skipped, hdqs_engine.py still works -- it falls back to the pure NumPy
|
|
15
|
-
path and sets _NATIVE_AVAILABLE = False.
|
|
16
|
-
"""
|
|
17
|
-
import sys
|
|
18
|
-
from setuptools import setup, Extension
|
|
19
|
-
from pybind11.setup_helpers import Pybind11Extension, build_ext
|
|
20
|
-
|
|
21
|
-
# -O3 for standard optimization. -march=native/-mavx2/-mavx512f are
|
|
22
|
-
# deliberately NOT used here, and no longer need to be: hdqs_core.cpp
|
|
23
|
-
# now does its own runtime CPU dispatch (GCC/Clang function
|
|
24
|
-
# multiversioning + __builtin_cpu_supports, checked once at first call)
|
|
25
|
-
# for the four hottest kernels. This one build, compiled with these
|
|
26
|
-
# plain flags, contains scalar/AVX2/AVX-512 code paths simultaneously
|
|
27
|
-
# and picks the fastest one the *running* CPU actually supports --
|
|
28
|
-
# verified: compiled with zero SIMD flags, still dispatches to AVX-512
|
|
29
|
-
# on an AVX-512-capable machine, at the same speed as an explicitly
|
|
30
|
-
# -mavx512f-flagged build. No crash risk on older CPUs, no separate
|
|
31
|
-
# "fast" build needed. (This applies to GCC/Clang on x86_64 -- MSVC/
|
|
32
|
-
# Windows builds don't yet get automatic dispatch and fall back to the
|
|
33
|
-
# single compile-time path, i.e. scalar unless /arch:AVX2 is passed
|
|
34
|
-
# explicitly -- a known, stated gap, not a regression from before.)
|
|
35
|
-
extra_compile_args = ["-O3", "-std=c++17"]
|
|
36
|
-
extra_link_args = []
|
|
37
|
-
|
|
38
|
-
if sys.platform == "win32":
|
|
39
|
-
extra_compile_args = ["/O2", "/std:c++17", "/openmp"]
|
|
40
|
-
else:
|
|
41
|
-
extra_compile_args += ["-fopenmp"]
|
|
42
|
-
extra_link_args += ["-fopenmp"]
|
|
43
|
-
|
|
44
|
-
ext_modules = [
|
|
45
|
-
Pybind11Extension(
|
|
46
|
-
"sia.hdqs_core",
|
|
47
|
-
["src/hdqs_core.cpp"],
|
|
48
|
-
cxx_std=17,
|
|
49
|
-
extra_compile_args=extra_compile_args,
|
|
50
|
-
extra_link_args=extra_link_args,
|
|
51
|
-
),
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
setup(
|
|
55
|
-
packages=["sia"],
|
|
56
|
-
package_dir={"sia": "sia"},
|
|
57
|
-
ext_modules=ext_modules,
|
|
58
|
-
cmdclass={"build_ext": build_ext},
|
|
59
|
-
zip_safe=False,
|
|
60
|
-
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|