API

ExecResult

Bases: Result

Class that save the execute results returned from backend.

Attributes:

Name Type Description
counts dict

Samples counts on each bitstring.

probabilities dict

Calculated probabilities on each bitstring.

taskid int

Unique task id for the execute result.

transpiled_circuit QuantumCircuit

Quantum circuit transpiled on backend.

Source code in quafu\results\results.py
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
class ExecResult(Result):
    """ 
    Class that save the execute results returned from backend.

    Attributes:
        counts (dict): Samples counts on each bitstring.
        probabilities (dict): Calculated probabilities on each bitstring.
        taskid (int): Unique task id for the execute result.
        transpiled_circuit (QuantumCircuit): Quantum circuit transpiled on backend.
    """
    def __init__(self, input_dict, measures):
        status_map = {0:"In Queue", 1:"Running", 2:"Completed", "Canceled":3, 4:"Failed"}
        self.measures = measures
        self.task_status = status_map[input_dict["status"]]
        self.res = eval(input_dict['res'])
        self.counts = OrderedDict(sorted(self.res.items(), key=lambda s: s[0]))
        self.logicalq_res = {}
        cbits = list(self.measures.values())
        for key, values in self.counts.items():
           newkey = "".join([key[i] for i in cbits])
           self.logicalq_res[newkey] = values

        self.taskid = input_dict['task_id']
        self.taskname = input_dict['task_name']
        self.transpiled_openqasm = input_dict["openqasm"]
        from ..circuits.quantum_circuit import QuantumCircuit
        self.transpiled_circuit = QuantumCircuit(0)
        self.transpiled_circuit.from_openqasm(self.transpiled_openqasm)
        self.measure_base = []
        total_counts = sum(self.counts.values())
        self.probabilities = {} 
        for key in self.counts:
            self.probabilities[key] = self.counts[key]/total_counts


    def calculate_obs(self, pos):
        """
        Calculate observables Z on input position using probabilities

        Args: 
            pos (list[int]): Positions of observalbes.
        """
        return measure_obs(pos, self.logicalq_res) 

    def plot_probabilities(self):
        """
        Plot the probabilities from execute results.
        """
        bitstrs = list(self.probabilities.keys())
        probs = list(self.probabilities.values())
        plt.figure()
        plt.bar(range(len(probs)), probs, tick_label = bitstrs)
        plt.xticks(rotation=70)
        plt.ylabel("probabilities")

calculate_obs(pos)

Calculate observables Z on input position using probabilities

Args: pos (list[int]): Positions of observalbes.

Source code in quafu\results\results.py
48
49
50
51
52
53
54
55
def calculate_obs(self, pos):
    """
    Calculate observables Z on input position using probabilities

    Args: 
        pos (list[int]): Positions of observalbes.
    """
    return measure_obs(pos, self.logicalq_res) 

plot_probabilities()

Plot the probabilities from execute results.

Source code in quafu\results\results.py
57
58
59
60
61
62
63
64
65
66
def plot_probabilities(self):
    """
    Plot the probabilities from execute results.
    """
    bitstrs = list(self.probabilities.keys())
    probs = list(self.probabilities.values())
    plt.figure()
    plt.bar(range(len(probs)), probs, tick_label = bitstrs)
    plt.xticks(rotation=70)
    plt.ylabel("probabilities")

QuantumCircuit

Bases: object

Source code in quafu\circuits\quantum_circuit.py
  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
