qumat 0.0.1__py3-none-any.whl → 0.5.0rc1__py3-none-any.whl

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.
qumat/qumat.py CHANGED
@@ -16,75 +16,549 @@
16
16
  #
17
17
  from importlib import import_module
18
18
 
19
+
19
20
  class QuMat:
21
+ """Unified interface for quantum circuit operations across multiple backends.
22
+
23
+ Provides a consistent API for creating and manipulating quantum circuits
24
+ using different quantum computing backends (Qiskit, Cirq, Amazon Braket).
25
+ Abstracts backend-specific details for gate operations, circuit execution,
26
+ and state measurement.
27
+
28
+ :param backend_config: Configuration dictionary for the quantum backend.
29
+ Must contain ``backend_name`` (str) and ``backend_options`` (dict).
30
+ The ``backend_options`` should include ``simulator_type`` and ``shots``.
31
+ :type backend_config: dict
32
+ """
33
+
20
34
  def __init__(self, backend_config):
35
+ """Create a QuMat instance with the specified backend configuration.
36
+
37
+ :param backend_config: Configuration dictionary containing backend name
38
+ and options. Required keys:
39
+ - ``backend_name``: Name of the backend (e.g., "qiskit", "cirq", "amazon_braket")
40
+ - ``backend_options``: Dictionary with backend-specific options
41
+ :type backend_config: dict
42
+ :raises ImportError: If the specified backend module cannot be imported.
43
+ :raises ValueError: If backend_config is not a dictionary.
44
+ :raises KeyError: If required configuration keys are missing.
45
+ """
46
+ if not isinstance(backend_config, dict):
47
+ raise ValueError(
48
+ f"backend_config must be a dictionary, got {type(backend_config).__name__}"
49
+ )
50
+
51
+ if "backend_name" not in backend_config:
52
+ raise KeyError(
53
+ "backend_config is missing required key 'backend_name'. "
54
+ "Please provide a backend name (e.g., 'qiskit', 'cirq', 'amazon_braket')"
55
+ )
56
+
57
+ if "backend_options" not in backend_config:
58
+ raise KeyError(
59
+ "backend_config is missing required key 'backend_options'. "
60
+ "Please provide a dictionary with backend-specific options "
61
+ )
62
+
21
63
  self.backend_config = backend_config
22
- self.backend_name = backend_config['backend_name']
23
- self.backend_module = import_module(f".{self.backend_name}_backend", package="qumat")
64
+ self.backend_name = backend_config["backend_name"]
65
+ self.backend_module = import_module(
66
+ f".{self.backend_name}_backend", package="qumat"
67
+ )
24
68
  self.backend = self.backend_module.initialize_backend(backend_config)
25
69
  self.circuit = None
70
+ self.num_qubits = None
26
71
  self.parameters = {}
27
72
 
28
- def create_empty_circuit(self, num_qubits):
73
+ def create_empty_circuit(self, num_qubits: int | None = None):
74
+ """Create an empty quantum circuit with the specified number of qubits.
75
+
76
+ Must be called before applying any gates or executing operations.
77
+
78
+ :param num_qubits: Number of qubits in the circuit. If ``None``,
79
+ creates a circuit without pre-allocated qubits.
80
+ :type num_qubits: int | None, optional
81
+ """
82
+ self.num_qubits = num_qubits
29
83
  self.circuit = self.backend_module.create_empty_circuit(num_qubits)
30
84
 
85
+ def _ensure_circuit_initialized(self):
86
+ """Ensure the circuit has been created before operations.
87
+
88
+ Checks if the circuit has been initialized via ``create_empty_circuit()``.
89
+ Raises ``RuntimeError`` if not initialized.
90
+
91
+ :raises RuntimeError: If the circuit has not been initialized.
92
+ """
93
+ if self.circuit is None:
94
+ raise RuntimeError(
95
+ "circuit not initialized. call create_empty_circuit(num_qubits) "
96
+ "before applying gates or executing operations."
97
+ )
98
+
99
+ def _validate_qubit_index(self, qubit_index, param_name="qubit_index"):
100
+ """validate qubit index is within circuit bounds.
101
+
102
+ :param qubit_index: the qubit index to validate.
103
+ :type qubit_index: int
104
+ :param param_name: name of the parameter for error messages.
105
+ :type param_name: str
106
+ :raises TypeError: if qubit_index is not an integer.
107
+ :raises ValueError: if qubit_index is negative or out of range.
108
+ """
109
+ if not isinstance(qubit_index, int):
110
+ raise TypeError(
111
+ f"{param_name} must be an integer, got {type(qubit_index).__name__}"
112
+ )
113
+ if qubit_index < 0:
114
+ raise ValueError(f"{param_name} cannot be negative, got {qubit_index}")
115
+ if self.num_qubits is not None and qubit_index >= self.num_qubits:
116
+ raise ValueError(
117
+ f"{param_name} {qubit_index} out of range for circuit with "
118
+ f"{self.num_qubits} qubits (valid range: 0-{self.num_qubits - 1})"
119
+ )
120
+
31
121
  def apply_not_gate(self, qubit_index):
