zena-sdk 0.1.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.
@@ -0,0 +1,59 @@
1
+ # General / IDEs
2
+ .DS_Store
3
+ Thumbs.db
4
+ .vscode/
5
+ .idea/
6
+ *.swp
7
+
8
+ # Python
9
+ __pycache__/
10
+ *.pyc
11
+ *.pyo
12
+ *.pyd
13
+ *.egg-info/
14
+ .egg-info
15
+ .venv/
16
+ venv/
17
+ env/
18
+ .env
19
+ .mypy_cache/
20
+ .tox/
21
+
22
+ # Testing and Coverage
23
+ .coverage
24
+ htmlcov/
25
+ .pytest_cache/
26
+ .hypothesis/
27
+ junit.xml
28
+ cov.xml
29
+
30
+ # Rust
31
+ target/
32
+ **/*.rs.bk
33
+
34
+ # Node.js / Frontend
35
+ node_modules/
36
+ frontend/.vite/
37
+ frontend/dist/
38
+ npm-debug.log*
39
+ yarn-debug.log*
40
+ yarn-error.log*
41
+ .npm/
42
+ .next/
43
+
44
+ # Generated Artifacts / Logs
45
+ *.log
46
+ *.bak
47
+ test_*.png
48
+ *_results.png
49
+ *.ir
50
+ output.txt
51
+ app.py.local_backup
52
+
53
+ # Documentation
54
+ docs/_build/
55
+
56
+ # Project Specific
57
+ web_app_copy/
58
+ features/
59
+ tests/
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: zena-sdk
3
+ Version: 0.1.0
4
+ Summary: A high-level Python SDK for the Zena Quantum Simulator, inspired by Qiskit.
5
+ Author-email: Zena Team <info@zena.quantum>
6
+ License: Apache-2.0
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Physics
13
+ Requires-Python: >=3.8
14
+ Requires-Dist: numpy>=1.20
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest; extra == 'dev'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # Zena SDK
20
+
21
+ **Zena SDK** is a high-level Python library for creating and simulating quantum circuits, designed with a familiar API for users of Qiskit. It serves as the user-facing layer for the powerful Zena Quantum Simulator engine.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install zena-sdk
27
+ ```
28
+
29
+ *(Note: Requires valid `qsys` and `simulator_statevector` backend installations)*
30
+
31
+ ## Basic Usage
32
+
33
+ Zena SDK allows you to build circuits using `QuantumRegister`, `ClassicalRegister`, and `QuantumCircuit` in a way that feels completely natural.
34
+
35
+ ```python
36
+ from zena import QuantumCircuit, execute, Aer
37
+
38
+ # 1. Create a Quantum Circuit
39
+ qc = QuantumCircuit(2, 2)
40
+ qc.h(0)
41
+ qc.cx(0, 1)
42
+ qc.measure([0, 1], [0, 1])
43
+
44
+ # 2. visualizae
45
+ print(qc.draw())
46
+
47
+ # 3. Choose a Backend
48
+ backend = Aer.get_backend('statevector_simulator')
49
+
50
+ # 4. Execute
51
+ job = execute(qc, backend, shots=1024)
52
+ result = job.result()
53
+
54
+ # 5. Get Results
55
+ print("Counts:", result.get_counts())
56
+ ```
57
+
58
+ ## Features
59
+
60
+ - **Familiar API**: Drop-in conceptual replacement for basic Qiskit circuits.
61
+ - **High Performance**: Powered by a Rust-based statevector simulator.
62
+ - **Visualizations**: Built-in methods for circuit drawing and result plotting.
63
+ - **Extensible**: Modular architecture allowing for future backend plugins.
64
+
65
+ ## Architecture
66
+
67
+ Zena SDK (`zena`) sits on top of the Core Engine (`qsys`).
68
+
69
+ - **Base Layer**: `qsys` (Intermediate Representation & Composer)
70
+ - **Simulation Layer**: `simulator_statevector` (Rust-based capabilities)
71
+ - **User Layer**: `zena` (High-level Circuit & Provider API)
@@ -0,0 +1,53 @@
1
+ # Zena SDK
2
+
3
+ **Zena SDK** is a high-level Python library for creating and simulating quantum circuits, designed with a familiar API for users of Qiskit. It serves as the user-facing layer for the powerful Zena Quantum Simulator engine.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install zena-sdk
9
+ ```
10
+
11
+ *(Note: Requires valid `qsys` and `simulator_statevector` backend installations)*
12
+
13
+ ## Basic Usage
14
+
15
+ Zena SDK allows you to build circuits using `QuantumRegister`, `ClassicalRegister`, and `QuantumCircuit` in a way that feels completely natural.
16
+
17
+ ```python
18
+ from zena import QuantumCircuit, execute, Aer
19
+
20
+ # 1. Create a Quantum Circuit
21
+ qc = QuantumCircuit(2, 2)
22
+ qc.h(0)
23
+ qc.cx(0, 1)
24
+ qc.measure([0, 1], [0, 1])
25
+
26
+ # 2. visualizae
27
+ print(qc.draw())
28
+
29
+ # 3. Choose a Backend
30
+ backend = Aer.get_backend('statevector_simulator')
31
+
32
+ # 4. Execute
33
+ job = execute(qc, backend, shots=1024)
34
+ result = job.result()
35
+
36
+ # 5. Get Results
37
+ print("Counts:", result.get_counts())
38
+ ```
39
+
40
+ ## Features
41
+
42
+ - **Familiar API**: Drop-in conceptual replacement for basic Qiskit circuits.
43
+ - **High Performance**: Powered by a Rust-based statevector simulator.
44
+ - **Visualizations**: Built-in methods for circuit drawing and result plotting.
45
+ - **Extensible**: Modular architecture allowing for future backend plugins.
46
+
47
+ ## Architecture
48
+
49
+ Zena SDK (`zena`) sits on top of the Core Engine (`qsys`).
50
+
51
+ - **Base Layer**: `qsys` (Intermediate Representation & Composer)
52
+ - **Simulation Layer**: `simulator_statevector` (Rust-based capabilities)
53
+ - **User Layer**: `zena` (High-level Circuit & Provider API)
@@ -0,0 +1,7 @@
1
+ from .circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
2
+ from .providers.aer import Aer
3
+ from .execute import execute
4
+ from .visualization import plot_histogram
5
+ from .compiler import transpile
6
+
7
+ __version__ = "0.1.0"
@@ -0,0 +1,2 @@
1
+ from .quantum_circuit import QuantumCircuit
2
+ from .register import QuantumRegister, ClassicalRegister
@@ -0,0 +1,218 @@
1
+ """
2
+ QuantumCircuit class mimicking Qiskit's API, delegating to qsys.
3
+ """
4
+ from typing import Union, List, Optional
5
+ from .register import QuantumRegister, ClassicalRegister
6
+
7
+ # Import the core composer from existing codebase
8
+ # We treat qsys as the internal engine
9
+ try:
10
+ from qsys.circuit.quantum_circuit import QuantumCircuit as QSysCircuit
11
+ except ImportError as e:
12
+ # Fallback/Mock for development if qsys is not in path
13
+ print(f"DEBUG: Failed to import qsys: {e}")
14
+ QSysCircuit = None
15
+
16
+ class QuantumCircuit:
17
+ """
18
+ QuantumCircuit adaptable to Zena SDK.
19
+ """
20
+ def __init__(self, *regs, name: Optional[str] = None):
21
+ self.name = name
22
+ self.qregs = []
23
+ self.cregs = []
24
+ self._qubits = []
25
+ self._clbits = []
26
+
27
+ # Parse arguments similar to Qiskit (int or Register)
28
+ if len(regs) == 0:
29
+ # Empty circuit
30
+ pass
31
+ elif all(isinstance(r, int) for r in regs):
32
+ # Case: QuantumCircuit(n_qubits, n_clbits)
33
+ n_qubits = regs[0]
34
+ n_clbits = regs[1] if len(regs) > 1 else 0
35
+ self.add_register(QuantumRegister(n_qubits, 'q'))
36
+ if n_clbits > 0:
37
+ self.add_register(ClassicalRegister(n_clbits, 'c'))
38
+ else:
39
+ # Case: QuantumCircuit(qreg, creg)
40
+ for r in regs:
41
+ if isinstance(r, (QuantumRegister, ClassicalRegister)):
42
+ self.add_register(r)
43
+ else:
44
+ raise TypeError("Arguments must be integers or Registers")
45
+
46
+ # Initialize the underlying qsys Core Circuit
47
+ if QSysCircuit:
48
+ self._core_circuit = QSysCircuit(len(self._qubits), len(self._clbits))
49
+ else:
50
+ self._core_circuit = None
51
+ print("Warning: qsys.circuit not found. Core delegation disabled.")
52
+
53
+ def add_register(self, register: Union[QuantumRegister, ClassicalRegister]):
54
+ if isinstance(register, QuantumRegister):
55
+ self.qregs.append(register)
56
+ # Flatten qubits mapping (simplistic for now)
57
+ start_index = len(self._qubits)
58
+ self._qubits.extend([i for i in range(start_index, start_index + register.size)])
59
+ elif isinstance(register, ClassicalRegister):
60
+ self.cregs.append(register)
61
+ start_index = len(self._clbits)
62
+ self._clbits.extend([i for i in range(start_index, start_index + register.size)])
63
+
64
+ def h(self, qubit):
65
+ """Apply Hadamard gate."""
66
+ q_idx = self._resolve_index(qubit)
67
+ if self._core_circuit:
68
+ self._core_circuit.h(q_idx)
69
+ return self
70
+
71
+ def x(self, qubit):
72
+ """Apply Pauli-X gate."""
73
+ q_idx = self._resolve_index(qubit)
74
+ if self._core_circuit:
75
+ self._core_circuit.x(q_idx)
76
+ return self
77
+
78
+ def y(self, qubit):
79
+ """Apply Pauli-Y gate."""
80
+ q_idx = self._resolve_index(qubit)
81
+ if self._core_circuit:
82
+ # Note: qsys uses string name 'y' for Instruction, which execute.py handles
83
+ from qsys.ir.types import Instruction
84
+ self._core_circuit._instructions.append(Instruction(name="y", qubits=(q_idx,)))
85
+ return self
86
+
87
+ def z(self, qubit):
88
+ """Apply Pauli-Z gate."""
89
+ q_idx = self._resolve_index(qubit)
90
+ if self._core_circuit:
91
+ from qsys.ir.types import Instruction
92
+ self._core_circuit._instructions.append(Instruction(name="z", qubits=(q_idx,)))
93
+ return self
94
+
95
+ def s(self, qubit):
96
+ """Apply S gate."""
97
+ q_idx = self._resolve_index(qubit)
98
+ if self._core_circuit:
99
+ from qsys.ir.types import Instruction
100
+ self._core_circuit._instructions.append(Instruction(name="s", qubits=(q_idx,)))
101
+ return self
102
+
103
+ def sdg(self, qubit):
104
+ """Apply S-dagger gate."""
105
+ q_idx = self._resolve_index(qubit)
106
+ if self._core_circuit:
107
+ from qsys.ir.types import Instruction
108
+ self._core_circuit._instructions.append(Instruction(name="sdg", qubits=(q_idx,)))
109
+ return self
110
+
111
+ def t(self, qubit):
112
+ """Apply T gate."""
113
+ q_idx = self._resolve_index(qubit)
114
+ if self._core_circuit:
115
+ from qsys.ir.types import Instruction
116
+ self._core_circuit._instructions.append(Instruction(name="t", qubits=(q_idx,)))
117
+ return self
118
+
119
+ def tdg(self, qubit):
120
+ """Apply T-dagger gate."""
121
+ q_idx = self._resolve_index(qubit)
122
+ if self._core_circuit:
123
+ from qsys.ir.types import Instruction
124
+ self._core_circuit._instructions.append(Instruction(name="tdg", qubits=(q_idx,)))
125
+ return self
126
+
127
+ def sx(self, qubit):
128
+ """Apply sqrt-X gate."""
129
+ q_idx = self._resolve_index(qubit)
130
+ if self._core_circuit:
131
+ self._core_circuit.sx(q_idx)
132
+ return self
133
+
134
+ def rx(self, theta, qubit):
135
+ """Apply RX gate."""
136
+ q_idx = self._resolve_index(qubit)
137
+ if self._core_circuit:
138
+ from qsys.ir.types import Instruction
139
+ self._core_circuit._instructions.append(
140
+ Instruction(name="rx", qubits=(q_idx,), params=(float(theta),))
141
+ )
142
+ return self
143
+
144
+ def ry(self, theta, qubit):
145
+ """Apply RY gate."""
146
+ q_idx = self._resolve_index(qubit)
147
+ if self._core_circuit:
148
+ from qsys.ir.types import Instruction
149
+ self._core_circuit._instructions.append(
150
+ Instruction(name="ry", qubits=(q_idx,), params=(float(theta),))
151
+ )
152
+ return self
153
+
154
+ def rz(self, theta, qubit):
155
+ """Apply RZ gate."""
156
+ q_idx = self._resolve_index(qubit)
157
+ if self._core_circuit:
158
+ self._core_circuit.rz(q_idx, theta)
159
+ return self
160
+
161
+ def cx(self, control_qubit, target_qubit):
162
+ """Apply CNOT gate."""
163
+ c_idx = self._resolve_index(control_qubit)
164
+ t_idx = self._resolve_index(target_qubit)
165
+ if self._core_circuit:
166
+ self._core_circuit.cx(c_idx, t_idx)
167
+ return self
168
+
169
+ def swap(self, qubit1, qubit2):
170
+ """Apply SWAP gate."""
171
+ q1_idx = self._resolve_index(qubit1)
172
+ q2_idx = self._resolve_index(qubit2)
173
+ if self._core_circuit:
174
+ from qsys.ir.types import Instruction
175
+ self._core_circuit._instructions.append(Instruction(name="swap", qubits=(q1_idx, q2_idx)))
176
+ return self
177
+
178
+ def measure(self, qubits, clbits):
179
+ """Measure quantum bits into classical bits."""
180
+ if isinstance(qubits, int):
181
+ qubits = [qubits]
182
+ if isinstance(clbits, int):
183
+ clbits = [clbits]
184
+
185
+ if len(qubits) != len(clbits):
186
+ raise ValueError("Number of qubits and classical bits must match.")
187
+
188
+ for q, c in zip(qubits, clbits):
189
+ q_idx = self._resolve_index(q)
190
+ c_idx = self._resolve_cbit_index(c)
191
+ if self._core_circuit:
192
+ self._core_circuit.measure(q_idx, c_idx)
193
+ return self
194
+
195
+ def draw(self, output='text'):
196
+ """Draw the circuit."""
197
+ if self._core_circuit:
198
+ return self._core_circuit.draw(mode=output)
199
+ return "Core circuit not initialized."
200
+
201
+ def _resolve_index(self, qubit) -> int:
202
+ """
203
+ Resolve user input (int or bit object) to flat integer index for qsys.
204
+ For now, we assume user passes integers as indices.
205
+ """
206
+ # TODO: Support Qubit object (e.g. qreg[0])
207
+ if isinstance(qubit, int):
208
+ if qubit < 0 or qubit >= len(self._qubits):
209
+ raise IndexError(f"Qubit index {qubit} out of range.")
210
+ return qubit
211
+ raise TypeError("Qubit must be an integer index for now.")
212
+
213
+ def _resolve_cbit_index(self, cbit) -> int:
214
+ if isinstance(cbit, int):
215
+ if cbit < 0 or cbit >= len(self._clbits):
216
+ raise IndexError(f"Clbit index {cbit} out of range.")
217
+ return cbit
218
+ raise TypeError("Clbit must be an integer index for now.")
@@ -0,0 +1,28 @@
1
+ """
2
+ Quantum and Classical Registers.
3
+ """
4
+ from typing import Optional
5
+
6
+ class Register:
7
+ """Base class for registers."""
8
+ def __init__(self, size: int, name: Optional[str] = None):
9
+ if size <= 0:
10
+ raise ValueError("Register size must be positive")
11
+ self.size = size
12
+ self.name = name
13
+
14
+ def __len__(self):
15
+ return self.size
16
+
17
+ def __repr__(self):
18
+ return f"{self.__class__.__name__}({self.size}, '{self.name}')"
19
+
20
+ class QuantumRegister(Register):
21
+ """Implement a quantum register."""
22
+ def __init__(self, size: int, name: Optional[str] = "q"):
23
+ super().__init__(size, name)
24
+
25
+ class ClassicalRegister(Register):
26
+ """Implement a classical register."""
27
+ def __init__(self, size: int, name: Optional[str] = "c"):
28
+ super().__init__(size, name)
@@ -0,0 +1,72 @@
1
+ """
2
+ Zena Compiler module.
3
+ """
4
+ from typing import Union, List, Optional
5
+ from ..circuit import QuantumCircuit
6
+ from ..providers.backend import Backend
7
+
8
+ # Import qsys transpiler
9
+ try:
10
+ from qsys.transpiler.passes import transpile as qsys_transpile
11
+ except ImportError:
12
+ qsys_transpile = None
13
+
14
+ def transpile(circuits: Union[QuantumCircuit, List[QuantumCircuit]],
15
+ backend: Optional[Backend] = None,
16
+ **kwargs):
17
+ """
18
+ Transpile the circuit(s) for a given backend.
19
+
20
+ Args:
21
+ circuits: Circuit or list of circuits to transpile.
22
+ backend: Backend to target (optional).
23
+
24
+ Returns:
25
+ The transpiled circuit(s).
26
+ """
27
+ if not qsys_transpile:
28
+ print("Warning: qsys transpiler not found. Returning circuit(s) as is.")
29
+ return circuits
30
+
31
+ is_list = isinstance(circuits, list)
32
+ if not is_list:
33
+ circuits = [circuits]
34
+
35
+ transpiled_circuits = []
36
+ for qc in circuits:
37
+ if not hasattr(qc, "_core_circuit") or not qc._core_circuit:
38
+ transpiled_circuits.append(qc)
39
+ continue
40
+
41
+ # Call qsys transpiler on the internal core circuit
42
+ # qsys.transpile expects (core_circuit, backend)
43
+ # Note: In Phase 2, we passed use_transpiler=False because our Zena Backends
44
+ # didn't have .target. To make this work, we'll need to pass the wrapped qsys backend
45
+ # if available, or a default one.
46
+
47
+ # For now, let's just use the opt1q if no backend is provided
48
+ from qsys.transpiler.opt1q import optimize_1q
49
+
50
+ core_qc = qc._core_circuit
51
+
52
+ if backend:
53
+ # If backend is provided, we try to use the full qsys transpile
54
+ # We need to extract the underlying qsys backend from Zena backend if possible
55
+ # Or just pass the Zena backend if it has the required properties (target, etc.)
56
+ try:
57
+ # We expect qsys.transpile to handle cases where backend might be None or simplified
58
+ # but if it needs .target, we might need a fallback.
59
+ transpiled_core = qsys_transpile(core_qc, backend, **kwargs)
60
+ except Exception as e:
61
+ print(f"Warning: full transpile failed ({e}). Falling back to 1q optimization.")
62
+ transpiled_core = optimize_1q(core_qc)
63
+ else:
64
+ # Default optimization if no backend
65
+ transpiled_core = optimize_1q(core_qc)
66
+
67
+ # Update the Zena circuit with the transpiled core
68
+ # In a real SDK, we'd probably return a NEW Zena circuit
69
+ qc._core_circuit = transpiled_core
70
+ transpiled_circuits.append(qc)
71
+
72
+ return transpiled_circuits[0] if not is_list else transpiled_circuits
@@ -0,0 +1,35 @@
1
+ """
2
+ Execute function.
3
+ """
4
+ from .providers.aer import Aer
5
+ from .compiler import transpile
6
+
7
+ def execute(experiments, backend=None, shots=1024, **kwargs):
8
+ """
9
+ Execute a list of circuits or a single circuit on a backend.
10
+
11
+ Args:
12
+ experiments (QuantumCircuit or list): Circuit(s) to execute.
13
+ backend (Backend): The backend to execute on.
14
+ shots (int): Number of shots (default: 1024).
15
+
16
+ Returns:
17
+ Job: A job object containing the result.
18
+ """
19
+ if backend is None:
20
+ # Default to statevector simulator if none provided
21
+ backend = Aer.get_backend("statevector_simulator")
22
+
23
+ # Transpile the circuits before execution
24
+ experiments = transpile(experiments, backend=backend)
25
+
26
+ if not isinstance(experiments, list):
27
+ experiments = [experiments]
28
+
29
+ # For now, we only support single circuit execution in this simplified wrapper
30
+ # In full implementation, we would loop and aggregate results
31
+ if len(experiments) > 1:
32
+ raise NotImplementedError("Batch execution not yet fully supported in this simplified wrapper.")
33
+
34
+ circuit = experiments[0]
35
+ return backend.run(circuit, shots=shots, **kwargs)
@@ -0,0 +1,5 @@
1
+ """
2
+ Zena Providers module.
3
+ """
4
+ class Aer:
5
+ pass
@@ -0,0 +1,71 @@
1
+ """
2
+ Aer Provider for local simulation.
3
+ """
4
+ from .backend import Backend
5
+ from .job import Job, Result
6
+ import uuid
7
+
8
+ # Import the internal execution logic from qsys
9
+ try:
10
+ from qsys.runtime.execute import execute as qsys_execute
11
+ except ImportError:
12
+ qsys_execute = None
13
+
14
+ class StatevectorSimulator(Backend):
15
+ """Local statevector simulator backend."""
16
+
17
+ @property
18
+ def name(self):
19
+ return "statevector_simulator"
20
+
21
+ @property
22
+ def target(self):
23
+ """Return a default target for the simulator."""
24
+ try:
25
+ from qsys.backends.base import Target
26
+ # Simulator typically has all-to-all connectivity and standard basis
27
+ return Target(
28
+ n_qubits=32, # Theoretical limit for 8GB RAM approx
29
+ basis_gates={"x", "sx", "rz", "cx", "measure", "h", "y", "z", "s", "sdg", "t", "tdg", "rx", "ry", "swap"},
30
+ coupling_map=[] # Empty list often means all-to-all in these engines, or we can generate it
31
+ )
32
+ except ImportError:
33
+ return None
34
+
35
+ def run(self, circuit, shots=1024, **kwargs):
36
+ """Run the circuit using qsys."""
37
+ if not qsys_execute:
38
+ raise ImportError("qsys.runtime.execute not available. Cannot run simulation.")
39
+
40
+ # Extract the core qsys circuit
41
+ if not hasattr(circuit, "_core_circuit") or not circuit._core_circuit:
42
+ raise ValueError("Circuit has no underlying qsys circuit.")
43
+
44
+ core_qc = circuit._core_circuit
45
+
46
+ # Execute using qsys
47
+ # qsys.execute returns a dict with 'counts', 'statevector', etc.
48
+ # We disable the internal qsys transpiler for now to avoid 'backend.target' errors
49
+ # since we are not yet passing a formal qsys.Backend object.
50
+ res_data = qsys_execute(core_qc, shots=shots, use_transpiler=False)
51
+
52
+ # Wrap in Result and Job
53
+ result = Result(res_data)
54
+ job_id = str(uuid.uuid4())
55
+ return Job(self, job_id, result)
56
+
57
+ class AerProvider:
58
+ """Provider for Aer backends."""
59
+
60
+ def __init__(self):
61
+ self._backends = {
62
+ "statevector_simulator": StatevectorSimulator()
63
+ }
64
+
65
+ def get_backend(self, name="statevector_simulator"):
66
+ if name not in self._backends:
67
+ raise ValueError(f"Backend '{name}' not found.")
68
+ return self._backends[name]
69
+
70
+ # Global instance
71
+ Aer = AerProvider()
@@ -0,0 +1,18 @@
1
+ """
2
+ Base Backend Class.
3
+ """
4
+ from abc import ABC, abstractmethod
5
+
6
+ class Backend(ABC):
7
+ """Abstract base class for backends."""
8
+
9
+ @abstractmethod
10
+ def run(self, circuit, shots=1024, **kwargs):
11
+ """Run a circuit on the backend."""
12
+ pass
13
+
14
+ @property
15
+ @abstractmethod
16
+ def name(self):
17
+ """Backend name."""
18
+ pass
@@ -0,0 +1,24 @@
1
+ """
2
+ Job Class to handle execution status and results.
3
+ """
4
+ class Job:
5
+ def __init__(self, backend, job_id, result=None):
6
+ self._backend = backend
7
+ self._job_id = job_id
8
+ self._result = result
9
+
10
+ def result(self):
11
+ """Return the result of the job."""
12
+ return self._result
13
+
14
+ class Result:
15
+ def __init__(self, backend_result_dict):
16
+ self._data = backend_result_dict
17
+
18
+ def get_counts(self):
19
+ """Return counts from the result."""
20
+ return self._data.get("counts", {})
21
+
22
+ def get_statevector(self):
23
+ """Return statevector from the result."""
24
+ return self._data.get("statevector", [])
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "zena-sdk"
7
+ version = "0.1.0"
8
+ description = "A high-level Python SDK for the Zena Quantum Simulator, inspired by Qiskit."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [
13
+ { name = "Zena Team", email = "info@zena.quantum" },
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: Apache Software License",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Scientific/Engineering :: Physics",
22
+ ]
23
+ dependencies = [
24
+ "numpy>=1.20",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "pytest",
30
+ ]
31
+
32
+ [tool.hatch.build.targets.wheel]
33
+ packages = ["zena"]
@@ -0,0 +1,28 @@
1
+ def plot_histogram(data, title=None):
2
+ """
3
+ Plot a histogram of counts.
4
+
5
+ Args:
6
+ data (dict): Counts dictionary (e.g., {'00': 500, '11': 500})
7
+ title (str): Optional title for the plot.
8
+ """
9
+ if title:
10
+ print(f"\n--- {title} ---")
11
+ else:
12
+ print("\n--- Histogram ---")
13
+
14
+ if not data:
15
+ print("Empty data.")
16
+ return
17
+
18
+ max_val = max(data.values())
19
+ total = sum(data.values())
20
+
21
+ # Sort keys for consistent display
22
+ for key in sorted(data.keys()):
23
+ val = data[key]
24
+ percentage = (val / total) * 100
25
+ bar_length = int((val / max_val) * 20)
26
+ bar = "█" * bar_length
27
+ print(f"{key:5s} | {bar:<20} | {val:4d} ({percentage:>5.1f}%)")
28
+ print("-" * 45)