class QuantumCircuit(object):
    def __init__(self, num: int):
        """
        Initialize a QuantumCircuit object

        Args:   
            num (int): Total qubit number used
        """
        self.num = num
        self.gates = []
        self.openqasm = ""
        self.circuit = []
        self.measures = dict(zip(range(num), range(num)))
        self._used_qubits = []

    @property
    def used_qubits(self) -> List:
        self.layered_circuit()
        return self._used_qubits

    def add_gate(self, gate : QuantumGate):
        self.gates.append(gate)

    def layered_circuit(self) -> np.ndarray:
        """
        Make layered circuit from the gate sequence self.gates.

        Returns: 
            A layered list with left justed circuit.
        """
        num = self.num
        gatelist = self.gates
        gateQlist = [[] for i in range(num)]
        used_qubits = []
        for gate in gatelist:
            if isinstance(gate, SingleQubitGate) or isinstance(gate, Delay):
                gateQlist[gate.pos].append(gate)
                if gate.pos not in used_qubits:
                    used_qubits.append(gate.pos)

            elif isinstance(gate, Barrier) or isinstance(gate, MultiQubitGate) or isinstance(gate, XYResonance):
                pos1 = min(gate.pos)
                pos2 = max(gate.pos)
                gateQlist[pos1].append(gate)
                for j in range(pos1 + 1, pos2 + 1):
                    gateQlist[j].append(None)

                if isinstance(gate, MultiQubitGate) or isinstance(gate, XYResonance):
                    for pos in gate.pos:
                        if pos not in used_qubits:
                            used_qubits.append(pos)

                maxlayer = max([len(gateQlist[j]) for j in range(pos1, pos2 + 1)])
                for j in range(pos1, pos2 + 1):
                    layerj = len(gateQlist[j])
                    pos = layerj - 1
                    if not layerj == maxlayer:
                        for i in range(abs(layerj - maxlayer)):
                            gateQlist[j].insert(pos, None)

        maxdepth = max([len(gateQlist[i]) for i in range(num)])

        for gates in gateQlist:
            gates.extend([None] * (maxdepth - len(gates)))

        for m in self.measures.keys():
            if m not in used_qubits:
                used_qubits.append(m)
        used_qubits = np.sort(used_qubits)

        new_gateQlist = []
        for old_qi in range(len(gateQlist)):
            gates = gateQlist[old_qi]
            if old_qi in used_qubits:
                new_gateQlist.append(gates)

        lc = np.array(new_gateQlist)
        lc = np.vstack((used_qubits, lc.T)).T
        self.circuit = lc
        self._used_qubits = list(used_qubits)
        return self.circuit

    def draw_circuit(self, width : int=4, return_str : bool=False):
        """
        Draw layered circuit using ASCII, print in terminal.

        Args:
            width (int): The width of each gate.
            return_str: Whether return the circuit string.
        """
        self.layered_circuit()
        gateQlist = self.circuit
        num = gateQlist.shape[0]
        depth = gateQlist.shape[1] - 1
        printlist = np.array([[""] * depth for i in range(2 * num)], dtype="<U30")

        reduce_map = dict(zip(gateQlist[:, 0], range(num)))
        reduce_map_inv = dict(zip(range(num), gateQlist[:, 0]))
        for l in range(depth):
            layergates = gateQlist[:, l + 1]
            maxlen = 1 + width
            for i in range(num):
                gate = layergates[i]
                if isinstance(gate, SingleQubitGate) or isinstance(gate, Delay):
                    printlist[i * 2, l] = gate.symbol
                    maxlen = max(maxlen, len(gate.symbol) + width)

                elif isinstance(gate, MultiQubitGate) or isinstance(gate, XYResonance):
                    q1 = reduce_map[min(gate.pos)]
                    q2 = reduce_map[max(gate.pos)]
                    printlist[2 * q1 + 1:2 * q2, l] = "|"
                    printlist[q1 * 2, l] = "#"
                    printlist[q2 * 2, l] = "#"
                    if isinstance(gate, ControlledGate): #Controlled-Multiqubit gate
                        for ctrl in gate.ctrls:
                            printlist[reduce_map[ctrl] * 2, l] = "*"

                        if gate.targ_name == "SWAP":
                            printlist[reduce_map[gate.targs[0]] * 2, l] = "x"
                            printlist[reduce_map[gate.targs[1]] * 2, l] = "x"
                        else:
                            tq1 = reduce_map[min(gate.targs)]
                            tq2 = reduce_map[max(gate.targs)]
                            printlist[tq1 * 2, l] = "#"
                            printlist[tq2 * 2, l] = "#"
                            if tq1 + tq2 in [reduce_map[ctrl] * 2 for ctrl in gate.ctrls]:
                                printlist[tq1 + tq2, l] = "*" + gate.symbol
                            else:
                                printlist[tq1 + tq2, l] = gate.symbol
                            maxlen = max(maxlen, len(gate.symbol) + width)

                    else: #Multiqubit gate
                        if gate.name == "SWAP":
                            printlist[q1 * 2, l] = "x"
                            printlist[q2 * 2, l] = "x"

                        else:
                            printlist[q1 + q2, l] = gate.symbol
                            maxlen = max(maxlen, len(gate.symbol) + width)

                elif isinstance(gate, Barrier):
                    pos = [i for i in gate.pos if i in reduce_map.keys()]
                    q1 = reduce_map[min(pos)]
                    q2 = reduce_map[max(pos)]
                    printlist[2 * q1:2 * q2 + 1, l] = "||"
                    maxlen = max(maxlen, len("||"))


            printlist[-1, l] = maxlen

        circuitstr = []
        for j in range(2 * num - 1):
            if j % 2 == 0:
                linestr = ("q[%d]" % (reduce_map_inv[j // 2])).ljust(6) + "".join(
                    [printlist[j, l].center(int(printlist[-1, l]), "-") for l in range(depth)])
                if reduce_map_inv[j // 2] in self.measures.keys():
                    linestr += " M->c[%d]" % self.measures[reduce_map_inv[j // 2]]
                circuitstr.append(linestr)
            else:
                circuitstr.append("".ljust(6) + "".join(
                    [printlist[j, l].center(int(printlist[-1, l]), " ") for l in range(depth)]))
        circuitstr = "\n".join(circuitstr)

        if return_str:
            return circuitstr
        else:
            print(circuitstr)

    def from_openqasm(self, openqasm : str):
        """
        Initialize the circuit from openqasm text.
        Args:
            openqasm: input openqasm str.
        """
        from numpy import pi
        import re
        self.openqasm = openqasm
        # lines = self.openqasm.strip("\n").splitlines(";")
        lines = self.openqasm.splitlines()
        lines = [line for line in lines if line]
        self.gates = []
        self.measures = {}
        measured_qubits = []
        global_valid = True
        for line in lines[2:]:
            if line:
                operations_qbs = line.split(" ", 1)
                operations = operations_qbs[0]
                if operations == "qreg":
                    qbs = operations_qbs[1]
                    self.num = int(re.findall("\d+", qbs)[0])
                elif operations == "creg":
                    pass
                elif operations == "measure":
                    qbs = operations_qbs[1]
                    indstr = re.findall("\d+", qbs)
                    inds = [int(indst) for indst in indstr]
                    mb = inds[0]
                    cb = inds[1]
                    self.measures[mb] = cb
                    measured_qubits.append(mb)
                else:
                    qbs = operations_qbs[1]
                    indstr = re.findall("\d+", qbs)
                    inds = [int(indst) for indst in indstr]
                    valid = True
                    for pos in inds:
                        if pos in measured_qubits:
                            valid = False
                            global_valid = False
                            break

                    if valid:
                        if operations == "barrier":
                            self.barrier(inds)

                        else:
                            sp_op = operations.split("(")
                            gatename = sp_op[0]
                            if gatename == "delay":
                                paras = sp_op[1].strip("()")
                                duration = int(re.findall("\d+", paras)[0])
                                unit = re.findall("[a-z]+", paras)[0]
                                self.delay(inds[0], duration, unit)
                            elif gatename == "xy":
                                paras = sp_op[1].strip("()")
                                duration = int(re.findall("\d+", paras)[0])
                                unit = re.findall("[a-z]+", paras)[0]
                                self.xy(min(inds), max(inds), duration, unit)
                            else:
                                if len(sp_op) > 1:
                                    paras = sp_op[1].strip("()")
                                    parastr = paras.split(",")
                                    paras = [eval(parai, {"pi": pi}) for parai in parastr]

                                if gatename == "cx":
                                    self.cnot(inds[0], inds[1])
                                elif gatename == "cy":
                                    self.cy(inds[0], inds[1])
                                elif gatename == "cz":
                                    self.cz(inds[0], inds[1])
                                elif gatename == "cp":
                                    self.cp(inds[0], inds[1], paras[0])
                                elif gatename == "swap":
                                    self.swap(inds[0], inds[1])
                                elif gatename == "rx":
                                    self.rx(inds[0], paras[0])
                                elif gatename == "ry":
                                    self.ry(inds[0], paras[0])
                                elif gatename == "rz":
                                    self.rz(inds[0], paras[0])
                                elif gatename == "p":
                                    self.p(inds[0], paras[0])
                                elif gatename == "x":
                                    self.x(inds[0])
                                elif gatename == "y":
                                    self.y(inds[0])
                                elif gatename == "z":
                                    self.z(inds[0])
                                elif gatename == "h":
                                    self.h(inds[0])
                                elif gatename == "id":
                                    self.id(inds[0])
                                elif gatename == "s":
                                    self.s(inds[0])
                                elif gatename == "sdg":
                                    self.sdg(inds[0])
                                elif gatename == "t":
                                    self.t(inds[0])
                                elif gatename == "tdg":
                                    self.tdg(inds[0])
                                elif gatename == "sx":
                                    self.sx(inds[0])
                                elif gatename == "ccx":
                                    self.toffoli(inds[0], inds[1], inds[2])
                                elif gatename == "cswap":
                                    self.fredkin(inds[0], inds[1], inds[2])
                                elif gatename == "u1":
                                    self.rz(inds[0], paras[0])
                                elif gatename == "u2":
                                    self.rz(inds[0], paras[1])
                                    self.ry(inds[0], pi / 2)
                                    self.rz(inds[0], paras[0])
                                elif gatename == "u3":
                                    self.rz(inds[0], paras[2])
                                    self.ry(inds[0], paras[0])
                                    self.rz(inds[0], paras[1])
                                elif gatename == "rxx":
                                    self.rxx(inds[0], inds[1], paras[0])
                                elif gatename == "ryy":
                                    self.ryy(inds[0], inds[1], paras[0])
                                elif gatename == "rzz":
                                    self.rzz(inds[0], inds[1], paras[0])
                                else:
                                    print(
                                        "Warning: Operations %s may be not supported by QuantumCircuit class currently." % gatename)

        if not self.measures:
            self.measures = dict(zip(range(self.num), range(self.num)))
        if not global_valid:
            print("Warning: All operations after measurement will be removed for executing on experiment")

    def to_openqasm(self) -> str:
        """
        Convert the circuit to openqasm text.

        Returns: 
            openqasm text.
        """
        qasm = "OPENQASM 2.0;\ninclude \"qelib1.inc\";\n"
        qasm += "qreg q[%d];\n" % self.num
        qasm += "creg meas[%d];\n" % len(self.measures)
        for gate in self.gates:
            if isinstance(gate, SingleQubitGate):
                if isinstance(gate, ParaSingleQubitGate):
                    if isinstance(gate.paras, Iterable):
                        qasm += "%s(" %gate.name.lower() + ",".join(["%s" %para for para in gate.paras]) + ") q[%d];\n" % (gate.pos)
                    else:
                        qasm += "%s(%s) " %(gate.name.lower(), gate.paras) + "q[%d];\n" % (gate.pos)
                else:
                    if gate.name == "SY":
                        qasm += "ry(pi/2) q[%d];\n" %(gate.pos)
                    elif gate.name == "W":
                        qasm += "rz(-pi/4) q[%d];\nrx(pi) q[%d];\nrz(pi/4) q[%d];\n"  %(gate.pos, gate.pos, gate.pos)
                    elif gate.name == "SW":
                        qasm += "rz(-pi/4) q[%d];\nrx(pi/2) q[%d];\nrz(pi/4) q[%d];\n"  %(gate.pos, gate.pos, gate.pos)
                    else:
                        qasm += "%s q[%d];\n" % (gate.name.lower(), gate.pos)

            elif isinstance(gate, Delay):
                qasm += "delay(%d%s) q[%d];\n" % (gate.duration, gate.unit, gate.pos)
            elif isinstance(gate, XYResonance):
                qasm += "xy(%d%s) " %(gate.duration, gate.unit) + ",".join(["q[%d]" % p for p in range(min(gate.pos), max(gate.pos)+1)]) + ";\n"

            elif isinstance(gate, Barrier) or isinstance(gate, MultiQubitGate):
                if isinstance(gate, ParaMultiQubitGate) or (isinstance(gate, ControlledGate) and bool(gate.paras)):
                    if isinstance(gate.paras, Iterable):
                        qasm += "%s(" %gate.name.lower() + ",".join(["%s" %para for para in gate.paras]) + ") " + ",".join(["q[%d]" % p for p in gate.pos]) + ";\n"
                    else:
                         qasm += "%s(%s) " %(gate.name.lower(), gate.paras) + ",".join(["q[%d]" % p for p in gate.pos]) + ";\n"

                else:
                    if gate.name == "CS":
                        qasm += "cp(pi/2) " + "q[%d],q[%d];\n" % (gate.pos[0], gate.pos[1])
                    elif gate.name == "CT":
                        qasm += "cp(pi/4) " + "q[%d],q[%d];\n" % (gate.pos[0], gate.pos[1])
                    elif gate.name == "barrier":
                        qasm += "barrier " + ",".join(["q[%d]" % p for p in range(min(gate.pos), max(gate.pos)+1)]) + ";\n"
                    else:
                        qasm += "%s " %(gate.name.lower()) + ",".join(["q[%d]" % p for p in gate.pos]) + ";\n"

        for key in self.measures:
            qasm += "measure q[%d] -> meas[%d];\n" % (key, self.measures[key])

        self.openqasm = qasm
        return qasm


    def id(self, pos: int) -> "QuantumCircuit":
        """
        Identity gate.

        Args:
            pos (int): qubit the gate act.
        """ 
        self.gates.append(IdGate(pos))
        return self

    def h(self, pos: int) -> "QuantumCircuit":
        """
        Hadamard gate.

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(HGate(pos))
        return self

    def x(self, pos: int) -> "QuantumCircuit":
        """
        X gate.

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(XGate(pos))
        return self

    def y(self, pos: int) -> "QuantumCircuit":
        """
        Y gate.

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(YGate(pos))
        return self

    def z(self, pos: int) -> "QuantumCircuit":
        """
        Z gate.

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(ZGate(pos))
        return self

    def t(self, pos: int) -> "QuantumCircuit":
        """
        T gate. (~Z^(1/4))

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(TGate(pos))
        return self

    def tdg(self, pos: int) -> "QuantumCircuit":
        """
        Tdg gate. (Inverse of T gate)

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(TdgGate(pos))

    def s(self, pos: int) -> "QuantumCircuit":
        """
        S gate. (~√Z)

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(SGate(pos))
        return self

    def sdg(self, pos: int) -> "QuantumCircuit":
        """
        Sdg gate. (Inverse of S gate)

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(SdgGate(pos))
        return self

    def sx(self, pos: int) -> "QuantumCircuit":
        """
        √X gate.

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(SXGate(pos))
        return self

    def sy(self, pos: int) -> "QuantumCircuit":
        """
        √Y gate.

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(SYGate(pos))
        return self

    def w(self, pos: int) -> "QuantumCircuit":
        """
        W gate. (~(X + Y)/√2)

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(WGate(pos))
        return self

    def sw(self, pos: int) -> "QuantumCircuit":
        """
        √W gate.

        Args:
            pos (int): qubit the gate act.
        """
        self.gates.append(SWGate(pos))
        return self

    def rx(self, pos: int, para: float) -> "QuantumCircuit":
        """
        Single qubit rotation Rx gate.

        Args:
            pos (int): qubit the gate act.
            para (float): rotation angle
        """
        self.gates.append(RXGate(pos, para))
        return self

    def ry(self, pos: int, para: float) -> "QuantumCircuit":
        """
        Single qubit rotation Ry gate.

        Args:
            pos (int): qubit the gate act.
            para (float): rotation angle
        """
        self.gates.append(RYGate(pos, para))
        return self

    def rz(self, pos: int, para: float) -> "QuantumCircuit":
        """
        Single qubit rotation Rz gate.

        Args:
            pos (int): qubit the gate act.
            para (float): rotation angle
        """
        self.gates.append(RZGate(pos, para))
        return self

    def p(self, pos: int, para: float) -> "QuantumCircuit":
        """
        Phase gate

        Args:
            pos (int): qubit the gate act.
            para (float): rotation angle
        """
        self.gates.append(PhaseGate(pos, para))

    def cnot(self, ctrl: int, tar: int) -> "QuantumCircuit":
        """
        CNOT gate.

        Args:
            ctrl (int): control qubit.
            tar (int): target qubit.
        """
        self.gates.append(CXGate(ctrl, tar))
        return self

    def cy(self, ctrl: int, tar: int) -> "QuantumCircuit":
        """
        Control-Y gate.

        Args:
            ctrl (int): control qubit.
            tar (int): target qubit.
        """
        self.gates.append(CYGate(ctrl, tar))
        return self

    def cz(self, ctrl: int, tar: int) -> "QuantumCircuit":
        """
        Control-Z gate.

        Args:
            ctrl (int): control qubit.
            tar (int): target qubit.
        """
        self.gates.append(CZGate(ctrl, tar))
        return self

    def cs(self, ctrl: int, tar: int) -> "QuantumCircuit":
        """
        Control-S gate.
        Args:
            ctrl (int): control qubit.
            tar (int): target qubit.
        """
        self.gates.append(CSGate(ctrl, tar))
        return self

    def ct(self, ctrl: int, tar: int) -> "QuantumCircuit":
        """
        Control-T gate.
        Args:
            ctrl (int): control qubit.
            tar (int): target qubit.
        """

        self.gates.append(CTGate(ctrl, tar))
        return self

    def cp(self, ctrl: int, tar: int, para) -> "QuantumCircuit":
        """
        Control-P gate.

        Args:
            ctrl (int): control qubit.
            tar (int): target qubit.
        """
        self.gates.append(CPGate(ctrl, tar, para))
        return self


    def swap(self, q1: int, q2: int) -> "QuantumCircuit":
        """
        SWAP gate

        Args:
            q1 (int): qubit the gate act.
            q2 (int): qubit the gate act.
        """
        self.gates.append(SwapGate(q1, q2))
        return self

    def toffoli(self, ctrl1: int, ctrl2: int, targ: int) -> "QuantumCircuit":
        """
        Toffoli gate

        Args:
            ctrl1 (int): control qubit
            ctrl2 (int): control qubit
            targ (int): target qubit
        """
        self.gates.append(ToffoliGate(ctrl1, ctrl2, targ))
        return self

    def fredkin(self, ctrl: int, targ1:int , targ2: int) -> "QuantumCircuit":
        """
        Fredkin gate

        Args:
            ctrl (int):  control qubit
            targ1 (int): target qubit
            targ2 (int): target qubit
        """
        self.gates.append(FredkinGate(ctrl, targ1, targ2))
        return self

    def barrier(self, qlist: List[int]) -> "QuantumCircuit":
        """
        Add barrier for qubits in qlist.

        Args:
            qlist (list[int]): A list contain the qubit need add barrier. When qlist contain at least two qubit, the barrier will be added from minimum qubit to maximum qubit. For example: barrier([0, 2]) create barrier for qubits 0, 1, 2. To create discrete barrier, using barrier([0]), barrier([2]).
        """
        self.gates.append(Barrier(qlist))
        return self

    def delay(self, pos, duration, unit="ns") -> "QuantumCircuit":
        """
        Let the qubit idle for a certain duration.

        Args:
            pos (int): qubit need delay.
            duration (int): duration of qubit delay, which represents integer times of unit.
            unit (str): time unit for the duration. Can be "ns" and "us". 
        """
        self.gates.append(Delay(pos, duration, unit=unit))
        return self

    def xy(self, qs: int, qe: int, duration: int, unit: str="ns") -> "QuantumCircuit":
        """
        XY resonance time evolution for quantum simulator
        Args:
            qs: start position of resonant qubits.
            qe: end position of resonant qubits.
            duration: duration must be integer, which represents integer times of unit.
            unit: time unit of duration.

        """
        self.gates.append(XYResonance(qs, qe, duration, unit=unit))
        return self

    def rxx(self, q1: int, q2: int, theta):
        """
        Rotation about 2-qubit XX axis.
        Args:
            q1 (int): qubit the gate act.
            q2 (int): qubit the gate act.
            theta: rotation angle.

        """
        self.gates.append(RXXGate(q1, q2, theta))

    def ryy(self, q1: int, q2: int, theta):
        """
        Rotation about 2-qubit YY axis.
        Args:
            q1 (int): qubit the gate act.
            q2 (int): qubit the gate act.
            theta: rotation angle.

        """
        self.gates.append(RYYGate(q1, q2, theta))

    def rzz(self, q1: int, q2: int, theta):
        """
        Rotation about 2-qubit ZZ axis.
        Args:
            q1 (int): qubit the gate act.
            q2 (int): qubit the gate act.
            theta: rotation angle.

        """
        self.gates.append(RZZGate(q1, q2, theta))

    def mcx(self, ctrls: List[int], targ: int):
        """
        Multi-controlled X gate.
        Args:
            ctrls: A list of control qubits.
            targ: Target qubits.
        """
        self.gates.append(MCXGate(ctrls, targ))

    def mcy(self, ctrls: List[int], targ: int):
        """
        Multi-controlled Y gate.
        Args:
            ctrls: A list of control qubits.
            targ: Target qubits.
        """
        self.gates.append(MCYGate(ctrls, targ))

    def mcz(self, ctrls: List[int], targ: int):
        """
        Multi-controlled Z gate.
        Args:
            ctrls: A list of control qubits.
            targ: Target qubits.
        """
        self.gates.append(MCZGate(ctrls, targ))


    def measure(self, pos: List[int], cbits: List[int] = []) -> None:
        """
        Measurement setting for experiment device.

        Args:
            pos: Qubits need measure.
            cbits: Classical bits keeping the measure results.
        """

        self.measures = dict(zip(pos, range(len(pos))))

        if cbits:
            if len(cbits) == len(self.measures):
                self.measures = dict(zip(pos, cbits))
            else:
                raise CircuitError("Number of measured bits should equal to the number of classical bits")

__init__(num)

Initialize a QuantumCircuit object

Args:
num (int): Total qubit number used

Source code in quafu\circuits\quantum_circuit.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def __init__(self, num: int):
    """
    Initialize a QuantumCircuit object

    Args:   
        num (int): Total qubit number used
    """
    self.num = num
    self.gates = []
    self.openqasm = ""
    self.circuit = []
    self.measures = dict(zip(range(num), range(num)))
    self._used_qubits = []

barrier(qlist)

Add barrier for qubits in qlist.

Parameters:

Name Type Description Default
qlist list[int]

A list contain the qubit need add barrier. When qlist contain at least two qubit, the barrier will be added from minimum qubit to maximum qubit. For example: barrier([0, 2]) create barrier for qubits 0, 1, 2. To create discrete barrier, using barrier([0]), barrier([2]).

required
Source code in quafu\circuits\quantum_circuit.py
638
639
640
641
642
643
644
645
646
def barrier(self, qlist: List[int]) -> "QuantumCircuit":
    """
    Add barrier for qubits in qlist.

    Args:
        qlist (list[int]): A list contain the qubit need add barrier. When qlist contain at least two qubit, the barrier will be added from minimum qubit to maximum qubit. For example: barrier([0, 2]) create barrier for qubits 0, 1, 2. To create discrete barrier, using barrier([0]), barrier([2]).
    """
    self.gates.append(Barrier(qlist))
    return self

cnot(ctrl, tar)

CNOT gate.

Parameters:

Name Type Description Default
ctrl int

control qubit.

required
tar int

target qubit.

required
Source code in quafu\circuits\quantum_circuit.py
537
538
539
540
541
542
543
544
545
546
def cnot(self, ctrl: int, tar: int) -> "QuantumCircuit":
    """
    CNOT gate.

    Args:
        ctrl (int): control qubit.
        tar (int): target qubit.
    """
    self.gates.append(CXGate(ctrl, tar))
    return self

cp(ctrl, tar, para)

Control-P gate.

Parameters:

Name Type Description Default
ctrl int

control qubit.

required
tar int

target qubit.

required
Source code in quafu\circuits\quantum_circuit.py
591
592
593
594
595
596
597
598
599
600
def cp(self, ctrl: int, tar: int, para) -> "QuantumCircuit":
    """
    Control-P gate.

    Args:
        ctrl (int): control qubit.
        tar (int): target qubit.
    """
    self.gates.append(CPGate(ctrl, tar, para))
    return self

cs(ctrl, tar)

Control-S gate.

Parameters:

Name Type Description Default
ctrl int

control qubit.

required
tar int

target qubit.

required
Source code in quafu\circuits\quantum_circuit.py
570
571
572
573
574
575
576
577
578
def cs(self, ctrl: int, tar: int) -> "QuantumCircuit":
    """
    Control-S gate.
    Args:
        ctrl (int): control qubit.
        tar (int): target qubit.
    """
    self.gates.append(CSGate(ctrl, tar))
    return self

ct(ctrl, tar)

Control-T gate.

Parameters:

Name Type Description Default
ctrl int

control qubit.

required
tar int

target qubit.

required
Source code in quafu\circuits\quantum_circuit.py
580
581
582
583
584
585
586
587
588
589
def ct(self, ctrl: int, tar: int) -> "QuantumCircuit":
    """
    Control-T gate.
    Args:
        ctrl (int): control qubit.
        tar (int): target qubit.
    """

    self.gates.append(CTGate(ctrl, tar))
    return self

cy(ctrl, tar)

Control-Y gate.

Parameters:

Name Type Description Default
ctrl int

control qubit.

required
tar int

target qubit.

required
Source code in quafu\circuits\quantum_circuit.py
548
549
550
551
552
553
554
555
556
557
def cy(self, ctrl: int, tar: int) -> "QuantumCircuit":
    """
    Control-Y gate.

    Args:
        ctrl (int): control qubit.
        tar (int): target qubit.
    """
    self.gates.append(CYGate(ctrl, tar))
    return self

cz(ctrl, tar)

Control-Z gate.

Parameters:

Name Type Description Default
ctrl int

control qubit.

required
tar int

target qubit.

required
Source code in quafu\circuits\quantum_circuit.py
559
560
561
562
563
564
565
566
567
568
def cz(self, ctrl: int, tar: int) -> "QuantumCircuit":
    """
    Control-Z gate.

    Args:
        ctrl (int): control qubit.
        tar (int): target qubit.
    """
    self.gates.append(CZGate(ctrl, tar))
    return self

delay(pos, duration, unit='ns')

Let the qubit idle for a certain duration.

Parameters:

Name Type Description Default
pos int

qubit need delay.

required
duration int

duration of qubit delay, which represents integer times of unit.

required
unit str

time unit for the duration. Can be "ns" and "us".

'ns'
Source code in quafu\circuits\quantum_circuit.py
648
649
650
651
652
653
654
655
656
657
658
def delay(self, pos, duration, unit="ns") -> "QuantumCircuit":
    """
    Let the qubit idle for a certain duration.

    Args:
        pos (int): qubit need delay.
        duration (int): duration of qubit delay, which represents integer times of unit.
        unit (str): time unit for the duration. Can be "ns" and "us". 
    """
    self.gates.append(Delay(pos, duration, unit=unit))
    return self

draw_circuit(width=4, return_str=False)

Draw layered circuit using ASCII, print in terminal.

Parameters:

Name Type Description Default
width int

The width of each gate.

4
return_str bool

Whether return the circuit string.

False
Source code in quafu\circuits\quantum_circuit.py
 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
def draw_circuit(self, width : int=4, return_str : bool=False):
    """
    Draw layered circuit using ASCII, print in terminal.

    Args:
        width (int): The width of each gate.
        return_str: Whether return the circuit string.
    """
    self.layered_circuit()
    gateQlist = self.circuit
    num = gateQlist.shape[0]
    depth = gateQlist.shape[1] - 1
    printlist = np.array([[""] * depth for i in range(2 * num)], dtype="<U30")

    reduce_map = dict(zip(gateQlist[:, 0], range(num)))
    reduce_map_inv = dict(zip(range(num), gateQlist[:, 0]))
    for l in range(depth):
        layergates = gateQlist[:, l + 1]
        maxlen = 1 + width
        for i in range(num):
            gate = layergates[i]
            if isinstance(gate, SingleQubitGate) or isinstance(gate, Delay):
                printlist[i * 2, l] = gate.symbol
                maxlen = max(maxlen, len(gate.symbol) + width)

            elif isinstance(gate, MultiQubitGate) or isinstance(gate, XYResonance):
                q1 = reduce_map[min(gate.pos)]
                q2 = reduce_map[max(gate.pos)]
                printlist[2 * q1 + 1:2 * q2, l] = "|"
                printlist[q1 * 2, l] = "#"
                printlist[q2 * 2, l] = "#"
                if isinstance(gate, ControlledGate): #Controlled-Multiqubit gate
                    for ctrl in gate.ctrls:
                        printlist[reduce_map[ctrl] * 2, l] = "*"

                    if gate.targ_name == "SWAP":
                        printlist[reduce_map[gate.targs[0]] * 2, l] = "x"
                        printlist[reduce_map[gate.targs[1]] * 2, l] = "x"
                    else:
                        tq1 = reduce_map[min(gate.targs)]
                        tq2 = reduce_map[max(gate.targs)]
                        printlist[tq1 * 2, l] = "#"
                        printlist[tq2 * 2, l] = "#"
                        if tq1 + tq2 in [reduce_map[ctrl] * 2 for ctrl in gate.ctrls]:
                            printlist[tq1 + tq2, l] = "*" + gate.symbol
                        else:
                            printlist[tq1 + tq2, l] = gate.symbol
                        maxlen = max(maxlen, len(gate.symbol) + width)

                else: #Multiqubit gate
                    if gate.name == "SWAP":
                        printlist[q1 * 2, l] = "x"
                        printlist[q2 * 2, l] = "x"

                    else:
                        printlist[q1 + q2, l] = gate.symbol
                        maxlen = max(maxlen, len(gate.symbol) + width)

            elif isinstance(gate, Barrier):
                pos = [i for i in gate.pos if i in reduce_map.keys()]
                q1 = reduce_map[min(pos)]
                q2 = reduce_map[max(pos)]
                printlist[2 * q1:2 * q2 + 1, l] = "||"
                maxlen = max(maxlen, len("||"))


        printlist[-1, l] = maxlen

    circuitstr = []
    for j in range(2 * num - 1):
        if j % 2 == 0:
            linestr = ("q[%d]" % (reduce_map_inv[j // 2])).ljust(6) + "".join(
                [printlist[j, l].center(int(printlist[-1, l]), "-") for l in range(depth)])
            if reduce_map_inv[j // 2] in self.measures.keys():
                linestr += " M->c[%d]" % self.measures[reduce_map_inv[j // 2]]
            circuitstr.append(linestr)
        else:
            circuitstr.append("".ljust(6) + "".join(
                [printlist[j, l].center(int(printlist[-1, l]), " ") for l in range(depth)]))
    circuitstr = "\n".join(circuitstr)

    if return_str:
        return circuitstr
    else:
        print(circuitstr)

fredkin(ctrl, targ1, targ2)

Fredkin gate

Parameters:

Name Type Description Default
ctrl int

control qubit

required
targ1 int

target qubit

required
targ2 int

target qubit

required
Source code in quafu\circuits\quantum_circuit.py
626
627
628
629
630
631
632
633
634
635
636
def fredkin(self, ctrl: int, targ1:int , targ2: int) -> "QuantumCircuit":
    """
    Fredkin gate

    Args:
        ctrl (int):  control qubit
        targ1 (int): target qubit
        targ2 (int): target qubit
    """
    self.gates.append(FredkinGate(ctrl, targ1, targ2))
    return self

from_openqasm(openqasm)

Initialize the circuit from openqasm text.

Parameters:

Name Type Description Default
openqasm str

input openqasm str.

required
Source code in quafu\circuits\quantum_circuit.py
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
def from_openqasm(self, openqasm : str):
    """
    Initialize the circuit from openqasm text.
    Args:
        openqasm: input openqasm str.
    """
    from numpy import pi
    import re
    self.openqasm = openqasm
    # lines = self.openqasm.strip("\n").splitlines(";")
    lines = self.openqasm.splitlines()
    lines = [line for line in lines if line]
    self.gates = []
    self.measures = {}
    measured_qubits = []
    global_valid = True
    for line in lines[2:]:
        if line:
            operations_qbs = line.split(" ", 1)
            operations = operations_qbs[0]
            if operations == "qreg":
                qbs = operations_qbs[1]
                self.num = int(re.findall("\d+", qbs)[0])
            elif operations == "creg":
                pass
            elif operations == "measure":
                qbs = operations_qbs[1]
                indstr = re.findall("\d+", qbs)
                inds = [int(indst) for indst in indstr]
                mb = inds[0]
                cb = inds[1]
                self.measures[mb] = cb
                measured_qubits.append(mb)
            else:
                qbs = operations_qbs[1]
                indstr = re.findall("\d+", qbs)
                inds = [int(indst) for indst in indstr]
                valid = True
                for pos in inds:
                    if pos in measured_qubits:
                        valid = False
                        global_valid = False
                        break

                if valid:
                    if operations == "barrier":
                        self.barrier(inds)

                    else:
                        sp_op = operations.split("(")
                        gatename = sp_op[0]
                        if gatename == "delay":
                            paras = sp_op[1].strip("()")
                            duration = int(re.findall("\d+", paras)[0])
                            unit = re.findall("[a-z]+", paras)[0]
                            self.delay(inds[0], duration, unit)
                        elif gatename == "xy":
                            paras = sp_op[1].strip("()")
                            duration = int(re.findall("\d+", paras)[0])
                            unit = re.findall("[a-z]+", paras)[0]
                            self.xy(min(inds), max(inds), duration, unit)
                        else:
                            if len(sp_op) > 1:
                                paras = sp_op[1].strip("()")
                                parastr = paras.split(",")
                                paras = [eval(parai, {"pi": pi}) for parai in parastr]

                            if gatename == "cx":
                                self.cnot(inds[0], inds[1])
                            elif gatename == "cy":
                                self.cy(inds[0], inds[1])
                            elif gatename == "cz":
                                self.cz(inds[0], inds[1])
                            elif gatename == "cp":
                                self.cp(inds[0], inds[1], paras[0])
                            elif gatename == "swap":
                                self.swap(inds[0], inds[1])
                            elif gatename == "rx":
                                self.rx(inds[0], paras[0])
                            elif gatename == "ry":
                                self.ry(inds[0], paras[0])
                            elif gatename == "rz":
                                self.rz(inds[0], paras[0])
                            elif gatename == "p":
                                self.p(inds[0], paras[0])
                            elif gatename == "x":
                                self.x(inds[0])
                            elif gatename == "y":
                                self.y(inds[0])
                            elif gatename == "z":
                                self.z(inds[0])
                            elif gatename == "h":
                                self.h(inds[0])
                            elif gatename == "id":
                                self.id(inds[0])
                            elif gatename == "s":
                                self.s(inds[0])
                            elif gatename == "sdg":
                                self.sdg(inds[0])
                            elif gatename == "t":
                                self.t(inds[0])
                            elif gatename == "tdg":
                                self.tdg(inds[0])
                            elif gatename == "sx":
                                self.sx(inds[0])
                            elif gatename == "ccx":
                                self.toffoli(inds[0], inds[1], inds[2])
                            elif gatename == "cswap":
                                self.fredkin(inds[0], inds[1], inds[2])
                            elif gatename == "u1":
                                self.rz(inds[0], paras[0])
                            elif gatename == "u2":
                                self.rz(inds[0], paras[1])
                                self.ry(inds[0], pi / 2)
                                self.rz(inds[0], paras[0])
                            elif gatename == "u3":
                                self.rz(inds[0], paras[2])
                                self.ry(inds[0], paras[0])
                                self.rz(inds[0], paras[1])
                            elif gatename == "rxx":
                                self.rxx(inds[0], inds[1], paras[0])
                            elif gatename == "ryy":
                                self.ryy(inds[0], inds[1], paras[0])
                            elif gatename == "rzz":
                                self.rzz(inds[0], inds[1], paras[0])
                            else:
                                print(
                                    "Warning: Operations %s may be not supported by QuantumCircuit class currently." % gatename)

    if not self.measures:
        self.measures = dict(zip(range(self.num), range(self.num)))
    if not global_valid:
        print("Warning: All operations after measurement will be removed for executing on experiment")

h(pos)

Hadamard gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
375
376
377
378
379
380
381
382
383
def h(self, pos: int) -> "QuantumCircuit":
    """
    Hadamard gate.

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(HGate(pos))
    return self

id(pos)

Identity gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
365
366
367
368
369
370
371
372
373
def id(self, pos: int) -> "QuantumCircuit":
    """
    Identity gate.

    Args:
        pos (int): qubit the gate act.
    """ 
    self.gates.append(IdGate(pos))
    return self

layered_circuit()

Make layered circuit from the gate sequence self.gates.

Returns: A layered list with left justed circuit.

Source code in quafu\circuits\quantum_circuit.py
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
def layered_circuit(self) -> np.ndarray:
    """
    Make layered circuit from the gate sequence self.gates.

    Returns: 
        A layered list with left justed circuit.
    """
    num = self.num
    gatelist = self.gates
    gateQlist = [[] for i in range(num)]
    used_qubits = []
    for gate in gatelist:
        if isinstance(gate, SingleQubitGate) or isinstance(gate, Delay):
            gateQlist[gate.pos].append(gate)
            if gate.pos not in used_qubits:
                used_qubits.append(gate.pos)

        elif isinstance(gate, Barrier) or isinstance(gate, MultiQubitGate) or isinstance(gate, XYResonance):
            pos1 = min(gate.pos)
            pos2 = max(gate.pos)
            gateQlist[pos1].append(gate)
            for j in range(pos1 + 1, pos2 + 1):
                gateQlist[j].append(None)

            if isinstance(gate, MultiQubitGate) or isinstance(gate, XYResonance):
                for pos in gate.pos:
                    if pos not in used_qubits:
                        used_qubits.append(pos)

            maxlayer = max([len(gateQlist[j]) for j in range(pos1, pos2 + 1)])
            for j in range(pos1, pos2 + 1):
                layerj = len(gateQlist[j])
                pos = layerj - 1
                if not layerj == maxlayer:
                    for i in range(abs(layerj - maxlayer)):
                        gateQlist[j].insert(pos, None)

    maxdepth = max([len(gateQlist[i]) for i in range(num)])

    for gates in gateQlist:
        gates.extend([None] * (maxdepth - len(gates)))

    for m in self.measures.keys():
        if m not in used_qubits:
            used_qubits.append(m)
    used_qubits = np.sort(used_qubits)

    new_gateQlist = []
    for old_qi in range(len(gateQlist)):
        gates = gateQlist[old_qi]
        if old_qi in used_qubits:
            new_gateQlist.append(gates)

    lc = np.array(new_gateQlist)
    lc = np.vstack((used_qubits, lc.T)).T
    self.circuit = lc
    self._used_qubits = list(used_qubits)
    return self.circuit

mcx(ctrls, targ)

Multi-controlled X gate.

Parameters:

Name Type Description Default
ctrls List[int]

A list of control qubits.

required
targ int

Target qubits.

required
Source code in quafu\circuits\quantum_circuit.py
706
707
708
709
710
711
712
713
def mcx(self, ctrls: List[int], targ: int):
    """
    Multi-controlled X gate.
    Args:
        ctrls: A list of control qubits.
        targ: Target qubits.
    """
    self.gates.append(MCXGate(ctrls, targ))

mcy(ctrls, targ)

Multi-controlled Y gate.

Parameters:

Name Type Description Default
ctrls List[int]

A list of control qubits.

required
targ int

Target qubits.

required
Source code in quafu\circuits\quantum_circuit.py
715
716
717
718
719
720
721
722
def mcy(self, ctrls: List[int], targ: int):
    """
    Multi-controlled Y gate.
    Args:
        ctrls: A list of control qubits.
        targ: Target qubits.
    """
    self.gates.append(MCYGate(ctrls, targ))

mcz(ctrls, targ)

Multi-controlled Z gate.

Parameters:

Name Type Description Default
ctrls List[int]

A list of control qubits.

required
targ int

Target qubits.

required
Source code in quafu\circuits\quantum_circuit.py
724
725
726
727
728
729
730
731
def mcz(self, ctrls: List[int], targ: int):
    """
    Multi-controlled Z gate.
    Args:
        ctrls: A list of control qubits.
        targ: Target qubits.
    """
    self.gates.append(MCZGate(ctrls, targ))

measure(pos, cbits=[])

Measurement setting for experiment device.

Parameters:

Name Type Description Default
pos List[int]

Qubits need measure.

required
cbits List[int]

Classical bits keeping the measure results.

[]
Source code in quafu\circuits\quantum_circuit.py
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
def measure(self, pos: List[int], cbits: List[int] = []) -> None:
    """
    Measurement setting for experiment device.

    Args:
        pos: Qubits need measure.
        cbits: Classical bits keeping the measure results.
    """

    self.measures = dict(zip(pos, range(len(pos))))

    if cbits:
        if len(cbits) == len(self.measures):
            self.measures = dict(zip(pos, cbits))
        else:
            raise CircuitError("Number of measured bits should equal to the number of classical bits")

p(pos, para)

Phase gate

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
para float

rotation angle

required
Source code in quafu\circuits\quantum_circuit.py
527
528
529
530
531
532
533
534
535
def p(self, pos: int, para: float) -> "QuantumCircuit":
    """
    Phase gate

    Args:
        pos (int): qubit the gate act.
        para (float): rotation angle
    """
    self.gates.append(PhaseGate(pos, para))

rx(pos, para)

Single qubit rotation Rx gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
para float

rotation angle

required
Source code in quafu\circuits\quantum_circuit.py
494
495
496
497
498
499
500
501
502
503
def rx(self, pos: int, para: float) -> "QuantumCircuit":
    """
    Single qubit rotation Rx gate.

    Args:
        pos (int): qubit the gate act.
        para (float): rotation angle
    """
    self.gates.append(RXGate(pos, para))
    return self

rxx(q1, q2, theta)

Rotation about 2-qubit XX axis.

Parameters:

Name Type Description Default
q1 int

qubit the gate act.

required
q2 int

qubit the gate act.

required
theta

rotation angle.

required
Source code in quafu\circuits\quantum_circuit.py
673
674
675
676
677
678
679
680
681
682
def rxx(self, q1: int, q2: int, theta):
    """
    Rotation about 2-qubit XX axis.
    Args:
        q1 (int): qubit the gate act.
        q2 (int): qubit the gate act.
        theta: rotation angle.

    """
    self.gates.append(RXXGate(q1, q2, theta))

ry(pos, para)

Single qubit rotation Ry gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
para float

rotation angle

required
Source code in quafu\circuits\quantum_circuit.py
505
506
507
508
509
510
511
512
513
514
def ry(self, pos: int, para: float) -> "QuantumCircuit":
    """
    Single qubit rotation Ry gate.

    Args:
        pos (int): qubit the gate act.
        para (float): rotation angle
    """
    self.gates.append(RYGate(pos, para))
    return self

ryy(q1, q2, theta)

Rotation about 2-qubit YY axis.

Parameters:

Name Type Description Default
q1 int

qubit the gate act.

required
q2 int

qubit the gate act.

required
theta

rotation angle.

required
Source code in quafu\circuits\quantum_circuit.py
684
685
686
687
688
689
690
691
692
693
def ryy(self, q1: int, q2: int, theta):
    """
    Rotation about 2-qubit YY axis.
    Args:
        q1 (int): qubit the gate act.
        q2 (int): qubit the gate act.
        theta: rotation angle.

    """
    self.gates.append(RYYGate(q1, q2, theta))

rz(pos, para)

Single qubit rotation Rz gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
para float

rotation angle

required
Source code in quafu\circuits\quantum_circuit.py
516
517
518
519
520
521
522
523
524
525
def rz(self, pos: int, para: float) -> "QuantumCircuit":
    """
    Single qubit rotation Rz gate.

    Args:
        pos (int): qubit the gate act.
        para (float): rotation angle
    """
    self.gates.append(RZGate(pos, para))
    return self

rzz(q1, q2, theta)

Rotation about 2-qubit ZZ axis.

Parameters:

Name Type Description Default
q1 int

qubit the gate act.

required
q2 int

qubit the gate act.

required
theta

rotation angle.

required
Source code in quafu\circuits\quantum_circuit.py
695
696
697
698
699
700
701
702
703
704
def rzz(self, q1: int, q2: int, theta):
    """
    Rotation about 2-qubit ZZ axis.
    Args:
        q1 (int): qubit the gate act.
        q2 (int): qubit the gate act.
        theta: rotation angle.

    """
    self.gates.append(RZZGate(q1, q2, theta))

s(pos)

S gate. (~√Z)

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
434
435
436
437
438
439
440
441
442
def s(self, pos: int) -> "QuantumCircuit":
    """
    S gate. (~√Z)

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(SGate(pos))
    return self

sdg(pos)

Sdg gate. (Inverse of S gate)

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
444
445
446
447
448
449
450
451
452
def sdg(self, pos: int) -> "QuantumCircuit":
    """
    Sdg gate. (Inverse of S gate)

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(SdgGate(pos))
    return self

sw(pos)

√W gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
484
485
486
487
488
489
490
491
492
def sw(self, pos: int) -> "QuantumCircuit":
    """
    √W gate.

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(SWGate(pos))
    return self

swap(q1, q2)

SWAP gate

Parameters:

Name Type Description Default
q1 int

qubit the gate act.

required
q2 int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
603
604
605
606
607
608
609
610
611
612
def swap(self, q1: int, q2: int) -> "QuantumCircuit":
    """
    SWAP gate

    Args:
        q1 (int): qubit the gate act.
        q2 (int): qubit the gate act.
    """
    self.gates.append(SwapGate(q1, q2))
    return self

sx(pos)

√X gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
454
455
456
457
458
459
460
461
462
def sx(self, pos: int) -> "QuantumCircuit":
    """
    √X gate.

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(SXGate(pos))
    return self

sy(pos)

√Y gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
464
465
466
467
468
469
470
471
472
def sy(self, pos: int) -> "QuantumCircuit":
    """
    √Y gate.

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(SYGate(pos))
    return self

t(pos)

T gate. (~Z^(1/4))

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
415
416
417
418
419
420
421
422
423
def t(self, pos: int) -> "QuantumCircuit":
    """
    T gate. (~Z^(1/4))

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(TGate(pos))
    return self

tdg(pos)

Tdg gate. (Inverse of T gate)

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
425
426
427
428
429
430
431
432
def tdg(self, pos: int) -> "QuantumCircuit":
    """
    Tdg gate. (Inverse of T gate)

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(TdgGate(pos))

to_openqasm()

Convert the circuit to openqasm text.

Returns: openqasm text.

Source code in quafu\circuits\quantum_circuit.py
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
def to_openqasm(self) -> str:
    """
    Convert the circuit to openqasm text.

    Returns: 
        openqasm text.
    """
    qasm = "OPENQASM 2.0;\ninclude \"qelib1.inc\";\n"
    qasm += "qreg q[%d];\n" % self.num
    qasm += "creg meas[%d];\n" % len(self.measures)
    for gate in self.gates:
        if isinstance(gate, SingleQubitGate):
            if isinstance(gate, ParaSingleQubitGate):
                if isinstance(gate.paras, Iterable):
                    qasm += "%s(" %gate.name.lower() + ",".join(["%s" %para for para in gate.paras]) + ") q[%d];\n" % (gate.pos)
                else:
                    qasm += "%s(%s) " %(gate.name.lower(), gate.paras) + "q[%d];\n" % (gate.pos)
            else:
                if gate.name == "SY":
                    qasm += "ry(pi/2) q[%d];\n" %(gate.pos)
                elif gate.name == "W":
                    qasm += "rz(-pi/4) q[%d];\nrx(pi) q[%d];\nrz(pi/4) q[%d];\n"  %(gate.pos, gate.pos, gate.pos)
                elif gate.name == "SW":
                    qasm += "rz(-pi/4) q[%d];\nrx(pi/2) q[%d];\nrz(pi/4) q[%d];\n"  %(gate.pos, gate.pos, gate.pos)
                else:
                    qasm += "%s q[%d];\n" % (gate.name.lower(), gate.pos)

        elif isinstance(gate, Delay):
            qasm += "delay(%d%s) q[%d];\n" % (gate.duration, gate.unit, gate.pos)
        elif isinstance(gate, XYResonance):
            qasm += "xy(%d%s) " %(gate.duration, gate.unit) + ",".join(["q[%d]" % p for p in range(min(gate.pos), max(gate.pos)+1)]) + ";\n"

        elif isinstance(gate, Barrier) or isinstance(gate, MultiQubitGate):
            if isinstance(gate, ParaMultiQubitGate) or (isinstance(gate, ControlledGate) and bool(gate.paras)):
                if isinstance(gate.paras, Iterable):
                    qasm += "%s(" %gate.name.lower() + ",".join(["%s" %para for para in gate.paras]) + ") " + ",".join(["q[%d]" % p for p in gate.pos]) + ";\n"
                else:
                     qasm += "%s(%s) " %(gate.name.lower(), gate.paras) + ",".join(["q[%d]" % p for p in gate.pos]) + ";\n"

            else:
                if gate.name == "CS":
                    qasm += "cp(pi/2) " + "q[%d],q[%d];\n" % (gate.pos[0], gate.pos[1])
                elif gate.name == "CT":
                    qasm += "cp(pi/4) " + "q[%d],q[%d];\n" % (gate.pos[0], gate.pos[1])
                elif gate.name == "barrier":
                    qasm += "barrier " + ",".join(["q[%d]" % p for p in range(min(gate.pos), max(gate.pos)+1)]) + ";\n"
                else:
                    qasm += "%s " %(gate.name.lower()) + ",".join(["q[%d]" % p for p in gate.pos]) + ";\n"

    for key in self.measures:
        qasm += "measure q[%d] -> meas[%d];\n" % (key, self.measures[key])

    self.openqasm = qasm
    return qasm

toffoli(ctrl1, ctrl2, targ)

Toffoli gate

Parameters:

Name Type Description Default
ctrl1 int

control qubit

required
ctrl2 int

control qubit

required
targ int

target qubit

required
Source code in quafu\circuits\quantum_circuit.py
614
615
616
617
618
619
620
621
622
623
624
def toffoli(self, ctrl1: int, ctrl2: int, targ: int) -> "QuantumCircuit":
    """
    Toffoli gate

    Args:
        ctrl1 (int): control qubit
        ctrl2 (int): control qubit
        targ (int): target qubit
    """
    self.gates.append(ToffoliGate(ctrl1, ctrl2, targ))
    return self

w(pos)

W gate. (~(X + Y)/√2)

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
474
475
476
477
478
479
480
481
482
def w(self, pos: int) -> "QuantumCircuit":
    """
    W gate. (~(X + Y)/√2)

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(WGate(pos))
    return self

x(pos)

X gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
385
386
387
388
389
390
391
392
393
def x(self, pos: int) -> "QuantumCircuit":
    """
    X gate.

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(XGate(pos))
    return self

xy(qs, qe, duration, unit='ns')

XY resonance time evolution for quantum simulator

Parameters:

Name Type Description Default
qs int

start position of resonant qubits.

required
qe int

end position of resonant qubits.

required
duration int

duration must be integer, which represents integer times of unit.

required
unit str

time unit of duration.

'ns'
Source code in quafu\circuits\quantum_circuit.py
660
661
662
663
664
665
666
667
668
669
670
671
def xy(self, qs: int, qe: int, duration: int, unit: str="ns") -> "QuantumCircuit":
    """
    XY resonance time evolution for quantum simulator
    Args:
        qs: start position of resonant qubits.
        qe: end position of resonant qubits.
        duration: duration must be integer, which represents integer times of unit.
        unit: time unit of duration.

    """
    self.gates.append(XYResonance(qs, qe, duration, unit=unit))
    return self

y(pos)

Y gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
395
396
397
398
399
400
401
402
403
def y(self, pos: int) -> "QuantumCircuit":
    """
    Y gate.

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(YGate(pos))
    return self

z(pos)

Z gate.

Parameters:

Name Type Description Default
pos int

qubit the gate act.

required
Source code in quafu\circuits\quantum_circuit.py
405
406
407
408
409
410
411
412
413
def z(self, pos: int) -> "QuantumCircuit":
    """
    Z gate.

    Args:
        pos (int): qubit the gate act.
    """
    self.gates.append(ZGate(pos))
    return self

SimuResult

Bases: Result

Class that save the execute simulation results returned from classical simulator.

Attributes:

Name Type Description
num int

Numbers of measured qubits

probabilities ndarray

Calculated probabilities on each bitstring.

rho ndarray

Simulated density matrix of measured qubits

Source code in quafu\results\results.py
 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
class SimuResult(Result):
    """
    Class that save the execute simulation results returned from classical simulator.

    Attributes:
        num (int): Numbers of measured qubits
        probabilities (ndarray): Calculated probabilities on each bitstring.
        rho (ndarray): Simulated density matrix of measured qubits
    """
    def __init__(self, input, input_form):
        self.num = int(np.log2(input.shape[0]))
        if input_form == "density_matrix":
            self.rho = np.array(input)
            self.probabilities = np.diag(input)
        elif input_form == "probabilities":
            self.probabilities = input
        elif input_form == "state_vector":
            self.state_vector = input            

    def plot_probabilities(self, full: bool=False, reverse_basis: bool=False, sort: bool=None):
        """
        Plot the probabilities from simulated results, ordered in big endian convention.

        Args:
            full: Whether plot on the full basis of measured qubits.
            reverse_basis: Whether reverse the bitstring of basis. (Little endian convention).
            sort:  Sort the results by probabilities values. Can be `"ascend"` order or `"descend"` order. 
        """

        from ..utils.basis import get_basis
        probs = self.probabilities
        inds = range(len(probs))
        if not full:
            inds = np.where(self.probabilities > 1e-14)[0]
            probs = self.probabilities[inds]

        basis=np.array([bin(i)[2:].zfill(self.num) for i in inds])
        if reverse_basis:
            basis=np.array([bin(i)[2:].zfill(self.num)[::-1] for i in inds])

        if sort == "ascend":
            orders = np.argsort(probs)
            probs = probs[orders]
            basis = basis[orders]
        elif sort == "descend":
            orders = np.argsort(probs)
            probs = probs[orders][::-1]
            basis = basis[orders][::-1]


        plt.figure()
        plt.bar(inds, probs, tick_label=basis)
        plt.xticks(rotation=70)
        plt.ylabel("probabilities")

    def get_statevector(self):
        return self.state_vector

    def calculate_obs(self, pos):
        "Calculate observables Z on input position using probabilities"
        inds = np.where(self.probabilities > 1e-14)[0]
        probs = self.probabilities[inds]
        basis=np.array([bin(i)[2:].zfill(self.num) for i in inds])
        res_reduced = dict(zip(basis, probs))
        return measure_obs(pos, res_reduced)

calculate_obs(pos)

Calculate observables Z on input position using probabilities

Source code in quafu\results\results.py
127
128
129
130
131
132
133
def calculate_obs(self, pos):
    "Calculate observables Z on input position using probabilities"
    inds = np.where(self.probabilities > 1e-14)[0]
    probs = self.probabilities[inds]
    basis=np.array([bin(i)[2:].zfill(self.num) for i in inds])
    res_reduced = dict(zip(basis, probs))
    return measure_obs(pos, res_reduced)

plot_probabilities(full=False, reverse_basis=False, sort=None)

Plot the probabilities from simulated results, ordered in big endian convention.

Parameters:

Name Type Description Default
full bool

Whether plot on the full basis of measured qubits.

False
reverse_basis bool

Whether reverse the bitstring of basis. (Little endian convention).

False
sort bool

Sort the results by probabilities values. Can be "ascend" order or "descend" order.

None
Source code in quafu\results\results.py
 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
def plot_probabilities(self, full: bool=False, reverse_basis: bool=False, sort: bool=None):
    """
    Plot the probabilities from simulated results, ordered in big endian convention.

    Args:
        full: Whether plot on the full basis of measured qubits.
        reverse_basis: Whether reverse the bitstring of basis. (Little endian convention).
        sort:  Sort the results by probabilities values. Can be `"ascend"` order or `"descend"` order. 
    """

    from ..utils.basis import get_basis
    probs = self.probabilities
    inds = range(len(probs))
    if not full:
        inds = np.where(self.probabilities > 1e-14)[0]
        probs = self.probabilities[inds]

    basis=np.array([bin(i)[2:].zfill(self.num) for i in inds])
    if reverse_basis:
        basis=np.array([bin(i)[2:].zfill(self.num)[::-1] for i in inds])

    if sort == "ascend":
        orders = np.argsort(probs)
        probs = probs[orders]
        basis = basis[orders]
    elif sort == "descend":
        orders = np.argsort(probs)
        probs = probs[orders][::-1]
        basis = basis[orders][::-1]


    plt.figure()
    plt.bar(inds, probs, tick_label=basis)
    plt.xticks(rotation=70)
    plt.ylabel("probabilities")

Task

Bases: object

Class for submitting quantum computation task to the backend.

Attributes:

Name Type Description
token str

Apitoken that associate to your Quafu account.

shots int

Numbers of single shot measurement.

compile bool

Whether compile the circuit on the backend

tomo bool

Whether do tomography (Not support yet)

Source code in quafu\tasks\tasks.py
 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
class Task(object): 
    """
    Class for submitting quantum computation task to the backend.

    Attributes:
        token (str): Apitoken that associate to your Quafu account.
        shots (int): Numbers of single shot measurement.
        compile (bool): Whether compile the circuit on the backend
        tomo (bool): Whether do tomography (Not support yet)
    """
    def __init__(self):   
        self.shots = 1000
        self.tomo = False
        self.compile = True
        self.priority = 2
        self.submit_history = { }
        self.token, self._url = load_account()
        self._available_backends = {info["system_name"]:Backend(info) for info in get_backends_info()}
        self.backend = self._available_backends[list(self._available_backends.keys())[0]]

    def config(self, 
               backend: str="ScQ-P10", 
               shots: int=1000, 
               compile: bool=True,
               tomo: bool=False,
               priority: int=2) -> None:
        """
        Configure the task properties

        Args:
            backend: Select the experimental backend.
            shots: Numbers of single shot measurement.
            compile: Whether compile the circuit on the backend
            tomo:  Whether do tomography (Not support yet)
            priority: Task priority.
        """
        if backend not in self._available_backends.keys():
            raise UserError("backend %s is not valid, available backends are "%backend+", ".join(self._available_backends.keys()))

        self.backend = self._available_backends[backend]
        self.shots = shots
        self.tomo = tomo
        self.compile = compile
        self.priority = priority


    def get_history(self) -> Dict:
        """
        Get the history of submitted task.
        Returns:
            A dict of history. The key is the group name and the value is a list of task id in the group. 
        """
        return self.submit_history

    def get_backend_info(self) -> Dict:
        """
        Get the calibration information of the experimental backend.

        Returns: 
            Backend information dictionary containing the mapping from the indices to the names of physical bits `'mapping'`, backend topology  `'topology_diagram'` and full calibration inforamtion `'full_info'`.
        """
        return self.backend.get_chip_info()

    def submit(self,
               qc: QuantumCircuit,
               obslist: List=[])\
                -> Tuple[List[ExecResult], List[int]]:
        """
        Execute the circuit with observable expectation measurement task.
        Args:
            qc (QuantumCircuit): Quantum circuit that need to be executed on backend.
            obslist (list[str, list[int]]): List of pauli string and its position.

        Returns: 
            List of executed results and list of measured observable

        Examples: 
            1) input [["XYX", [0, 1, 2]], ["Z", [1]]] measure pauli operator XYX at 0, 1, 2 qubit, and Z at 1 qubit.\n
            2) Measure 5-qubit Ising Hamiltonian we can use\n
            obslist = [["X", [i]] for i in range(5)]]\n
            obslist.extend([["ZZ", [i, i+1]] for i in range(4)])\n

        For the energy expectation of Ising Hamiltonian \n
        res, obsexp = q.submit_task(obslist)\n
        E = sum(obsexp)
        """
        # save input circuit
        inputs = copy.deepcopy(qc.gates)
        measures = list(qc.measures.keys())
        if len(obslist) == 0:
            print("No observable measurement task.")
            res = self.run(qc)
            return res, []

        else:
            for obs in obslist:
                for p in obs[1]:
                    if p not in measures:
                        raise CircuitError("Qubit %d in observer %s is not measured." % (p, obs[0]))

            measure_basis, targlist = merge_measure(obslist)
            print("Job start, need measured in ", measure_basis)

            exec_res = []
            for measure_base in measure_basis:
                res = self.run(qc, measure_base=measure_base)
                qc.gates = copy.deepcopy(inputs)
                exec_res.append(res)

            measure_results = []
            for obi in range(len(obslist)):
                obs = obslist[obi]
                rpos = [measures.index(p) for p in obs[1]]
                measure_results.append(exec_res[targlist[obi]].calculate_obs(rpos))

        return exec_res, measure_results

    def run(self, 
            qc: QuantumCircuit, 
            measure_base: List=[]) -> ExecResult:
        """Single run for measurement task.

        Args:
            qc (QuantumCircuit): Quantum circuit that need to be executed on backend.
            measure_base (list[str, list[int]]): measure base and it position.
        """
        if len(measure_base) == 0:
            res = self.send(qc)
            res.measure_base = ''

        else:
            for base, pos in zip(measure_base[0], measure_base[1]):
                if base == "X":
                    qc.ry(pos, -np.pi / 2)
                elif base == "Y":
                    qc.rx(pos, np.pi / 2)

            res = self.send(qc)
            res.measure_base = measure_base

        return res

    def send(self, 
             qc: QuantumCircuit, 
             name: str="", 
             group: str="",
            wait: bool=True) -> ExecResult:
        """
        Run the circuit on experimental device.

        Args:
            qc: Quantum circuit that need to be executed on backend.
            name: Task name.
            group: The task belong which group.
            wait: Whether wait until the execution return.
        Returns: 
            ExecResult object that contain the dict return from quantum device.
        """
        from quafu import get_version
        version = get_version()
        if qc.num > self.backend.qubit_num:
            raise CircuitError("The qubit number %d is too large for backend %s which has %d qubits" %(qc.num, self.backend.name, self.backend.qubit_num))

        self.check_valid_gates(qc)
        qc.to_openqasm()
        data = {"qtasm": qc.openqasm, "shots": self.shots, "qubits": qc.num, "scan": 0,
                "tomo": int(self.tomo), "selected_server": self.backend.system_id,
                "compile": int(self.compile), "priority": self.priority, "task_name": name, "pyquafu_version": version}

        if wait:
            url = self._url  + "qbackend/scq_kit/"
        else:
            url = self._url + "qbackend/scq_kit_asyc/"

        headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'api_token': self.token}
        data = parse.urlencode(data)
        data = data.replace("%27", "'")
        res = requests.post(url, headers=headers, data=data)
        res_dict = json.loads(res.text)

        if res.json()["status"] in [201, 205]:
            raise UserError(res_dict["message"])
        elif res.json()["status"] == 5001:
            raise CircuitError(res_dict["message"])
        elif res.json()["status"] == 5003:
            raise ServerError(res_dict["message"])
        elif res.json()["status"] == 5004:
            raise CompileError(res_dict["message"]) 
        else:
            task_id = res_dict["task_id"]

            if not (group in self.submit_history):
                self.submit_history[group] = [task_id]
            else:
                self.submit_history[group].append(task_id)

            return ExecResult(res_dict, qc.measures)

    def retrieve(self, taskid: str) -> ExecResult:
        """
        Retrieve the results of submited task by taskid.

        Args:
            taskid: The taskid of the task need to be retrieved.
        """
        data = {"task_id" : taskid}
        url = self._url  + "qbackend/scq_task_recall/"

        headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'api_token': self.token}
        res = requests.post(url, headers=headers, data=data)

        res_dict = json.loads(res.text)
        measures = eval(res_dict["measure"])

        return ExecResult(res_dict, measures)

    def retrieve_group(self, 
                       group: str,
                       history: Dict={}, 
                       verbose: bool=True) -> List[ExecResult]:
        """
        Retrieve the results of submited task by group name.

        Args:
            group: The group name need to be retrieved.
            history: History from which to retrieve the results. If not provided, the history will be the submit history of saved by current task.
            verbose: Whether print the task status in the group.
        Returns:
            A list of execution results in the retrieved group. Only completed task will be added. 
        """
        history = history if history else self.submit_history
        taskids = history[group]

        group_res = []
        if verbose:
            group = group if group else "Untitled group"
            print("Group: ", group)
            print((" "*5).join(["task_id".ljust(16), "task_name".ljust(10),   "status".ljust(10)]))
        for taskid in taskids:
            res = self.retrieve(taskid)
            taskname = res.taskname
            if verbose:
                taskname = taskname if taskname else "Untitled"
                print((" "*5).join([("%s" %res.taskid).ljust(16), ("%s" %taskname).ljust(10), ("%s" %res.task_status).ljust(10)]))
            if res.task_status == "Completed":
                group_res.append(res)

        return group_res


    def check_valid_gates(self, qc: QuantumCircuit) -> None:
        """
        Check the validity of the quantum circuit.
        Args:
            qc: QuantumCicuit object that need to be checked.
        """
        if not self.compile:
            valid_gates = self.backend.get_valid_gates()
            for gate in qc.gates:
                if gate.name.lower() not in valid_gates:
                    raise CircuitError("Invalid operations '%s' for backend '%s'" %(gate.name, self.backend.name))

        else:
            if self.backend.name == "ScQ-S41":
                raise CircuitError("Backend ScQ-S41 must be used without compilation")
            if self.backend.name == "ScQ-P136":
                for gate in qc.gates:
                    if gate.name.lower() in ["xy"]:
                        raise CircuitError("Invalid operations '%s' for backend '%s'" %(gate.name, self.backend.name))

check_valid_gates(qc)

Check the validity of the quantum circuit.

Parameters:

Name Type Description Default
qc QuantumCircuit

QuantumCicuit object that need to be checked.

required
Source code in quafu\tasks\tasks.py
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
def check_valid_gates(self, qc: QuantumCircuit) -> None:
    """
    Check the validity of the quantum circuit.
    Args:
        qc: QuantumCicuit object that need to be checked.
    """
    if not self.compile:
        valid_gates = self.backend.get_valid_gates()
        for gate in qc.gates:
            if gate.name.lower() not in valid_gates:
                raise CircuitError("Invalid operations '%s' for backend '%s'" %(gate.name, self.backend.name))

    else:
        if self.backend.name == "ScQ-S41":
            raise CircuitError("Backend ScQ-S41 must be used without compilation")
        if self.backend.name == "ScQ-P136":
            for gate in qc.gates:
                if gate.name.lower() in ["xy"]:
                    raise CircuitError("Invalid operations '%s' for backend '%s'" %(gate.name, self.backend.name))

config(backend='ScQ-P10', shots=1000, compile=True, tomo=False, priority=2)

Configure the task properties

Parameters:

Name Type Description Default
backend str

Select the experimental backend.

'ScQ-P10'
shots int

Numbers of single shot measurement.

1000
compile bool

Whether compile the circuit on the backend

True
tomo bool

Whether do tomography (Not support yet)

False
priority int

Task priority.

2
Source code in quafu\tasks\tasks.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def config(self, 
           backend: str="ScQ-P10", 
           shots: int=1000, 
           compile: bool=True,
           tomo: bool=False,
           priority: int=2) -> None:
    """
    Configure the task properties

    Args:
        backend: Select the experimental backend.
        shots: Numbers of single shot measurement.
        compile: Whether compile the circuit on the backend
        tomo:  Whether do tomography (Not support yet)
        priority: Task priority.
    """
    if backend not in self._available_backends.keys():
        raise UserError("backend %s is not valid, available backends are "%backend+", ".join(self._available_backends.keys()))

    self.backend = self._available_backends[backend]
    self.shots = shots
    self.tomo = tomo
    self.compile = compile
    self.priority = priority

get_backend_info()

Get the calibration information of the experimental backend.

Returns: Backend information dictionary containing the mapping from the indices to the names of physical bits 'mapping', backend topology 'topology_diagram' and full calibration inforamtion 'full_info'.

Source code in quafu\tasks\tasks.py
73
74
75
76
77
78
79
80
def get_backend_info(self) -> Dict:
    """
    Get the calibration information of the experimental backend.

    Returns: 
        Backend information dictionary containing the mapping from the indices to the names of physical bits `'mapping'`, backend topology  `'topology_diagram'` and full calibration inforamtion `'full_info'`.
    """
    return self.backend.get_chip_info()

get_history()

Get the history of submitted task.

Returns:

Type Description
Dict

A dict of history. The key is the group name and the value is a list of task id in the group.

Source code in quafu\tasks\tasks.py
65
66
67
68
69
70
71
def get_history(self) -> Dict:
    """
    Get the history of submitted task.
    Returns:
        A dict of history. The key is the group name and the value is a list of task id in the group. 
    """
    return self.submit_history

retrieve(taskid)

Retrieve the results of submited task by taskid.

Parameters:

Name Type Description Default
taskid str

The taskid of the task need to be retrieved.

required
Source code in quafu\tasks\tasks.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
def retrieve(self, taskid: str) -> ExecResult:
    """
    Retrieve the results of submited task by taskid.

    Args:
        taskid: The taskid of the task need to be retrieved.
    """
    data = {"task_id" : taskid}
    url = self._url  + "qbackend/scq_task_recall/"

    headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'api_token': self.token}
    res = requests.post(url, headers=headers, data=data)

    res_dict = json.loads(res.text)
    measures = eval(res_dict["measure"])

    return ExecResult(res_dict, measures)

retrieve_group(group, history={}, verbose=True)

Retrieve the results of submited task by group name.

Parameters:

Name Type Description Default
group str

The group name need to be retrieved.

required
history Dict

History from which to retrieve the results. If not provided, the history will be the submit history of saved by current task.

{}
verbose bool

Whether print the task status in the group.

True

Returns:

Type Description
List[ExecResult]

A list of execution results in the retrieved group. Only completed task will be added.

Source code in quafu\tasks\tasks.py
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
def retrieve_group(self, 
                   group: str,
                   history: Dict={}, 
                   verbose: bool=True) -> List[ExecResult]:
    """
    Retrieve the results of submited task by group name.

    Args:
        group: The group name need to be retrieved.
        history: History from which to retrieve the results. If not provided, the history will be the submit history of saved by current task.
        verbose: Whether print the task status in the group.
    Returns:
        A list of execution results in the retrieved group. Only completed task will be added. 
    """
    history = history if history else self.submit_history
    taskids = history[group]

    group_res = []
    if verbose:
        group = group if group else "Untitled group"
        print("Group: ", group)
        print((" "*5).join(["task_id".ljust(16), "task_name".ljust(10),   "status".ljust(10)]))
    for taskid in taskids:
        res = self.retrieve(taskid)
        taskname = res.taskname
        if verbose:
            taskname = taskname if taskname else "Untitled"
            print((" "*5).join([("%s" %res.taskid).ljust(16), ("%s" %taskname).ljust(10), ("%s" %res.task_status).ljust(10)]))
        if res.task_status == "Completed":
            group_res.append(res)

    return group_res

run(qc, measure_base=[])

Single run for measurement task.

Parameters:

Name Type Description Default
qc QuantumCircuit

Quantum circuit that need to be executed on backend.

required
measure_base list[str, list[int]]

measure base and it position.

[]
Source code in quafu\tasks\tasks.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def run(self, 
        qc: QuantumCircuit, 
        measure_base: List=[]) -> ExecResult:
    """Single run for measurement task.

    Args:
        qc (QuantumCircuit): Quantum circuit that need to be executed on backend.
        measure_base (list[str, list[int]]): measure base and it position.
    """
    if len(measure_base) == 0:
        res = self.send(qc)
        res.measure_base = ''

    else:
        for base, pos in zip(measure_base[0], measure_base[1]):
            if base == "X":
                qc.ry(pos, -np.pi / 2)
            elif base == "Y":
                qc.rx(pos, np.pi / 2)

        res = self.send(qc)
        res.measure_base = measure_base

    return res

send(qc, name='', group='', wait=True)

Run the circuit on experimental device.

Parameters:

Name Type Description Default
qc QuantumCircuit

Quantum circuit that need to be executed on backend.

required
name str

Task name.

''
group str

The task belong which group.

''
wait bool

Whether wait until the execution return.

True

Returns: ExecResult object that contain the dict return from quantum device.

Source code in quafu\tasks\tasks.py
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
def send(self, 
         qc: QuantumCircuit, 
         name: str="", 
         group: str="",
        wait: bool=True) -> ExecResult:
    """
    Run the circuit on experimental device.

    Args:
        qc: Quantum circuit that need to be executed on backend.
        name: Task name.
        group: The task belong which group.
        wait: Whether wait until the execution return.
    Returns: 
        ExecResult object that contain the dict return from quantum device.
    """
    from quafu import get_version
    version = get_version()
    if qc.num > self.backend.qubit_num:
        raise CircuitError("The qubit number %d is too large for backend %s which has %d qubits" %(qc.num, self.backend.name, self.backend.qubit_num))

    self.check_valid_gates(qc)
    qc.to_openqasm()
    data = {"qtasm": qc.openqasm, "shots": self.shots, "qubits": qc.num, "scan": 0,
            "tomo": int(self.tomo), "selected_server": self.backend.system_id,
            "compile": int(self.compile), "priority": self.priority, "task_name": name, "pyquafu_version": version}

    if wait:
        url = self._url  + "qbackend/scq_kit/"
    else:
        url = self._url + "qbackend/scq_kit_asyc/"

    headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'api_token': self.token}
    data = parse.urlencode(data)
    data = data.replace("%27", "'")
    res = requests.post(url, headers=headers, data=data)
    res_dict = json.loads(res.text)

    if res.json()["status"] in [201, 205]:
        raise UserError(res_dict["message"])
    elif res.json()["status"] == 5001:
        raise CircuitError(res_dict["message"])
    elif res.json()["status"] == 5003:
        raise ServerError(res_dict["message"])
    elif res.json()["status"] == 5004:
        raise CompileError(res_dict["message"]) 
    else:
        task_id = res_dict["task_id"]

        if not (group in self.submit_history):
            self.submit_history[group] = [task_id]
        else:
            self.submit_history[group].append(task_id)

        return ExecResult(res_dict, qc.measures)

submit(qc, obslist=[])

Execute the circuit with observable expectation measurement task.

Parameters:

Name Type Description Default
qc QuantumCircuit

Quantum circuit that need to be executed on backend.

required
obslist list[str, list[int]]

List of pauli string and its position.

[]

Returns: List of executed results and list of measured observable

Examples: 1) input [["XYX", [0, 1, 2]], ["Z", [1]]] measure pauli operator XYX at 0, 1, 2 qubit, and Z at 1 qubit.