122
+ """Apply a NOT gate (Pauli-X gate) to the specified qubit.
123
+
124
+ Flips the qubit state from |0⟩ to |1⟩ or |1⟩ to |0⟩.
125
+ Equivalent to the Pauli-X gate.
126
+
127
+ :param qubit_index: Index of the qubit.
128
+ :type qubit_index: int
129
+ :raises RuntimeError: If the circuit has not been initialized.
130
+ """
131
+ self._ensure_circuit_initialized()
132
+ self._validate_qubit_index(qubit_index)
32
133
  self.backend_module.apply_not_gate(self.circuit, qubit_index)
33
134
 
34
135
  def apply_hadamard_gate(self, qubit_index):
136
+ """Apply a Hadamard gate to the specified qubit.
137
+
138
+ Creates a superposition state, transforming |0⟩ to (|0⟩ + |1⟩)/√2
139
+ and |1⟩ to (|0⟩ - |1⟩)/√2.
140
+
141
+ :param qubit_index: Index of the qubit.
142
+ :type qubit_index: int
143
+ :raises RuntimeError: If the circuit has not been initialized.
144
+ """
145
+ self._ensure_circuit_initialized()
146
+ self._validate_qubit_index(qubit_index)
35
147
  self.backend_module.apply_hadamard_gate(self.circuit, qubit_index)
36
148
 
37
149
  def apply_cnot_gate(self, control_qubit_index, target_qubit_index):
38
- self.backend_module.apply_cnot_gate(self.circuit, control_qubit_index, target_qubit_index)
150
+ """Apply a Controlled-NOT (CNOT) gate between two qubits.
151
+
152
+ Fundamental for entangling qubits. Flips the target qubit if and only
153
+ if the control qubit is in the |1⟩ state.
39
154
 
40
- def apply_toffoli_gate(self, control_qubit_index1, control_qubit_index2, target_qubit_index):
41
- self.backend_module.apply_toffoli_gate(self.circuit, control_qubit_index1, control_qubit_index2, target_qubit_index)
155
+ :param control_qubit_index: Index of the control qubit.
156
+ :type control_qubit_index: int
157
+ :param target_qubit_index: Index of the target qubit.
158
+ :type target_qubit_index: int
159
+ :raises RuntimeError: If the circuit has not been initialized.
160
+ """
161
+ self._ensure_circuit_initialized()
162
+ self._validate_qubit_index(control_qubit_index, "control_qubit_index")
163
+ self._validate_qubit_index(target_qubit_index, "target_qubit_index")
164
+ self.backend_module.apply_cnot_gate(
165
+ self.circuit, control_qubit_index, target_qubit_index
166
+ )
167
+
168
+ def apply_toffoli_gate(
169
+ self, control_qubit_index1, control_qubit_index2, target_qubit_index
170
+ ):
171
+ """Apply a Toffoli gate (CCX gate) to three qubits.
172
+
173
+ Acts as a quantum AND gate. Flips the target qubit if and only if
174
+ both control qubits are in the |1⟩ state.
175
+
176
+ :param control_qubit_index1: Index of the first control qubit.
177
+ :type control_qubit_index1: int
178
+ :param control_qubit_index2: Index of the second control qubit.
179
+ :type control_qubit_index2: int
180
+ :param target_qubit_index: Index of the target qubit.
181
+ :type target_qubit_index: int
182
+ :raises RuntimeError: If the circuit has not been initialized.
183
+ """
184
+ self._ensure_circuit_initialized()
185
+ self._validate_qubit_index(control_qubit_index1, "control_qubit_index1")
186
+ self._validate_qubit_index(control_qubit_index2, "control_qubit_index2")
187
+ self._validate_qubit_index(target_qubit_index, "target_qubit_index")
188
+ self.backend_module.apply_toffoli_gate(
189
+ self.circuit, control_qubit_index1, control_qubit_index2, target_qubit_index
190
+ )
42
191
 
