LoginSignup
1
0

More than 1 year has passed since last update.

【量子機械学習】cirqでHHL

Last updated at Posted at 2022-10-27
!pip install cirq==0.8.2
class HHL:
    def __init__(
        self,
        hamiltonian,
        initial_state=None,
        initial_state_transforms=None,
        qpe_register_size=4,
        C=None,
        t=1
    ):
        self.hamiltonian = hamiltonian
        self.initial_state = initial_state
        self.initial_state_transforms = initial_state_transforms
        self.qpe_register_size = qpe_register_size
        self.C = C
        self.t = t

        const = self.t/np.pi
        self.t = const*np.pi
        if self.C is None:
            self.C = 2*np.pi / (2**self.qpe_register_size * t)

    def build_hhl_circuit(self):
        self.circuit = cirq.Circuit()
        self.ancilla_qubit = cirq.LineQubit(0)
        self.qpe_register = [cirq.LineQubit(i) for i in range(1, self.qpe_register_size+1)]
        if self.initial_state is None:
            self.initial_state_size = int(np.log2(self.hamiltonian.shape[0]))
            if self.initial_state_size == 1:
                self.initial_state = [cirq.LineQubit(self.qpe_register_size + 1)]
            else:
                self.initial_state = [cirq.LineQubit(i) for i in range(self.qpe_register_size + 1,self.qpe_register_size + 1 + self.initial_state_size)]

        for op in list(self.initial_state_transforms):
            self.circuit.append(op(self.initial_state[0]))
        
        self.U = HamiltonianSimulation(_H_=self.hamiltonian, t=self.t)
        _qpe_ = QuantumPhaseEstimation(input_qubits=self.initial_state, output_qubits=self.qpe_register, U=self.U)
        _qpe_.circuit()
        self.circuit += _qpe_.circuit
        
        _eig_val_inv_ = EigenValueInversion(num_qubits=self.qpe_register_size + 1, C=self.C, t=self.t)
        self.circuit.append(_eig_val_inv_(*(self.qpe_register + [self.ancilla_qubit])))
        
        self.circuit.append(_qpe_.circuit**(-1))
        self.circuit.append(cirq.measure(self.ancilla_qubit,key='a'))
        self.circuit.append([
            cirq.PhasedXPowGate(
                exponent=sympy.Symbol('exponent'),
                phase_exponent=sympy.Symbol('phase_exponent')
            )(*self.initial_state),
            cirq.measure(*self.initial_state, key='m')
        ])
    
    def simulate(self):
        simulator = cirq.Simulator()

        params = [{
            'exponent': 0.5,
            'phase_exponent': -0.5
        }, {
            'exponent': 0.5,
            'phase_exponent': 0
        }, {
            'exponent': 0,
            'phase_exponent': 0
        }]

        results = simulator.run_sweep(self.circuit, params, repetitions=5000)

        for label, result in zip(('X', 'Y', 'Z'), list(results)):
            expectation = 1 - 2 * np.mean(
                result.measurements['m'][result.measurements['a'] == 1])
            print('{} = {}'.format(label, expectation))
class ControlledUnitary(cirq.Gate):
    def __init__(self, num_qubits, num_input_qubits, U):
        self._num_qubits = num_qubits
        self.num_input_qubits = num_input_qubits
        self.num_control_qubits = num_qubits - self.num_input_qubits
        self.U = U

    def num_qubits(self) -> int:
        return self._num_qubits

    def _decompose_(self, qubits):
        qubits = list(qubits)
        input_state_qubit = qubits[:self.num_input_qubits]
        control_qubits = qubits[self.num_input_qubits:]

        for i,q in enumerate(control_qubits):
            _pow_ = 2 ** (self.num_control_qubits - i - 1)
            yield cirq.ControlledGate(self.U**_pow_)(q, *input_state_qubit)
class QuantumPhaseEstimation:
    def __init__(
        self,
        U,
        input_qubits,
        num_output_qubits=None,
        output_qubits=None,
        initial_circuit=[],
        measure_or_sim=False
    ):
        self.U = U
        self.input_qubits = input_qubits
        self.num_input_qubits = len(self.input_qubits)
        self.initial_circuit = initial_circuit
        self.measure_or_sim = measure_or_sim
        
        if output_qubits is not None:
            self.output_qubits = output_qubits
            self.num_output_qubits = len(self.output_qubits)
          
        elif num_output_qubits is not None:
            self.num_output_qubits = num_output_qubits
            self.output_qubits = [cirq.LineQubit(i) for i 
               in range(self.num_input_qubits,self.num_input_qubits+self.num_output_qubits)]
        
        else:
            raise ValueError("Alteast one of num_output_qubits or output_qubits to be specified")
        
        self.num_qubits = self.num_input_qubits+self.num_output_qubits
    
    def inv_qft(self):
        self._qft_= QFT(qubits=self.output_qubits)
        self._qft_.qft_circuit()
        self.QFT_inv_circuit =  self._qft_.inv_circuit
    
    def circuit(self):
        self.circuit = cirq.Circuit()
        self.circuit.append(cirq.H.on_each(*self.output_qubits))
        self.qubits = list(self.input_qubits + self.output_qubits) 
        self.circuit.append(
            ControlledUnitary(
                self.num_qubits,
                self.num_input_qubits,self.U
            )(*self.qubits)
        )
        self.inv_qft()
        self.circuit.append(self.QFT_inv_circuit)
        if len(self.initial_circuit) > 0 :
            self.circuit = self.initial_circuit + self.circuit
    
    def measure(self):
        self.circuit.append(cirq.measure(*self.output_qubits,key='m'))
     
    def simulate_circuit(self,measure=True):
        sim = cirq.Simulator()
        if measure == False:
            result = sim.simulate(self.circuit)
        else:
            result = sim.run(self.circuit, repetitions=1000).histogram(key='m')
        return result
