2
2
Circuit object translation in different packages
3
3
"""
4
4
5
- from typing import Any , Dict , List , Optional , Tuple , Union , Sequence
6
- from copy import deepcopy
7
5
import logging
6
+ from copy import deepcopy
7
+ from typing import Any , Dict , List , Optional , Sequence , Tuple , Union
8
+
8
9
import numpy as np
9
10
10
11
logger = logging .getLogger (__name__ )
11
12
12
13
13
14
try :
15
+ import qiskit .quantum_info as qi
16
+ import symengine
17
+ import sympy
14
18
from qiskit import QuantumCircuit
19
+ from qiskit .circuit import Parameter , ParameterExpression
15
20
from qiskit .circuit .library import XXPlusYYGate
21
+ from qiskit .circuit .parametervector import ParameterVectorElement
22
+ from qiskit .circuit .quantumcircuitdata import CircuitInstruction
16
23
from qiskit .extensions import UnitaryGate
17
- import qiskit .quantum_info as qi
18
24
from qiskit .extensions .exceptions import ExtensionError
19
- from qiskit .circuit .quantumcircuitdata import CircuitInstruction
20
- from qiskit .circuit .parametervector import ParameterVectorElement
21
- from qiskit .circuit import Parameter , ParameterExpression
22
25
except ImportError :
23
26
logger .warning (
24
27
"Please first ``pip install -U qiskit`` to enable related functionality in translation module"
34
37
35
38
from . import gates
36
39
from .circuit import Circuit
37
- from .densitymatrix import DMCircuit2
38
40
from .cons import backend
41
+ from .densitymatrix import DMCircuit2
39
42
from .interfaces .tensortrans import tensor_to_numpy
40
43
41
-
42
44
Tensor = Any
43
45
44
46
@@ -358,7 +360,7 @@ def _translate_qiskit_params(
358
360
gate_info : CircuitInstruction , binding_params : Any
359
361
) -> List [float ]:
360
362
parameters = []
361
- for p in gate_info [ 0 ] .params :
363
+ for p in gate_info . operation .params :
362
364
if isinstance (p , ParameterVectorElement ):
363
365
parameters .append (binding_params [p .index ])
364
366
elif isinstance (p , Parameter ):
@@ -367,30 +369,29 @@ def _translate_qiskit_params(
367
369
if len (p .parameters ) == 0 :
368
370
parameters .append (float (p ))
369
371
continue
370
- if len (p .parameters ) != 1 :
371
- raise ValueError (
372
- f"Can't translate parameter expression with more than 1 parameters: { p } "
373
- )
374
- p_real = list (p .parameters )[0 ]
375
- if not isinstance (p_real , ParameterVectorElement ):
376
- raise TypeError (
377
- "Parameters in parameter expression should be ParameterVectorElement"
378
- )
372
+
379
373
# note "sym" != "sim"
380
374
expr = p .sympify ().simplify ()
381
- # only allow simple expressions like 1.0 * theta
382
- if not expr .is_Mul :
383
- raise ValueError (f"Unsupported parameter expression: { p } " )
384
- arg1 , arg2 = expr .args
385
- if arg1 .is_number and arg2 .is_symbol :
386
- coeff = arg1
387
- elif arg1 .is_symbol and arg2 .is_number :
388
- coeff = arg2
389
- else :
390
- raise ValueError (f"Unsupported parameter expression: { p } " )
391
- # taking real part here because using complex type will result in a type error
392
- # for tf backend when the binding parameter is real
393
- parameters .append (float (coeff ) * binding_params [p_real .index ])
375
+ if isinstance (expr , symengine .Expr ): # qiskit uses symengine if available
376
+ expr = expr ._sympy_ () # sympy.Expr
377
+
378
+ for free_symbol in expr .free_symbols :
379
+ # replace names: theta[0] -> theta_0
380
+ # ParameterVector creates symbols with brackets like theta[0]
381
+ # but sympy.lambdify does not allow brackets in symbol names
382
+ free_symbol .name = free_symbol .name .replace ("[" , "_" ).replace ("]" , "" )
383
+
384
+ parameter_list = list (p .parameters )
385
+ sympy_symbols = [param ._symbol_expr for param in parameter_list ]
386
+ # replace names again: theta[0] -> theta_0
387
+ sympy_symbols = [
388
+ sympy .Symbol (str (symbol ).replace ("[" , "_" ).replace ("]" , "" ))
389
+ for symbol in sympy_symbols
390
+ ]
391
+ lam_f = sympy .lambdify (sympy_symbols , expr , modules = backend .name )
392
+ parameters .append (
393
+ lam_f (* [binding_params [param .index ] for param in parameter_list ])
394
+ )
394
395
else :
395
396
# numbers, arrays, etc.
396
397
parameters .append (p )
@@ -403,7 +404,7 @@ def ctrl_str2ctrl_state(ctrl_str: str, nctrl: int) -> List[int]:
403
404
404
405
405
406
def qiskit2tc (
406
- qcdata : List [ CircuitInstruction ] ,
407
+ qc : QuantumCircuit ,
407
408
n : int ,
408
409
inputs : Optional [List [float ]] = None ,
409
410
is_dm : bool = False ,
@@ -412,19 +413,18 @@ def qiskit2tc(
412
413
binding_params : Optional [Union [Sequence [float ], Dict [Any , float ]]] = None ,
413
414
) -> Any :
414
415
r"""
415
- Generate a tensorcircuit circuit using the quantum circuit data in qiskit .
416
+ Generate a tensorcircuit circuit from the qiskit circuit.
416
417
417
418
:Example:
418
419
419
420
>>> qisc = QuantumCircuit(2)
420
421
>>> qisc.h(0)
421
422
>>> qisc.x(1)
422
- >>> qc = tc.translation.qiskit2tc(qisc.data , 2)
423
+ >>> qc = tc.translation.qiskit2tc(qisc, 2)
423
424
>>> qc.to_qir()[0]['gatef']
424
- h
425
425
426
- :param qcdata: Quantum circuit data from qiskit.
427
- :type qcdata: List[CircuitInstruction]
426
+ :param qc: A quantum circuit in qiskit.
427
+ :type qc: QuantumCircuit
428
428
:param n: # of qubits
429
429
:type n: int
430
430
:param inputs: Input state of the circuit. Default is None.
@@ -435,7 +435,7 @@ def qiskit2tc(
435
435
:type circuit_params: Optional[Dict[str, Any]]
436
436
:param binding_params: (variational) parameters for the circuit.
437
437
Could be either a sequence or dictionary depending on the type of parameters in the Qiskit circuit.
438
- For ``ParameterVectorElement`` use sequence. For ``Parameter`` use dictionary
438
+ For ``ParameterVectorElement`` use sequence. For ``Parameter`` use dictionary.
439
439
:type binding_params: Optional[Union[Sequence[float], Dict[Any, float]]]
440
440
:return: A quantum circuit in tensorcircuit
441
441
:rtype: Any
@@ -451,17 +451,17 @@ def qiskit2tc(
451
451
if "nqubits" not in circuit_params :
452
452
circuit_params ["nqubits" ] = n
453
453
if (
454
- len (qcdata ) > 0
455
- and qcdata [0 ][0 ].name == "initialize"
454
+ len (qc . data ) > 0
455
+ and qc . data [0 ][0 ].name == "initialize"
456
456
and "inputs" not in circuit_params
457
457
):
458
- circuit_params ["inputs" ] = perm_matrix (n ) @ np .array (qcdata [0 ][0 ].params )
458
+ circuit_params ["inputs" ] = perm_matrix (n ) @ np .array (qc . data [0 ][0 ].params )
459
459
if inputs is not None :
460
460
circuit_params ["inputs" ] = inputs
461
461
462
462
tc_circuit : Any = Circ (** circuit_params )
463
- for gate_info in qcdata :
464
- idx = [qb .index for qb in gate_info [ 1 ] ]
463
+ for gate_info in qc . data :
464
+ idx = [qc . find_bit ( qb ) .index for qb in gate_info . qubits ]
465
465
gate_name = gate_info [0 ].name
466
466
parameters = _translate_qiskit_params (gate_info , binding_params )
467
467
if gate_name in [
0 commit comments