43
192
  def apply_swap_gate(self, qubit_index1, qubit_index2):
193
+ """Swap the states of two qubits.
194
+
195
+ :param qubit_index1: Index of the first qubit.
196
+ :type qubit_index1: int
197
+ :param qubit_index2: Index of the second qubit.
198
+ :type qubit_index2: int
199
+ :raises RuntimeError: If the circuit has not been initialized.
200
+ """
201
+ self._ensure_circuit_initialized()
202
+ self._validate_qubit_index(qubit_index1, "qubit_index1")
203
+ self._validate_qubit_index(qubit_index2, "qubit_index2")
44
204
  self.backend_module.apply_swap_gate(self.circuit, qubit_index1, qubit_index2)
45
205
 
206
+ def apply_cswap_gate(
207
+ self, control_qubit_index, target_qubit_index1, target_qubit_index2
208
+ ):
209
+ """Apply a controlled-SWAP (Fredkin) gate.
210
+
211
+ Swaps the states of two target qubits if and only if the control
212
+ qubit is in the |1⟩ state.
213
+
214
+ :param control_qubit_index: Index of the control qubit.
215
+ :type control_qubit_index: int
216
+ :param target_qubit_index1: Index of the first target qubit.
217
+ :type target_qubit_index1: int
218
+ :param target_qubit_index2: Index of the second target qubit.
219
+ :type target_qubit_index2: int
220
+ :raises RuntimeError: If the circuit has not been initialized.
221
+ """
222
+ self._ensure_circuit_initialized()
223
+ self._validate_qubit_index(control_qubit_index, "control_qubit_index")
224
+ self._validate_qubit_index(target_qubit_index1, "target_qubit_index1")
225
+ self._validate_qubit_index(target_qubit_index2, "target_qubit_index2")
226
+ self.backend_module.apply_cswap_gate(
227
+ self.circuit, control_qubit_index, target_qubit_index1, target_qubit_index2
228
+ )
229
+
46
230
  def apply_pauli_x_gate(self, qubit_index):
231
+ """Apply a Pauli-X gate to the specified qubit.
232
+
233
+ Equivalent to the NOT gate. Flips the qubit state from |0⟩ to |1⟩
234
+ or |1⟩ to |0⟩.
235
+
236
+ :param qubit_index: Index of the qubit.
237
+ :type qubit_index: int
238
+ :raises RuntimeError: If the circuit has not been initialized.
239
+ """
240
+ self._ensure_circuit_initialized()
241
+ self._validate_qubit_index(qubit_index)
47
242
  self.backend_module.apply_pauli_x_gate(self.circuit, qubit_index)
48
243
 
49
244
  def apply_pauli_y_gate(self, qubit_index):
245
+ """Apply a Pauli-Y gate to the specified qubit.
246
+
247
+ Rotates the qubit around the Y-axis of the Bloch sphere, affecting
248
+ both phase and amplitude.
249
+
250
+ :param qubit_index: Index of the qubit.
251
+ :type qubit_index: int
252
+ :raises RuntimeError: If the circuit has not been initialized.
253
+ """
254
+ self._ensure_circuit_initialized()
255
+ self._validate_qubit_index(qubit_index)
50
256
  self.backend_module.apply_pauli_y_gate(self.circuit, qubit_index)
51
257
 
52
258
  def apply_pauli_z_gate(self, qubit_index):
259
+ """Apply a Pauli-Z gate to the specified qubit.
260
+
261
+ Rotates the qubit around the Z-axis of the Bloch sphere, altering
262
+ the phase without changing the amplitude.
263
+
264
+ :param qubit_index: Index of the qubit.
265
+ :type qubit_index: int
266
+ :raises RuntimeError: If the circuit has not been initialized.
267
+ """
268
+ self._ensure_circuit_initialized()
269
+ self._validate_qubit_index(qubit_index)
53
270
  self.backend_module.apply_pauli_z_gate(self.circuit, qubit_index)