2) Measure 5-qubit Ising Hamiltonian we can use

obslist = [["X", [i]] for i in range(5)]]

obslist.extend([["ZZ", [i, i+1]] for i in range(4)])

For the energy expectation of Ising Hamiltonian

res, obsexp = q.submit_task(obslist)

E = sum(obsexp)

Source code in quafu\tasks\tasks.py
 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
def submit(self,
           qc: QuantumCircuit,
           obslist: List=[])\
            -> Tuple[List[ExecResult], List[int]]:
    """
    Execute the circuit with observable expectation measurement task.
    Args:
        qc (QuantumCircuit): Quantum circuit that need to be executed on backend.
        obslist (list[str, list[int]]): List of pauli string and its position.

    Returns: 
        List of executed results and list of measured observable

    Examples: 
        1) input [["XYX", [0, 1, 2]], ["Z", [1]]] measure pauli operator XYX at 0, 1, 2 qubit, and Z at 1 qubit.\n
        2) Measure 5-qubit Ising Hamiltonian we can use\n
        obslist = [["X", [i]] for i in range(5)]]\n
        obslist.extend([["ZZ", [i, i+1]] for i in range(4)])\n

    For the energy expectation of Ising Hamiltonian \n
    res, obsexp = q.submit_task(obslist)\n
    E = sum(obsexp)
    """
    # save input circuit
    inputs = copy.deepcopy(qc.gates)
    measures = list(qc.measures.keys())
    if len(obslist) == 0:
        print("No observable measurement task.")
        res = self.run(qc)
        return res, []

    else:
        for obs in obslist:
            for p in obs[1]:
                if p not in measures:
                    raise CircuitError("Qubit %d in observer %s is not measured." % (p, obs[0]))

        measure_basis, targlist = merge_measure(obslist)
        print("Job start, need measured in ", measure_basis)

        exec_res = []
        for measure_base in measure_basis:
            res = self.run(qc, measure_base=measure_base)
            qc.gates = copy.deepcopy(inputs)
            exec_res.append(res)

        measure_results = []
        for obi in range(len(obslist)):
            obs = obslist[obi]
            rpos = [measures.index(p) for p in obs[1]]
            measure_results.append(exec_res[targlist[obi]].calculate_obs(rpos))

    return exec_res, measure_results

User

Bases: object

Source code in quafu\users\userapi.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class User(object):
    def __init__(self):
        self.apitoken = ""

    def save_apitoken(self, apitoken):
        """
        Save your apitoken associate your Quafu account.
        """
        self.apitoken = apitoken
        homedir = get_homedir()
        file_dir = homedir + "/.quafu/"
        if not os.path.exists(file_dir):
            os.mkdir(file_dir)
        with open(file_dir + "api", "w") as f:
            f.write(self.apitoken+"\n")
            f.write("http://quafu.baqis.ac.cn/")

save_apitoken(apitoken)

Save your apitoken associate your Quafu account.

Source code in quafu\users\userapi.py
12
13
14
15
16
17
18
19
20
21
22
23
def save_apitoken(self, apitoken):
    """
    Save your apitoken associate your Quafu account.
    """
    self.apitoken = apitoken
    homedir = get_homedir()
    file_dir = homedir + "/.quafu/"
    if not os.path.exists(file_dir):
        os.mkdir(file_dir)
    with open(file_dir + "api", "w") as f:
        f.write(self.apitoken+"\n")
        f.write("http://quafu.baqis.ac.cn/")

simulate(qc, psi=np.array([]), simulator='qfvm_circ', output='probabilities')

Simulate quantum circuit