class HamiltonianSimulation(cirq.EigenGate, cirq.SingleQubitGate):
    def __init__(self, _H_, t, exponent=1.0):
        cirq.SingleQubitGate.__init__(self)
        cirq.EigenGate.__init__(self, exponent=exponent)
        self._H_ = _H_
        self.t = t
        eigen_vals, eigen_vecs = np.linalg.eigh(self._H_)
        self.eigen_components = []
        for _lambda_, vec in zip(eigen_vals, eigen_vecs.T):
            theta = -_lambda_*t / np.pi
            _proj_ = np.outer(vec, np.conj(vec))
            self.eigen_components.append((theta, _proj_))

    def _with_exponent(self, exponent):
        return HamiltonianSimulation(self._H_, self.t, exponent)
    
    def _eigen_components(self):
        return self.eigen_components
class EigenValueInversion(cirq.Gate):
    def __init__(self, num_qubits, C, t):
        super(EigenValueInversion, self)
        self._num_qubits = num_qubits
        self.C = C
        self.t = t
        self.N = 2**(num_qubits-1)

    def num_qubits(self):
        return self._num_qubits

    def _decompose_(self, qubits):
        base_state = 2**self.N - 1

        for eig_val_state in range(self.N):
            eig_val_gate = self._ancilla_rotation(eig_val_state)

            if (eig_val_state != 0):
                base_state = eig_val_state - 1
            qubits_to_flip = eig_val_state ^ base_state

            for q in qubits[-2::-1]:
                if qubits_to_flip % 2 == 1:
                    yield cirq.X(q)
                qubits_to_flip >>= 1

                eig_val_gate = cirq.ControlledGate(eig_val_gate)
            yield eig_val_gate(*qubits)

    def _ancilla_rotation(self, eig_val_state):
        if eig_val_state == 0:
            eig_val_state = self.N
        theta = 2*math.asin(self.C * self.N * self.t / (2*np.pi * eig_val_state))
        return cirq.ry(theta)
class QFT:
    def __init__(
        self,
        signal_length=16,
        basis_to_transform='',
        validate_inverse_fourier=False,
        qubits=None
    ):    
        self.signal_length = signal_length
        self.basis_to_transform = basis_to_transform
        
        if qubits is None:
            self.num_qubits = int(np.log2(signal_length))
            self.qubits = [cirq.LineQubit(i) for i in range(self.num_qubits)]
        else:
            self.qubits = qubits
            self.num_qubits = len(self.qubits)

        self.qubit_index = 0
        self.input_circuit = cirq.Circuit()

        self.validate_inverse_fourier = validate_inverse_fourier
        self.circuit = cirq.Circuit()

        self.inv_circuit = cirq.Circuit()
        
        for k, q_s in enumerate(self.basis_to_transform):
            if int(q_s) == 1:
                self.input_circuit.append(cirq.X(self.qubits[k]))

    def qft_circuit_iter(self):
        if self.qubit_index > 0:
            for j in range(self.qubit_index):
                diff = self.qubit_index - j + 1
                rotation_to_apply = -2.0 / (2.0 ** diff)
                self.circuit.append(
                    cirq.CZ(
                        self.qubits[self.qubit_index],
                        self.qubits[j]
                    ) ** rotation_to_apply
                )
        self.circuit.append(cirq.H(self.qubits[self.qubit_index]))
        self.qubit_index += 1

    def qft_circuit(self):
        while self.qubit_index < self.num_qubits:
            self.qft_circuit_iter()
        self.swap_qubits()
        self.inv_circuit = cirq.inverse(self.circuit.copy())

    def swap_qubits(self):
        for i in range(self.num_qubits // 2):
            self.circuit.append(cirq.SWAP(self.qubits[i], self.qubits[self.num_qubits - i - 1]))
    
    def simulate_circuit(self):
        sim = cirq.Simulator()
        result = sim.simulate(self.circuit)
        return result
A = np.array([
    [4.30213466 - 6.01593490e-08j, 0.23531802 + 9.34386156e-01j],
    [0.23531882 - 9.34388383e-01j, 0.58386534 + 6.01593489e-08j]
])
t = 0.358166 * np.pi
C = None
qpe_register_size = 4
initial_state_transforms = [cirq.rx(1.276359), cirq.rz(1276359)]

_hhl_ = HHL(
    hamiltonian=A,
    initial_state_transforms=initial_state_transforms,
    qpe_register_size=4
)
_hhl_.build_hhl_circuit()
_hhl_.simulate()

=> X = -0.38055555555555554
=> Y = 0.3121621621621622
=> Z = -0.8750821827744906


▼ ワンコインAI無料お試し

▼ DeepRecommendから限定情報を受け取る

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0