54
271
 
272
+ def apply_t_gate(self, qubit_index):
273
+ """Apply a T-gate (π/8 gate) to the specified qubit.
274
+
275
+ Applies a relative pi/4 phase (multiplies the |1> state by e^{i*pi/4}).
276
+ Essential for universal quantum computation when combined with
277
+ Hadamard and CNOT gates.
278
+
279
+ :param qubit_index: Index of the qubit.
280
+ :type qubit_index: int
281
+ :raises RuntimeError: If the circuit has not been initialized.
282
+ """
283
+ self._ensure_circuit_initialized()
284
+ self._validate_qubit_index(qubit_index)
285
+ self.backend_module.apply_t_gate(self.circuit, qubit_index)
286
+
55
287
  def execute_circuit(self, parameter_values=None):
288
+ """Execute the quantum circuit and return the measurement results.
289
+
290
+ Runs the circuit on the configured backend. For parameterized circuits,
291
+ provide parameter values to bind before execution.
292
+
293
+ :param parameter_values: Dictionary mapping parameter names to numerical
294
+ values. Binds these values to circuit parameters before execution.
295
+ :type parameter_values: dict, optional
296
+ :returns: Measurement results. Format depends on the backend:
297
+ - Qiskit/Braket: Dictionary with state strings as keys and counts as values
298
+ - Cirq: List of dictionaries with integer states as keys
299
+ :rtype: dict | list[dict]
300
+ :raises RuntimeError: If the circuit has not been initialized.
301
+ """
302
+ self._ensure_circuit_initialized()
303
+ if self.num_qubits == 0:
304
+ shots = self.backend_config["backend_options"].get("shots", 1)
305
+ if self.backend_name == "cirq":
306
+ return [{0: shots}]
307
+ else:
308
+ return {"": shots}
309
+
56
310
  if parameter_values:
57
311
  self.bind_parameters(parameter_values)
58
- self.backend_config['parameter_values'] = self.parameters # Pass parameters
59
- return self.backend_module.execute_circuit(self.circuit, self.backend, self.backend_config)
312
+
313
+ # Only pass bound parameters (non-None values) to backend
314
+ bound_parameters = {
315
+ param: value
316
+ for param, value in self.parameters.items()
317
+ if value is not None
318
+ }
319
+
320
+ # Check if there are unbound parameters in the circuit
321
+ if self.parameters and len(bound_parameters) < len(self.parameters):
322
+ unbound_params = [
323
+ p for p in self.parameters.keys() if self.parameters[p] is None
324
+ ]
325
+ raise ValueError(
326
+ f"Circuit contains unbound parameters: {unbound_params}. "
327
+ f"Please provide parameter_values when executing the circuit."
328
+ )
329
+
330
+ self.backend_config["parameter_values"] = bound_parameters
331
+ return self.backend_module.execute_circuit(
332
+ self.circuit, self.backend, self.backend_config
333
+ )
60
334
 
61
335
  def bind_parameters(self, parameter_values):
336
+ """Bind numerical values to circuit parameters.
337
+
338
+ Assigns numerical values to symbolic parameters defined in parameterized
339
+ gates.
340
+
341
+ :param parameter_values: Dictionary mapping parameter names to numerical
342
+ values.
343
+ :type parameter_values: dict
344
+ :raises ValueError: If a parameter name is not found in the circuit's
345
+ parameter list.
346
+ """
62
347
  for param, value in parameter_values.items():
63
- if param in self.parameters:
64
- self.parameters[param] = value
348
+ if param not in self.parameters:
349
+ raise ValueError(
350
+ f"parameter '{param}' not found in circuit. "
351
+ f"available parameters: {list(self.parameters.keys())}"
352
+ )
353
+ self.parameters[param] = value
65
354
 
66
- # placeholder method for use in the testing suite
67
355
  def get_final_state_vector(self):
