-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
quantumcircuit.py
2627 lines (2145 loc) · 110 KB
/
quantumcircuit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
# pylint: disable=bad-docstring-quotes,invalid-name
"""Quantum circuit object."""
import copy
import itertools
import sys
import warnings
import numbers
import multiprocessing as mp
from collections import OrderedDict, defaultdict
from typing import Union
import numpy as np
from qiskit.exceptions import QiskitError
from qiskit.utils.multiprocessing import is_main_process
from qiskit.circuit.instruction import Instruction
from qiskit.circuit.gate import Gate
from qiskit.circuit.parameter import Parameter
from qiskit.qasm.qasm import Qasm
from qiskit.qasm.exceptions import QasmError
from qiskit.circuit.exceptions import CircuitError
from qiskit.utils.deprecation import deprecate_function
from .parameterexpression import ParameterExpression
from .quantumregister import QuantumRegister, Qubit, AncillaRegister, AncillaQubit
from .classicalregister import ClassicalRegister, Clbit
from .parametertable import ParameterTable
from .parametervector import ParameterVector
from .instructionset import InstructionSet
from .register import Register
from .bit import Bit
from .quantumcircuitdata import QuantumCircuitData
from .delay import Delay
try:
import pygments
from pygments.formatters import Terminal256Formatter # pylint: disable=no-name-in-module
from qiskit.qasm.pygments import OpenQASMLexer # pylint: disable=ungrouped-imports
from qiskit.qasm.pygments import QasmTerminalStyle # pylint: disable=ungrouped-imports
HAS_PYGMENTS = True
except Exception: # pylint: disable=broad-except
HAS_PYGMENTS = False
class QuantumCircuit:
"""Create a new circuit.
A circuit is a list of instructions bound to some registers.
Args:
regs (list(:class:`Register`) or list(``int``) or list(list(:class:`Bit`))): The
registers to be included in the circuit.
* If a list of :class:`Register` objects, represents the :class:`QuantumRegister`
and/or :class:`ClassicalRegister` objects to include in the circuit.
For example:
* ``QuantumCircuit(QuantumRegister(4))``
* ``QuantumCircuit(QuantumRegister(4), ClassicalRegister(3))``
* ``QuantumCircuit(QuantumRegister(4, 'qr0'), QuantumRegister(2, 'qr1'))``
* If a list of ``int``, the amount of qubits and/or classical bits to include in
the circuit. It can either be a single int for just the number of quantum bits,
or 2 ints for the number of quantum bits and classical bits, respectively.
For example:
* ``QuantumCircuit(4) # A QuantumCircuit with 4 qubits``
* ``QuantumCircuit(4, 3) # A QuantumCircuit with 4 qubits and 3 classical bits``
* If a list of python lists containing :class:`Bit` objects, a collection of
:class:`Bit` s to be added to the circuit.
name (str): the name of the quantum circuit. If not set, an
automatically generated string will be assigned.
global_phase (float or ParameterExpression): The global phase of the circuit in radians.
metadata (dict): Arbitrary key value metadata to associate with the
circuit. This gets stored as free-form data in a dict in the
:attr:`~qiskit.circuit.QuantumCircuit.metadata` attribute. It will
not be directly used in the circuit.
Raises:
CircuitError: if the circuit name, if given, is not valid.
Examples:
Construct a simple Bell state circuit.
.. jupyter-execute::
from qiskit import QuantumCircuit
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
qc.draw()
Construct a 5-qubit GHZ circuit.
.. jupyter-execute::
from qiskit import QuantumCircuit
qc = QuantumCircuit(5)
qc.h(0)
qc.cx(0, range(1, 5))
qc.measure_all()
Construct a 4-qubit Bernstein-Vazirani circuit using registers.
.. jupyter-execute::
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
qr = QuantumRegister(3, 'q')
anc = QuantumRegister(1, 'ancilla')
cr = ClassicalRegister(3, 'c')
qc = QuantumCircuit(qr, anc, cr)
qc.x(anc[0])
qc.h(anc[0])
qc.h(qr[0:3])
qc.cx(qr[0:3], anc[0])
qc.h(qr[0:3])
qc.barrier(qr)
qc.measure(qr, cr)
qc.draw()
"""
instances = 0
prefix = 'circuit'
# Class variable OPENQASM header
header = "OPENQASM 2.0;"
extension_lib = "include \"qelib1.inc\";"
def __init__(self, *regs, name=None, global_phase=0, metadata=None):
if any(not isinstance(reg, (list, QuantumRegister, ClassicalRegister)) for reg in regs):
# check if inputs are integers, but also allow e.g. 2.0
try:
valid_reg_size = all(reg == int(reg) for reg in regs)
except (ValueError, TypeError):
valid_reg_size = False
if not valid_reg_size:
raise CircuitError("Circuit args must be Registers or integers. (%s '%s' was "
"provided)" % ([type(reg).__name__ for reg in regs], regs))
regs = tuple(int(reg) for reg in regs) # cast to int
if name is None:
name = self.cls_prefix() + str(self.cls_instances())
if sys.platform != "win32" and not is_main_process():
name += '-{}'.format(mp.current_process().pid)
self._increment_instances()
if not isinstance(name, str):
raise CircuitError("The circuit name should be a string "
"(or None to auto-generate a name).")
self.name = name
# Data contains a list of instructions and their contexts,
# in the order they were applied.
self._data = []
# This is a map of registers bound to this circuit, by name.
self.qregs = []
self.cregs = []
self._qubits = []
self._qubit_set = set()
self._clbits = []
self._clbit_set = set()
self._ancillas = []
self._calibrations = defaultdict(dict)
self.add_register(*regs)
# Parameter table tracks instructions with variable parameters.
self._parameter_table = ParameterTable()
self._layout = None
self._global_phase = 0
self.global_phase = global_phase
self.duration = None
self.unit = 'dt'
if not isinstance(metadata, dict) and metadata is not None:
raise TypeError("Only a dictionary or None is accepted for circuit metadata")
self._metadata = metadata
@property
def data(self):
"""Return the circuit data (instructions and context).
Returns:
QuantumCircuitData: a list-like object containing the tuples for the circuit's data.
Each tuple is in the format ``(instruction, qargs, cargs)``, where instruction is an
Instruction (or subclass) object, qargs is a list of Qubit objects, and cargs is a
list of Clbit objects.
"""
return QuantumCircuitData(self)
@property
def calibrations(self):
"""Return calibration dictionary.
The custom pulse definition of a given gate is of the form
{'gate_name': {(qubits, params): schedule}}
"""
return dict(self._calibrations)
@calibrations.setter
def calibrations(self, calibrations):
"""Set the circuit calibration data from a dictionary of calibration definition.
Args:
calibrations (dict): A dictionary of input in the format
{'gate_name': {(qubits, gate_params): schedule}}
"""
self._calibrations = defaultdict(dict, calibrations)
@data.setter
def data(self, data_input):
"""Sets the circuit data from a list of instructions and context.
Args:
data_input (list): A list of instructions with context
in the format (instruction, qargs, cargs), where Instruction
is an Instruction (or subclass) object, qargs is a list of
Qubit objects, and cargs is a list of Clbit objects.
"""
# If data_input is QuantumCircuitData(self), clearing self._data
# below will also empty data_input, so make a shallow copy first.
data_input = data_input.copy()
self._data = []
self._parameter_table = ParameterTable()
for inst, qargs, cargs in data_input:
self.append(inst, qargs, cargs)
@property
def metadata(self):
"""The user provided metadata associated with the circuit
The metadata for the circuit is a user provided ``dict`` of metadata
for the circuit. It will not be used to influence the execution or
operation of the circuit, but it is expected to be passed between
all transforms of the circuit (ie transpilation) and that providers will
associate any circuit metadata with the results it returns from
execution of that circuit.
"""
return self._metadata
@metadata.setter
def metadata(self, metadata):
"""Update the circuit metadata"""
if not isinstance(metadata, dict) and metadata is not None:
raise TypeError("Only a dictionary or None is accepted for circuit metadata")
self._metadata = metadata
def __str__(self):
return str(self.draw(output='text'))
def __eq__(self, other):
if not isinstance(other, QuantumCircuit):
return False
# TODO: remove the DAG from this function
from qiskit.converters import circuit_to_dag
return circuit_to_dag(self) == circuit_to_dag(other)
@classmethod
def _increment_instances(cls):
cls.instances += 1
@classmethod
def cls_instances(cls):
"""Return the current number of instances of this class,
useful for auto naming."""
return cls.instances
@classmethod
def cls_prefix(cls):
"""Return the prefix to use for auto naming."""
return cls.prefix
def has_register(self, register):
"""
Test if this circuit has the register r.
Args:
register (Register): a quantum or classical register.
Returns:
bool: True if the register is contained in this circuit.
"""
has_reg = False
if (isinstance(register, QuantumRegister) and
register in self.qregs):
has_reg = True
elif (isinstance(register, ClassicalRegister) and
register in self.cregs):
has_reg = True
return has_reg
def reverse_ops(self):
"""Reverse the circuit by reversing the order of instructions.
This is done by recursively reversing all instructions.
It does not invert (adjoint) any gate.
Returns:
QuantumCircuit: the reversed circuit.
Examples:
input:
┌───┐
q_0: ┤ H ├─────■──────
└───┘┌────┴─────┐
q_1: ─────┤ RX(1.57) ├
└──────────┘
output:
┌───┐
q_0: ─────■──────┤ H ├
┌────┴─────┐└───┘
q_1: ┤ RX(1.57) ├─────
└──────────┘
"""
reverse_circ = QuantumCircuit(self.qubits, self.clbits,
*self.qregs, *self.cregs,
name=self.name + '_reverse')
for inst, qargs, cargs in reversed(self.data):
reverse_circ._append(inst.reverse_ops(), qargs, cargs)
reverse_circ.duration = self.duration
reverse_circ.unit = self.unit
return reverse_circ
def reverse_bits(self):
"""Return a circuit with the opposite order of wires.
The circuit is "vertically" flipped. If a circuit is
defined over multiple registers, the resulting circuit will have
the same registers but with their order flipped.
This method is useful for converting a circuit written in little-endian
convention to the big-endian equivalent, and vice versa.
Returns:
QuantumCircuit: the circuit with reversed bit order.
Examples:
input:
┌───┐
q_0: ┤ H ├─────■──────
└───┘┌────┴─────┐
q_1: ─────┤ RX(1.57) ├
└──────────┘
output:
┌──────────┐
q_0: ─────┤ RX(1.57) ├
┌───┐└────┬─────┘
q_1: ┤ H ├─────■──────
└───┘
"""
circ = QuantumCircuit(*reversed(self.qregs), *reversed(self.cregs),
name=self.name)
num_qubits = self.num_qubits
num_clbits = self.num_clbits
old_qubits = self.qubits
old_clbits = self.clbits
new_qubits = circ.qubits
new_clbits = circ.clbits
for inst, qargs, cargs in self.data:
new_qargs = [new_qubits[num_qubits - old_qubits.index(q) - 1] for q in qargs]
new_cargs = [new_clbits[num_clbits - old_clbits.index(c) - 1] for c in cargs]
circ._append(inst, new_qargs, new_cargs)
return circ
def inverse(self):
"""Invert (take adjoint of) this circuit.
This is done by recursively inverting all gates.
Returns:
QuantumCircuit: the inverted circuit
Raises:
CircuitError: if the circuit cannot be inverted.
Examples:
input:
┌───┐
q_0: ┤ H ├─────■──────
└───┘┌────┴─────┐
q_1: ─────┤ RX(1.57) ├
└──────────┘
output:
┌───┐
q_0: ──────■──────┤ H ├
┌─────┴─────┐└───┘
q_1: ┤ RX(-1.57) ├─────
└───────────┘
"""
inverse_circ = QuantumCircuit(self.qubits, self.clbits,
*self.qregs, *self.cregs,
name=self.name + '_dg', global_phase=-self.global_phase)
for inst, qargs, cargs in reversed(self._data):
inverse_circ._append(inst.inverse(), qargs, cargs)
return inverse_circ
def repeat(self, reps):
"""Repeat this circuit ``reps`` times.
Args:
reps (int): How often this circuit should be repeated.
Returns:
QuantumCircuit: A circuit containing ``reps`` repetitions of this circuit.
"""
repeated_circ = QuantumCircuit(self.qubits, self.clbits,
*self.qregs, *self.cregs,
name=self.name + '**{}'.format(reps))
# benefit of appending instructions: decomposing shows the subparts, i.e. the power
# is actually `reps` times this circuit, and it is currently much faster than `compose`.
if reps > 0:
try: # try to append as gate if possible to not disallow to_gate
inst = self.to_gate()
except QiskitError:
inst = self.to_instruction()
for _ in range(reps):
repeated_circ._append(inst, self.qubits, self.clbits)
return repeated_circ
def power(self, power, matrix_power=False):
"""Raise this circuit to the power of ``power``.
If ``power`` is a positive integer and ``matrix_power`` is ``False``, this implementation
defaults to calling ``repeat``. Otherwise, if the circuit is unitary, the matrix is
computed to calculate the matrix power.
Args:
power (int): The power to raise this circuit to.
matrix_power (bool): If True, the circuit is converted to a matrix and then the
matrix power is computed. If False, and ``power`` is a positive integer,
the implementation defaults to ``repeat``.
Raises:
CircuitError: If the circuit needs to be converted to a gate but it is not unitary.
Returns:
QuantumCircuit: A circuit implementing this circuit raised to the power of ``power``.
"""
if power >= 0 and isinstance(power, numbers.Integral) and not matrix_power:
return self.repeat(power)
# attempt conversion to gate
if len(self.parameters) > 0:
raise CircuitError('Cannot raise a parameterized circuit to a non-positive power '
'or matrix-power, please bind the free parameters: '
'{}'.format(self.parameters))
try:
gate = self.to_gate()
except QiskitError as ex:
raise CircuitError('The circuit contains non-unitary operations and cannot be '
'controlled. Note that no qiskit.circuit.Instruction objects may '
'be in the circuit for this operation.') from ex
power_circuit = QuantumCircuit(self.qubits, self.clbits, *self.qregs, *self.cregs)
power_circuit.append(gate.power(power), list(range(gate.num_qubits)))
return power_circuit
def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
"""Control this circuit on ``num_ctrl_qubits`` qubits.
Args:
num_ctrl_qubits (int): The number of control qubits.
label (str): An optional label to give the controlled operation for visualization.
ctrl_state (str or int): The control state in decimal or as a bitstring
(e.g. '111'). If None, use ``2**num_ctrl_qubits - 1``.
Returns:
QuantumCircuit: The controlled version of this circuit.
Raises:
CircuitError: If the circuit contains a non-unitary operation and cannot be controlled.
"""
try:
gate = self.to_gate()
except QiskitError as ex:
raise CircuitError('The circuit contains non-unitary operations and cannot be '
'controlled. Note that no qiskit.circuit.Instruction objects may '
'be in the circuit for this operation.') from ex
controlled_gate = gate.control(num_ctrl_qubits, label, ctrl_state)
control_qreg = QuantumRegister(num_ctrl_qubits)
controlled_circ = QuantumCircuit(control_qreg, self.qubits, *self.qregs,
name='c_{}'.format(self.name))
controlled_circ.append(controlled_gate, controlled_circ.qubits)
return controlled_circ
def combine(self, rhs):
"""Append rhs to self if self contains compatible registers.
Two circuits are compatible if they contain the same registers
or if they contain different registers with unique names. The
returned circuit will contain all unique registers between both
circuits.
Return self + rhs as a new object.
Args:
rhs (QuantumCircuit): The quantum circuit to append to the right hand side.
Returns:
QuantumCircuit: Returns a new QuantumCircuit object
Raises:
QiskitError: if the rhs circuit is not compatible
"""
# Check registers in LHS are compatible with RHS
self._check_compatible_regs(rhs)
# Make new circuit with combined registers
combined_qregs = copy.deepcopy(self.qregs)
combined_cregs = copy.deepcopy(self.cregs)
for element in rhs.qregs:
if element not in self.qregs:
combined_qregs.append(element)
for element in rhs.cregs:
if element not in self.cregs:
combined_cregs.append(element)
circuit = QuantumCircuit(*combined_qregs, *combined_cregs)
for instruction_context in itertools.chain(self.data, rhs.data):
circuit._append(*instruction_context)
circuit.global_phase = self.global_phase + rhs.global_phase
for gate, cals in rhs.calibrations.items():
for key, sched in cals.items():
circuit.add_calibration(gate, qubits=key[0], schedule=sched, params=key[1])
return circuit
def extend(self, rhs):
"""Append QuantumCircuit to the right hand side if it contains compatible registers.
Two circuits are compatible if they contain the same registers
or if they contain different registers with unique names. The
returned circuit will contain all unique registers between both
circuits.
Modify and return self.
Args:
rhs (QuantumCircuit): The quantum circuit to append to the right hand side.
Returns:
QuantumCircuit: Returns this QuantumCircuit object (which has been modified)
Raises:
QiskitError: if the rhs circuit is not compatible
"""
# Check registers in LHS are compatible with RHS
self._check_compatible_regs(rhs)
# Add new registers
for element in rhs.qregs:
if element not in self.qregs:
self.qregs.append(element)
self._qubits += element[:]
self._qubit_set.update(element[:])
for element in rhs.cregs:
if element not in self.cregs:
self.cregs.append(element)
self._clbits += element[:]
self._clbit_set.update(element[:])
# Copy the circuit data if rhs and self are the same, otherwise the data of rhs is
# appended to both self and rhs resulting in an infinite loop
data = rhs.data.copy() if rhs is self else rhs.data
# Add new gates
for instruction_context in data:
self._append(*instruction_context)
self.global_phase += rhs.global_phase
for gate, cals in rhs.calibrations.items():
for key, sched in cals.items():
self.add_calibration(gate, qubits=key[0], schedule=sched, params=key[1])
return self
def compose(self, other, qubits=None, clbits=None, front=False, inplace=False):
"""Compose circuit with ``other`` circuit or instruction, optionally permuting wires.
``other`` can be narrower or of equal width to ``self``.
Args:
other (qiskit.circuit.Instruction or QuantumCircuit or BaseOperator):
(sub)circuit to compose onto self.
qubits (list[Qubit|int]): qubits of self to compose onto.
clbits (list[Clbit|int]): clbits of self to compose onto.
front (bool): If True, front composition will be performed (not implemented yet).
inplace (bool): If True, modify the object. Otherwise return composed circuit.
Returns:
QuantumCircuit: the composed circuit (returns None if inplace==True).
Raises:
CircuitError: if composing on the front.
QiskitError: if ``other`` is wider or there are duplicate edge mappings.
Examples::
lhs.compose(rhs, qubits=[3, 2], inplace=True)
.. parsed-literal::
┌───┐ ┌─────┐ ┌───┐
lqr_1_0: ───┤ H ├─── rqr_0: ──■──┤ Tdg ├ lqr_1_0: ───┤ H ├───────────────
├───┤ ┌─┴─┐└─────┘ ├───┤
lqr_1_1: ───┤ X ├─── rqr_1: ┤ X ├─────── lqr_1_1: ───┤ X ├───────────────
┌──┴───┴──┐ └───┘ ┌──┴───┴──┐┌───┐
lqr_1_2: ┤ U1(0.1) ├ + = lqr_1_2: ┤ U1(0.1) ├┤ X ├───────
└─────────┘ └─────────┘└─┬─┘┌─────┐
lqr_2_0: ─────■───── lqr_2_0: ─────■───────■──┤ Tdg ├
┌─┴─┐ ┌─┴─┐ └─────┘
lqr_2_1: ───┤ X ├─── lqr_2_1: ───┤ X ├───────────────
└───┘ └───┘
lcr_0: 0 ═══════════ lcr_0: 0 ═══════════════════════
lcr_1: 0 ═══════════ lcr_1: 0 ═══════════════════════
"""
if inplace:
dest = self
else:
dest = self.copy()
if not isinstance(other, QuantumCircuit):
if front:
dest.data.insert(0, (other, qubits, clbits))
else:
dest.append(other, qargs=qubits, cargs=clbits)
if inplace:
return None
return dest
instrs = other.data
if other.num_qubits > self.num_qubits or \
other.num_clbits > self.num_clbits:
raise CircuitError("Trying to compose with another QuantumCircuit "
"which has more 'in' edges.")
# number of qubits and clbits must match number in circuit or None
identity_qubit_map = dict(zip(other.qubits, self.qubits))
identity_clbit_map = dict(zip(other.clbits, self.clbits))
if qubits is None:
qubit_map = identity_qubit_map
elif len(qubits) != len(other.qubits):
raise CircuitError("Number of items in qubits parameter does not"
" match number of qubits in the circuit.")
else:
qubit_map = {other.qubits[i]: (self.qubits[q] if isinstance(q, int) else q)
for i, q in enumerate(qubits)}
if clbits is None:
clbit_map = identity_clbit_map
elif len(clbits) != len(other.clbits):
raise CircuitError("Number of items in clbits parameter does not"
" match number of clbits in the circuit.")
else:
clbit_map = {other.clbits[i]: (self.clbits[c] if isinstance(c, int) else c)
for i, c in enumerate(clbits)}
edge_map = {**qubit_map, **clbit_map} or {**identity_qubit_map, **identity_clbit_map}
mapped_instrs = []
for instr, qargs, cargs in instrs:
n_qargs = [edge_map[qarg] for qarg in qargs]
n_cargs = [edge_map[carg] for carg in cargs]
n_instr = instr.copy()
if instr.condition is not None:
from qiskit.dagcircuit import DAGCircuit # pylint: disable=cyclic-import
n_instr.condition = DAGCircuit._map_condition(edge_map, instr.condition, self.cregs)
mapped_instrs.append((n_instr, n_qargs, n_cargs))
if front:
dest._data = mapped_instrs + dest._data
else:
dest._data += mapped_instrs
for instr, _, _ in mapped_instrs:
dest._update_parameter_table(instr)
for gate, cals in other.calibrations.items():
dest._calibrations[gate].update(cals)
dest.global_phase += other.global_phase
if inplace:
return None
return dest
def tensor(self, other):
"""Tensor ``self`` with ``other``.
Remember that in the little-endian convention the leftmost operation will be at the bottom
of the circuit. See also
[the docs](qiskit.org/documentation/tutorials/circuits/3_summary_of_quantum_operations.html)
for more information.
.. parsed-literal::
┌────────┐ ┌─────┐ ┌─────┐
q_0: ┤ bottom ├ ⊗ q_0: ┤ top ├ = q_0: ─┤ top ├──
└────────┘ └─────┘ ┌┴─────┴─┐
q_1: ┤ bottom ├
└────────┘
Args:
other (QuantumCircuit): The other circuit to tensor this circuit with.
Examples:
.. jupyter-execute::
from qiskit import QuantumCircuit
top = QuantumCircuit(1)
top.x(0);
bottom = QuantumCircuit(2)
bottom.cry(0.2, 0, 1);
tensored = bottom.tensor(top)
print(tensored.draw())
Returns:
QuantumCircuit: The tensored circuit (returns None if inplace==True).
"""
num_qubits = self.num_qubits + other.num_qubits
num_clbits = self.num_clbits + other.num_clbits
# If a user defined both circuits with via register sizes and not with named registers
# (e.g. QuantumCircuit(2, 2)) then we have a naming collision, as the registers are by
# default called "q" resp. "c". To still allow tensoring we define new registers of the
# correct sizes.
if len(self.qregs) == len(other.qregs) == 1 and \
self.qregs[0].name == other.qregs[0].name == 'q':
# check if classical registers are in the circuit
if num_clbits > 0:
dest = QuantumCircuit(num_qubits, num_clbits)
else:
dest = QuantumCircuit(num_qubits)
# handle case if ``measure_all`` was called on both circuits, in which case the
# registers are both named "meas"
elif len(self.cregs) == len(other.cregs) == 1 and \
self.cregs[0].name == other.cregs[0].name == 'meas':
cr = ClassicalRegister(self.num_clbits + other.num_clbits, 'meas')
dest = QuantumCircuit(*other.qregs, *self.qregs, cr)
# Now we don't have to handle any more cases arising from special implicit naming
else:
dest = QuantumCircuit(other.qubits, self.qubits, other.clbits, self.clbits,
*other.qregs, *self.qregs, *other.cregs, *self.cregs)
# compose self onto the output, and then other
dest.compose(other, range(other.num_qubits), range(other.num_clbits), inplace=True)
dest.compose(self, range(other.num_qubits, num_qubits),
range(other.num_clbits, num_clbits), inplace=True)
return dest
@property
def qubits(self):
"""
Returns a list of quantum bits in the order that the registers were added.
"""
return self._qubits
@property
def clbits(self):
"""
Returns a list of classical bits in the order that the registers were added.
"""
return self._clbits
@property
def ancillas(self):
"""
Returns a list of ancilla bits in the order that the registers were added.
"""
return self._ancillas
def __add__(self, rhs):
"""Overload + to implement self.combine."""
return self.combine(rhs)
def __iadd__(self, rhs):
"""Overload += to implement self.extend."""
return self.extend(rhs)
def __len__(self):
"""Return number of operations in circuit."""
return len(self._data)
def __getitem__(self, item):
"""Return indexed operation."""
return self._data[item]
@staticmethod
def cast(value, _type):
"""Best effort to cast value to type. Otherwise, returns the value."""
try:
return _type(value)
except (ValueError, TypeError):
return value
@staticmethod
def _bit_argument_conversion(bit_representation, in_array):
ret = None
try:
if isinstance(bit_representation, Bit):
# circuit.h(qr[0]) -> circuit.h([qr[0]])
ret = [bit_representation]
elif isinstance(bit_representation, Register):
# circuit.h(qr) -> circuit.h([qr[0], qr[1]])
ret = bit_representation[:]
elif isinstance(QuantumCircuit.cast(bit_representation, int), int):
# circuit.h(0) -> circuit.h([qr[0]])
ret = [in_array[bit_representation]]
elif isinstance(bit_representation, slice):
# circuit.h(slice(0,2)) -> circuit.h([qr[0], qr[1]])
ret = in_array[bit_representation]
elif isinstance(bit_representation, list) and \
all(isinstance(bit, Bit) for bit in bit_representation):
# circuit.h([qr[0], qr[1]]) -> circuit.h([qr[0], qr[1]])
ret = bit_representation
elif isinstance(QuantumCircuit.cast(bit_representation, list), (range, list)):
# circuit.h([0, 1]) -> circuit.h([qr[0], qr[1]])
# circuit.h(range(0,2)) -> circuit.h([qr[0], qr[1]])
# circuit.h([qr[0],1]) -> circuit.h([qr[0], qr[1]])
ret = [index if isinstance(index, Bit) else in_array[
index] for index in bit_representation]
else:
raise CircuitError('Not able to expand a %s (%s)' % (bit_representation,
type(bit_representation)))
except IndexError as ex:
raise CircuitError('Index out of range.') from ex
except TypeError as ex:
raise CircuitError(
f'Type error handling {bit_representation} ({type(bit_representation)})'
) from ex
return ret
def qbit_argument_conversion(self, qubit_representation):
"""
Converts several qubit representations (such as indexes, range, etc.)
into a list of qubits.
Args:
qubit_representation (Object): representation to expand
Returns:
List(tuple): Where each tuple is a qubit.
"""
return QuantumCircuit._bit_argument_conversion(qubit_representation, self.qubits)
def cbit_argument_conversion(self, clbit_representation):
"""
Converts several classical bit representations (such as indexes, range, etc.)
into a list of classical bits.
Args:
clbit_representation (Object): representation to expand
Returns:
List(tuple): Where each tuple is a classical bit.
"""
return QuantumCircuit._bit_argument_conversion(clbit_representation, self.clbits)
def append(self, instruction, qargs=None, cargs=None):
"""Append one or more instructions to the end of the circuit, modifying
the circuit in place. Expands qargs and cargs.
Args:
instruction (qiskit.circuit.Instruction): Instruction instance to append
qargs (list(argument)): qubits to attach instruction to
cargs (list(argument)): clbits to attach instruction to
Returns:
qiskit.circuit.Instruction: a handle to the instruction that was just added
Raises:
CircuitError: if object passed is a subclass of Instruction
CircuitError: if object passed is neither subclass nor an instance of Instruction
"""
# Convert input to instruction
if not isinstance(instruction, Instruction) and not hasattr(instruction, 'to_instruction'):
if issubclass(instruction, Instruction):
raise CircuitError('Object is a subclass of Instruction, please add () to '
'pass an instance of this object.')
raise CircuitError('Object to append must be an Instruction or '
'have a to_instruction() method.')
if not isinstance(instruction, Instruction) and hasattr(instruction, "to_instruction"):
instruction = instruction.to_instruction()
# Make copy of parameterized gate instances
if hasattr(instruction, 'params'):
is_parameter = any(isinstance(param, Parameter) for param in instruction.params)
if is_parameter:
instruction = copy.deepcopy(instruction)
expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []]
expanded_cargs = [self.cbit_argument_conversion(carg) for carg in cargs or []]
instructions = InstructionSet()
for (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):
instructions.add(self._append(instruction, qarg, carg), qarg, carg)
return instructions
def _append(self, instruction, qargs, cargs):
"""Append an instruction to the end of the circuit, modifying
the circuit in place.
Args:
instruction (Instruction or Operator): Instruction instance to append
qargs (list(tuple)): qubits to attach instruction to
cargs (list(tuple)): clbits to attach instruction to
Returns:
Instruction: a handle to the instruction that was just added
Raises:
CircuitError: if the gate is of a different shape than the wires
it is being attached to.
"""
if not isinstance(instruction, Instruction):
raise CircuitError('object is not an Instruction.')
# do some compatibility checks
self._check_dups(qargs)
self._check_qargs(qargs)
self._check_cargs(cargs)
# add the instruction onto the given wires
instruction_context = instruction, qargs, cargs
self._data.append(instruction_context)
self._update_parameter_table(instruction)
# mark as normal circuit if a new instruction is added
self.duration = None
self.unit = 'dt'
return instruction
def _update_parameter_table(self, instruction):
for param_index, param in enumerate(instruction.params):
if isinstance(param, ParameterExpression):
current_parameters = self._parameter_table
for parameter in param.parameters: