File size: 5,347 Bytes
91d3e9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from typing import Dict
from qiskit import QuantumCircuit, Aer, execute
from quantum_perceptron.utils import (
    assert_negative,
    assert_bits,
    create_hypergraph_state,
    get_vector_from_int
)


class Perceptron:
    def __init__(self,
                 num_qubits: int,
                 weight: int = 1,
                 input: int = 1):
        """
        This class creates a quantum perceptron instance which has
        capability calculate input * weight. Note that we are not applying
        any non-linearity. Our perceptron design is as per
        https://arxiv.org/pdf/1811.02266.pdf

        Args:
          num_qubits: `int` denoting number of qubits in perceptron
          weight: `int` denoting the weight of the perceptron.
          input: `int` denoting the data to input to the perceptron.
        """
        self.num_qubits = num_qubits
        assert self.num_qubits > 0, "Number qubits must be positive"
        assert_negative(weight)
        self.weight = weight
        assert_negative(input)
        self.input = input
        assert_bits(self.weight, self.num_qubits)
        assert_bits(self.input, self.num_qubits)
        self.build_flag = False
        self.build_circuit()

    def Ui(self):
        """
        Sub-circuit to transform input data.
        """
        if not self.build_flag:
            raise RuntimeError("Ui() cannot be called independently.")

        Ui = QuantumCircuit(self.num_qubits)

        # Applying hadamard to first num_qubits
        for q in range(self.num_qubits):
            Ui.h(q)

        # Extracting vectors for input
        input_vector = get_vector_from_int(self.input, self.num_qubits)

        # Applying hypergraph state corresponding to input.
        Ui = create_hypergraph_state(Ui,
                                     input_vector,
                                     self.num_qubits)
        Ui = Ui.to_gate()
        Ui.name = "U_i"
        return Ui

    def Uw(self):
        """
        Sub-circuit to transform weight data.
        """
        if not self.build_flag:
            raise RuntimeError("Ui() cannot be called independently.")

        Uw = QuantumCircuit(self.num_qubits)

        # Extracting vectors for weight
        input_vector = get_vector_from_int(self.weight, self.num_qubits)

        # Applying hypergraph state corresponding to weight.
        Uw = create_hypergraph_state(Uw,
                                     input_vector,
                                     self.num_qubits)

        # Applying hadamard to first num_qubits
        for q in range(self.num_qubits):
            Uw.h(q)

        # Applying X gate to first num_qubits
        for q in range(self.num_qubits):
            Uw.x(q)
        Uw = Uw.to_gate()
        Uw.name = "U_w"
        return Uw

    def build_circuit(self):
        """
        Build quantum circuit corresponding to single perceptron combining
        input data and weight of the perceptron.
        """
        # Creating circuit with num_qubits + 1 (ancilla) qubit.
        self.circuit = QuantumCircuit(1 + self.num_qubits, 1)

        def toggle_build_flag():
            """
            Toggle the build circuit flag. Used to monitor Ui and Uf circuits
            to ensure that those functions are not called seperately but from
            the `build_circuit()` function.
            """
            self.build_flag = not self.build_flag

        # Append Ui for processing input
        toggle_build_flag()
        # self.Ui()
        self.circuit.append(
            self.Ui(),
            list(range(self.num_qubits))
        )
        toggle_build_flag()

        # Append Uf for processing input
        toggle_build_flag()
        self.circuit.append(
            self.Uw(),
            list(range(self.num_qubits))
        )
        toggle_build_flag()

        # Toffoli gate at the end with target as ancilla qubit
        self.circuit.mcx(
            control_qubits=list(range(self.num_qubits)),
            target_qubit=self.num_qubits
        )

        # Measure the last qubit.
        self.circuit.measure(self.num_qubits, 0)

    def measure_circuit(self, num_iters: int = 1000) -> Dict[str, int]:
        """
        Measure the perceptron and get the counts of the final results.

        Args:
          num_iters: `int` denoting number of iterations to execute circuit.

        Returns: `dict` containing the measurement frequencies.
        """
        if not hasattr(self, 'circuit'):
            raise RuntimeError("The circuit hasn't yet built.",
                               "Please call build_circuit() first.")
        backend = Aer.get_backend('qasm_simulator')

        # Execute the circuit
        job = execute(self.circuit, backend, shots=num_iters)

        # Get result and counts
        result = job.result()
        counts = result.get_counts(self.circuit)
        return dict(counts)

    def save_circuit_image(self,
                           file_path: str,
                           output_format: str = "mpl"):
        """
        Save circuit to the image file.
        """
        if not hasattr(self, 'circuit'):
            raise RuntimeError("The circuit hasn't yet built.",
                               "Please call build_circuit() first.")
        self.circuit.draw(output=output_format, filename=file_path)