68
- return self.backend_module.get_final_state_vector(self.circuit, self.backend, self.backend_config)
356
+ """Return the final state vector of the quantum circuit.
69
357
 
70
- def draw(self):
358
+ The complete quantum state vector after circuit execution,
359
+ representing the full quantum state of all qubits.
360
+
361
+ :returns: The final state vector as a numpy array.
362
+ :rtype: numpy.ndarray
363
+ :raises RuntimeError: If the circuit has not been initialized.
364
+ """
365
+ self._ensure_circuit_initialized()
366
+ return self.backend_module.get_final_state_vector(
367
+ self.circuit, self.backend, self.backend_config
368
+ )
369
+
370
+ def draw_circuit(self):
371
+ """Visualize the quantum circuit.
372
+
373
+ Generates a visual representation of the circuit. The output format
374
+ depends on the backend implementation.
375
+
376
+ :returns: Circuit visualization. The exact type depends on the backend.
377
+ :rtype: str | object
378
+ :raises RuntimeError: If the circuit has not been initialized.
379
+ """
380
+ self._ensure_circuit_initialized()
71
381
  return self.backend_module.draw_circuit(self.circuit)
72
382
 
383
+ def draw(self):
384
+ """Alias for draw_circuit() for convenience.
385
+
386
+ Provides a shorter method name that matches common quantum computing
387
+ library conventions and documentation examples.
388
+
389
+ :returns: Circuit visualization. The exact type depends on the backend.
390
+ :rtype: str | object
391
+ :raises RuntimeError: If the circuit has not been initialized.
392
+ """
393
+ return self.draw_circuit()
394
+
73
395
  def apply_rx_gate(self, qubit_index, angle):
396
+ """Apply a rotation around the X-axis to the specified qubit.
397
+
398
+ Rotates the qubit by the given angle around the X-axis of the Bloch
399
+ sphere. The angle can be a static value or a parameter name for
400
+ parameterized circuits.
401
+
402
+ :param qubit_index: Index of the qubit.
403
+ :type qubit_index: int
404
+ :param angle: Rotation angle in radians. Can be a float or a string
405
+ parameter name.
406
+ :type angle: float | str
407
+ :raises RuntimeError: If the circuit has not been initialized.
408
+ """
409
+ self._ensure_circuit_initialized()
410
+ self._validate_qubit_index(qubit_index)
74
411
  self._handle_parameter(angle)
75
412
  self.backend_module.apply_rx_gate(self.circuit, qubit_index, angle)
76
413
 
77
414
  def apply_ry_gate(self, qubit_index, angle):
415
+ """Apply a rotation around the Y-axis to the specified qubit.
416
+
417
+ Rotates the qubit by the given angle around the Y-axis of the Bloch
418
+ sphere. The angle can be a static value or a parameter name for
419
+ parameterized circuits.
420
+
421
+ :param qubit_index: Index of the qubit.
422
+ :type qubit_index: int
423
+ :param angle: Rotation angle in radians. Can be a float or a string
424
+ parameter name.
425
+ :type angle: float | str
426
+ :raises RuntimeError: If the circuit has not been initialized.
427
+ """
428
+ self._ensure_circuit_initialized()
429
+ self._validate_qubit_index(qubit_index)
78
430
  self._handle_parameter(angle)
79
431
  self.backend_module.apply_ry_gate(self.circuit, qubit_index, angle)
80
432
 
81
433
  def apply_rz_gate(self, qubit_index, angle):
434
+ """Apply a rotation around the Z-axis to the specified qubit.
435
+
436
+ Rotates the qubit by the given angle around the Z-axis of the Bloch
437
+ sphere. The angle can be a static value or a parameter name for
438
+ parameterized circuits.
439
+
440
+ :param qubit_index: Index of the qubit.
441
+ :type qubit_index: int
442
+ :param angle: Rotation angle in radians. Can be a float or a string
443
+ parameter name.
444
+ :type angle: float | str
445
+ :raises RuntimeError: If the circuit has not been initialized.
446
+ """
447
+ self._ensure_circuit_initialized()
448
+ self._validate_qubit_index(qubit_index)
82
449
  self._handle_parameter(angle)
83
450
  self.backend_module.apply_rz_gate(self.circuit, qubit_index, angle)
84
451
 
85
452
  def _handle_parameter(self, param_name):
453
+ """Register parameter names when parameterized gates are applied.
454
+
455
+ Automatically adds string parameter names to the parameters dictionary
456
+ if not already registered.
457
+
458
+ :param param_name: Parameter name to handle. If it's a string,
459
+ registers it as a parameter.
460
+ :type param_name: str | float
461
+ """
86
462
  if isinstance(param_name, str) and param_name not in self.parameters:
87
463
  self.parameters[param_name] = None