Parameters:

Name Type Description Default
qc Union[QuantumCircuit, str]

quantum circuit or qasm string that need to be simulated.

required
psi

Input state vector

np.array([])
simulator str

"qfvm_circ": The high performance C++ circuit simulator. "py_simu": Python implemented simulator by sparse matrix with low performace for large scale circuit. "qfvm_qasm": The high performance C++ qasm simulator with limited gate set.

'qfvm_circ'
output str

"probabilities": Return probabilities on measured qubits, ordered in big endian convention. "density_matrix": Return reduced density_amtrix on measured qubits, ordered in big endian convention. "state_vector: Return original full statevector. The statevector returned by qfvm backend is ordered in little endian convention (same as qiskit), while py_simu backend is orderd in big endian convention.

'probabilities'

Returns:

Type Description
SimuResult

SimuResult object that contain the results.

Source code in quafu\simulators\simulator.py
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
def simulate(qc : Union[QuantumCircuit, str], psi : np.ndarray= np.array([]), simulator:str="qfvm_circ", output: str="probabilities")-> SimuResult:
    """Simulate quantum circuit
    Args:
        qc: quantum circuit or qasm string that need to be simulated.
        psi : Input state vector
        simulator:`"qfvm_circ"`: The high performance C++ circuit simulator. 
                `"py_simu"`: Python implemented simulator by sparse matrix with low performace for large scale circuit.
                `"qfvm_qasm"`: The high performance C++ qasm simulator with limited gate set.

        output: `"probabilities"`: Return probabilities on measured qubits, ordered in big endian convention.
                `"density_matrix"`: Return reduced density_amtrix on measured qubits, ordered in big endian convention.
                `"state_vector`: Return original full statevector. The statevector returned by `qfvm` backend is ordered in little endian convention (same as qiskit), while `py_simu` backend is orderd in big endian convention.
    Returns:
        SimuResult object that contain the results.
"""
    qasm = ""
    if simulator == "qfvm_qasm":
        if not isinstance(qc, str):
            raise ValueError("Must input valid qasm str for qfvm_qasm simulator")

        qasm = qc
        qc = QuantumCircuit(0)
        qc.from_openqasm(qasm)

    measures = [qc.used_qubits.index(i) for i in qc.measures.keys()]
    num = 0
    if simulator == "qfvm_circ":
        num = max(qc.used_qubits)+1
        measures = list(qc.measures.keys())
        psi = simulate_circuit(qc, psi)

    elif simulator ==  "py_simu":
        psi = py_simulate(qc, psi)
    elif simulator == "qfvm_qasm":
        num = qc.num
        measures = list(qc.measures.keys())
        psi = execute(qasm)      
    else:
        raise ValueError("invalid circuit")


    if output == "density_matrix":
        if simulator in ["qfvm_circ", "qfvm_qasm"]:
            psi = permutebits(psi, range(num)[::-1])
        rho = ptrace(psi, measures, diag=False)
        rho = permutebits(rho, list(qc.measures.values()))
        return SimuResult(rho, output)

    elif output == "probabilities":
        if simulator in ["qfvm_circ", "qfvm_qasm"]:
            psi = permutebits(psi, range(num)[::-1])
        probabilities = ptrace(psi, measures)
        probabilities = permutebits(probabilities, list(qc.measures.values()))
        return SimuResult(probabilities, output) 

    elif output == "state_vector":
        return SimuResult(psi, output)

    else:
        raise ValueError("output should in be 'density_matrix', 'probabilities', or 'state_vector'")