88
464
 
89
465
  def apply_u_gate(self, qubit_index, theta, phi, lambd):
466
+ """Apply a U gate (universal single-qubit gate) to the specified qubit.
467
+
468
+ A universal single-qubit gate parameterized by three angles (theta,
469
+ phi, lambd) that can represent any single-qubit unitary operation.
470
+
471
+ :param qubit_index: Index of the qubit.
472
+ :type qubit_index: int
473
+ :param theta: First rotation angle in radians.
474
+ :type theta: float
475
+ :param phi: Second rotation angle in radians.
476
+ :type phi: float
477
+ :param lambd: Third rotation angle in radians.
478
+ :type lambd: float
479
+ :raises RuntimeError: If the circuit has not been initialized.
480
+ """
481
+ self._ensure_circuit_initialized()
482
+ self._validate_qubit_index(qubit_index)
90
483
  self.backend_module.apply_u_gate(self.circuit, qubit_index, theta, phi, lambd)
484
+
485
+ def swap_test(self, ancilla_qubit, qubit1, qubit2):
486
+ """Implement the swap test circuit for measuring overlap between two quantum states.
487
+
488
+ Measures the inner product between the states on ``qubit1`` and ``qubit2``.
489
+ The probability of measuring the ancilla qubit in state |0⟩ is related
490
+ to the overlap as: P(0) = (1 + |⟨ψ|φ⟩|²) / 2
491
+
492
+ :param ancilla_qubit: Index of the ancilla qubit (should be initialized to |0⟩).
493
+ :type ancilla_qubit: int
494
+ :param qubit1: Index of the first qubit containing state |ψ⟩.
495
+ :type qubit1: int
496
+ :param qubit2: Index of the second qubit containing state |φ⟩.
497
+ :type qubit2: int
498
+ :raises RuntimeError: If the circuit has not been initialized.
499
+ """
500
+ # Apply Hadamard to ancilla qubit
501
+ self.apply_hadamard_gate(ancilla_qubit)
502
+
503
+ # Apply controlled-SWAP (Fredkin gate) with ancilla as control
504
+ self.apply_cswap_gate(ancilla_qubit, qubit1, qubit2)
505
+
506
+ # Apply Hadamard to ancilla qubit again
507
+ self.apply_hadamard_gate(ancilla_qubit)
508
+
509
+ def measure_overlap(self, qubit1, qubit2, ancilla_qubit=0):
510
+ """Measure the overlap (fidelity) between two quantum states using the swap test.
511
+
512
+ Creates a swap test circuit to calculate the similarity between the
513
+ quantum states on ``qubit1`` and ``qubit2``. Returns the squared overlap
514
+ |⟨ψ|φ⟩|², which represents the fidelity between the two states.
515
+
516
+ The swap test measures P(ancilla=0), related to overlap as:
517
+ P(0) = (1 + |⟨ψ|φ⟩|²) / 2
518
+
519
+ For certain states (especially identical excited states), global phase
520
+ effects may cause the ancilla to measure predominantly |1⟩ instead of |0⟩.
521
+ This method handles both cases by taking the measurement probability
522
+ closer to 1.
523
+
524
+ :param qubit1: Index of the first qubit containing state |ψ⟩.
525
+ :type qubit1: int
526
+ :param qubit2: Index of the second qubit containing state |φ⟩.
527
+ :type qubit2: int
528
+ :param ancilla_qubit: Index of the ancilla qubit. Default is 0. Should be
529
+ initialized to |0⟩.
530
+ :type ancilla_qubit: int, optional
531
+ :returns: The squared overlap |⟨ψ|φ⟩|² between the two states (fidelity),
532
+ clamped to the range [0.0, 1.0].
533
+ :rtype: float
534
+ :raises RuntimeError: If the circuit has not been initialized.
535
+ """
536
+ # Perform the swap test
537
+ self.swap_test(ancilla_qubit, qubit1, qubit2)
538
+ results = self.execute_circuit()
539
+
540
+ # Calculate the probability of measuring ancilla in |0> state
541
+ prob_zero = self.calculate_prob_zero(results, ancilla_qubit)
542
+ prob_zero_or_one = max(prob_zero, 1 - prob_zero)
543
+ overlap_squared = 2 * prob_zero_or_one - 1
544
+ overlap_squared = max(0.0, min(1.0, overlap_squared))
545
+
546
+ return overlap_squared
547
+
548
+ def calculate_prob_zero(self, results, ancilla_qubit):
549
+ """Calculate the probability of measuring the ancilla qubit in |0⟩ state.
550
+
551
+ Delegates to the backend-specific implementation. Different backends
552
+ may use different qubit ordering conventions (little-endian vs big-endian).
553
+
554
+ :param results: Measurement results from ``execute_circuit()``. Format
555
+ depends on the backend.
556
+ :type results: dict | list[dict]
557
+ :param ancilla_qubit: Index of the ancilla qubit.
558
+ :type ancilla_qubit: int
559
+ :returns: Probability of measuring the ancilla qubit in |0⟩ state.
560
+ :rtype: float
561
+ """
562
+ return self.backend_module.calculate_prob_zero(
563
+ results, ancilla_qubit, self.num_qubits
564
+ )
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: qumat
3
+ Version: 0.5.0rc1
4
+ Summary: A library for composing quantum machine learning.
5
+ Author-email: Apache Mahout <dev@mahout.apache.org>
6
+ License-Expression: Apache-2.0
7
+ License-File: LICENSE
8
+ License-File: NOTICE
9
+ Requires-Python: <3.13,>=3.10
10
+ Requires-Dist: amazon-braket-sdk<2.0,>=1.108.0
11
+ Requires-Dist: cirq<1.6.0,>=1.5.0
12
+ Requires-Dist: qiskit-aer<0.18.0,>=0.17.2
13
+ Requires-Dist: qiskit<3.0.0,>=2.2.0
14
+ Requires-Dist: sympy<2.0,>=1.14.0
15
+ Provides-Extra: qdp
16
+ Requires-Dist: qumat-qdp; extra == 'qdp'
17
+ Description-Content-Type: text/markdown
18
+
19
+
20
+ <!--
21
+ Licensed to the Apache Software Foundation (ASF) under one or more
22
+ contributor license agreements. See the NOTICE file distributed with
23
+ this work for additional information regarding copyright ownership.
24
+ The ASF licenses this file to You under the Apache License, Version 2.0
25
+ (the "License"); you may not use this file except in compliance with
26
+ the License. You may obtain a copy of the License at
27
+
28
+ http://www.apache.org/licenses/LICENSE-2.0
29
+
30
+ Unless required by applicable law or agreed to in writing, software
31
+ distributed under the License is distributed on an "AS IS" BASIS,
32
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33
+ See the License for the specific language governing permissions and
34
+ limitations under the License.
35
+ -->
36
+
37
+ # Apache Mahout
38
+
39
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
40
+ [![Python](https://img.shields.io/badge/Python-3.10--3.12-blue.svg)](https://www.python.org/)
41
+ [![GitHub Stars](https://img.shields.io/github/stars/apache/mahout.svg)](https://github.com/apache/mahout/stargazers)
42
+ [![GitHub Contributors](https://img.shields.io/github/contributors/apache/mahout.svg)](https://github.com/apache/mahout/graphs/contributors)
43
+
44
+ The goal of the Apache Mahout™ project is to build an environment for quickly creating scalable, performant machine learning applications.\
45
+ For additional information about Mahout, visit the [Mahout Home Page](http://mahout.apache.org/)
46
+
47
+ ## Qumat
48
+
49
+ <p align="center">
50
+ <img src="docs/assets/mascot_with_text.png" width="400" alt="Apache Mahout">
51
+ </p>
52
+
53
+ Qumat is a high-level Python library for quantum computing that provides:
54
+
55
+ - **Quantum Circuit Abstraction** - Build quantum circuits with standard gates (Hadamard, CNOT, Pauli, etc.) and run them on Qiskit, Cirq, or Amazon Braket with a single unified API. Write once, execute anywhere. Check out [basic gates](docs/basic_gates.md) for a quick introduction to the basic gates supported across all backends.
56
+ - **QDP (Quantum Data Plane)** - Encode classical data into quantum states using GPU-accelerated kernels. Zero-copy tensor transfer via DLPack lets you move data between PyTorch, NumPy, and TensorFlow without overhead.
57
+
58
+ ## Quick Start
59
+
60
+ ```bash
61
+ git clone https://github.com/apache/mahout.git
62
+ cd mahout
63
+ pip install uv
64
+ uv sync # Core Qumat
65
+ uv sync --extra qdp # With QDP (requires CUDA GPU)
66
+ ```
67
+
68
+ ### Qumat: Run a Quantum Circuit
69
+
70
+ ```python
71
+ from qumat import QuMat
72
+
73
+ qumat = QuMat({"backend_name": "qiskit", "backend_options": {"simulator_type": "aer_simulator"}})
74
+ qumat.create_empty_circuit(num_qubits=2)
75
+ qumat.apply_hadamard_gate(0)
76
+ qumat.apply_cnot_gate(0, 1)
77
+ qumat.execute_circuit()
78
+ ```
79
+
80
+ ### QDP: Encode data for Quantum ML
81
+
82
+ ```python
83
+ import qumat.qdp as qdp
84
+
85
+ engine = qdp.QdpEngine(device_id=0)
86
+ qtensor = engine.encode([1.0, 2.0, 3.0, 4.0], num_qubits=2, encoding_method="amplitude")
87
+ ```
88
+
89
+ ## Roadmap
90
+
91
+ ### 2024
92
+ - [x] Transition of Classic to maintenance mode
93
+ - [x] Integration of Qumat with hardened (tests, docs, CI/CD) Cirq, Qiskit, and Braket backends
94
+ - [x] Integration with Amazon Braket
95
+ - [x] [Public talk about Qumat](https://2024.fossy.us/schedule/presentation/265/)
96
+
97
+ ### 2025
98
+ - [x] [FOSDEM talk](https://fosdem.org/2025/schedule/event/fosdem-2025-5298-introducing-qumat-an-apache-mahout-joint-/)
99
+ - [x] QDP: Foundation & Infrastructure (Rust workspace, build configuration)
100
+ - [x] QDP: Core Implementation (CUDA kernels, CPU preprocessing, GPU memory management)
101
+ - [x] QDP: Zero-copy and Safety (DLManagedTensor, DLPack structures)
102
+ - [x] QDP: Python Binding (PyO3 wrapping, DLPack protocol)
103
+
104
+ ### Q1 2026
105
+ - [ ] QDP: Input Format Support (PyTorch, NumPy, TensorFlow integration)
106
+ - [ ] QDP: Verification and Testing (device testing, benchmarking)
107
+ - [ ] QDP: Additional Encoders (angle/basis encoding, multi-GPU optimization)
108
+ - [ ] QDP: Integration & Release (documentation, example notebooks, PyPI publishing)
109
+
110
+ ## Legal
111
+ Please see the `NOTICE.txt` included in this directory for more information.
@@ -0,0 +1,11 @@
1
+ qumat/__init__.py,sha256=HweBfNndHmuHN3X2t0JR9vP9wsfDqkjSzBbEdS0XfF0,854
2
+ qumat/amazon_braket_backend.py,sha256=dDCn4oXsZwQqJdQIMWQD29HiNGECxYulklEiqFZsnPo,5787
3
+ qumat/cirq_backend.py,sha256=jBdNQBi_86Z4cloDV1LXmhXRl7ae3mQbwf30AOjNrfY,6490
4
+ qumat/qdp.py,sha256=gKiQjlrB_oFwm--_LdJKPgO_XvXfhdV6XXtWHonbtFg,2332
5
+ qumat/qiskit_backend.py,sha256=ZjNbu3ToN9FLVL_C_3HLp5tS_wib0up48ibmUiq3VTI,6996
6
+ qumat/qumat.py,sha256=pzpi015z9MwIgrRok1MFJjcKfLGHVBFbzsswJEcdwEo,24308
7
+ qumat-0.5.0rc1.dist-info/METADATA,sha256=0IcBFH6ADR1rjAiFzaMtYFxCFLYVv3snED7k9r7GTtA,4606
8
+ qumat-0.5.0rc1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
9
+ qumat-0.5.0rc1.dist-info/licenses/LICENSE,sha256=PM3t-YVh-GyXESTynXe-orGOAJ9z1xBgh0mbsLdn0Co,12628
10
+ qumat-0.5.0rc1.dist-info/licenses/NOTICE,sha256=TBC-GYUVZqnqxC23u5wXAUvyMwYXx0-H4_NT5ASLmvM,295
11
+ qumat-0.5.0rc1.dist-info/RECORD,,