diff --git a/.gitignore b/.gitignore index ab0dd6f..9d7ea98 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ .venv coverage* dist +.vscode \ No newline at end of file diff --git a/qrand/platforms/cirq/backend.py b/qrand/platforms/cirq/backend.py index 99f5993..e916255 100644 --- a/qrand/platforms/cirq/backend.py +++ b/qrand/platforms/cirq/backend.py @@ -19,23 +19,71 @@ ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. - +from cirq_google.engine import Engine from ..backend import QuantumBackend ############################################################################### ## CIRQ BACKEND (DECORATOR) ############################################################################### -class CirqBackend(QuantumBackend): - def __init__(self) -> None: - self.ERROR_MSG = f"{self.__class__.__name__}" # TODO - raise NotImplementedError(self.ERROR_MSG) + + + +class CirqBackend(QuantumBackend,Engine): + """ + + Encapsulating Google Engine API with Quantum Backend + + Since API and Cirq is still in Alpha,Changes are expected + + methods from Engine class: + + create_program()->Wraps a Circuit for use with the Quantum Engine. i.e return engineprogram + + run()->Runs the supplied Circuit via Quantum Engine. i.e return study.result + """ + def __init__( + self, + engine: Engine, + shots:int =1, + max_experiments:int=1 + ) -> None: + self._backend: Engine = engine + super(QuantumBackend, self).__init__( ) + self._options = engine.service_args + self._shots:int= shots, + self._max_experiments = max_experiments + self.max_shots=shots #Google API is still private ############################### PUBLIC API ############################### @property def max_measurements(self) -> int: - raise NotImplementedError(self.ERROR_MSG) + return (self._max_experiments*self._shots ) + @property + def max_experiments(self) -> int: + return self._max_experiments + + @max_experiments.setter + def max_experiments(self,max_experiments:int)->None: + self._max_experiments:int=max_experiments + + """ @property def max_qubits(self) -> int: - raise NotImplementedError(self.ERROR_MSG) + return ( + + ) + """ + + #Using Consitent Qiskit analogy + @property + def shots(self) -> int: + return self._shots + + @shots.setter + def shots(self,shots:int) -> None: + self._shots:int = shots + + def run(self, run_input, **options): + return self._backend.run(run_input, self._shots,**options) \ No newline at end of file diff --git a/qrand/platforms/cirq/circuit.py b/qrand/platforms/cirq/circuit.py index 849e623..212358e 100644 --- a/qrand/platforms/cirq/circuit.py +++ b/qrand/platforms/cirq/circuit.py @@ -19,85 +19,117 @@ ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. - +import cirq +from cirq.circuits import Circuit as CirqQuantumCircuit from ..circuit import QuantumCircuit - +from ...helpers import validate_natural_number, validate_type ############################################################################### ## CIRQ CIRCUIT ############################################################################### class CirqCircuit(QuantumCircuit): + def __init__(self, num_qubits: int) -> None: - self.ERROR_MSG = f"{self.__class__.__name__}" # TODO - raise NotImplementedError(self.ERROR_MSG) - + self.qubits=cirq.LineQubit.range(num_qubits) + self._num_qubits=num_qubits + self.moment_list:list=[] + + def _validate_qubit_index(self, qubit_index: int) -> None: + if qubit_index >= self.num_qubits: + raise ValueError( + f"Qubit index out of range {qubit_index} >= {self.num_qubits}." + ) + + + @property def num_qubits(self) -> int: - raise NotImplementedError(self.ERROR_MSG) + return self._num_qubits + + @num_qubits.setter + def num_qubits(self,num_qubits:int) -> None: + self._num_qubits=num_qubits ############################## SPECIAL GATES ############################## def measure(self, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) - + self.moment_list.append(cirq.ops.measure(self.qubits[target_qubit])) + ########################### SINGLE QUBIT GATES ########################### def h(self, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.H(self.qubits[target_qubit])) + def rx(self, radians: float, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.Rx(radians, self.qubits[target_qubit])) def ry(self, radians: float, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.Ry(radians,self.qubits[target_qubit])) def rz(self, radians: float, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.Rz(radians, self.qubits[target_qubit])) def s(self, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.S(self.qubits[target_qubit])) def t(self, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) - - def u1(self, theta: float, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) - - def u2(self, phi: float, lam: float, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) - - def u3( - self, theta: float, phi: float, lam: float, target_qubit: int - ) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.T(self.qubits[target_qubit])) def x(self, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.X(self.qubits[target_qubit])) + def y(self, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.Y(self.qubits[target_qubit])) + def z(self, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + self._validate_qubit_index(target_qubit) + self.moment_list.append(cirq.ops.Z(self.qubits[target_qubit])) ############################# TWO QUBIT GATES ############################# - def cs(self, control_qubit: int, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) - - def cx(self, control_qubit: int, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) - def cz(self, control_qubit: int, target_qubit: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit, True) + validate_natural_number(control_qubit, True) + self._validate_qubit_index(target_qubit) + self._validate_qubit_index(control_qubit) + self.moment_list.append(cirq.ops.CZ(self.qubits[control_qubit], self.qubits[target_qubit])) def swap(self, target_qubit_1: int, target_qubit_2: int) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit_1, True) + self._validate_qubit_index(target_qubit_1) + self._validate_qubit_index(target_qubit_2) + self.moment_list.append(cirq.ops.SWAP(self.qubits[target_qubit_1], self.qubits[target_qubit_2])) ############################ THREE QUBIT GATES ############################ - def ccx( - self, control_qubit_1: int, control_qubit_2: int, target_qubit: int - ) -> None: - raise NotImplementedError(self.ERROR_MSG) + def cswap( - self, control_qubit_1: int, control_qubit_2: int, target_qubit: int + self, control_qubit: int, target_qubit_1: int, target_qubit_2: int ) -> None: - raise NotImplementedError(self.ERROR_MSG) + validate_natural_number(target_qubit_1, True) + validate_natural_number(target_qubit_2, True) + validate_natural_number(control_qubit, True) + self._validate_qubit_index(control_qubit) + self._validate_qubit_index(target_qubit_1) + self._validate_qubit_index(target_qubit_2) + self.moment_list.append(cirq.ops.CSWAP(self.qubits[control_qubit], self.qubits[target_qubit_1], self.qubits[target_qubit_2])) + + def createcircuit(self): + return CirqQuantumCircuit(self.moment_list) \ No newline at end of file diff --git a/qrand/platforms/cirq/job.py b/qrand/platforms/cirq/job.py index a3fd777..0201e58 100644 --- a/qrand/platforms/cirq/job.py +++ b/qrand/platforms/cirq/job.py @@ -20,8 +20,13 @@ ## See the License for the specific language governing permissions and ## limitations under the License. + from typing import List, Optional +from warnings import warn + + +from ...helpers import compute_bounded_factorization, reverse_endian from ..job import QuantumJob from .backend import CirqBackend from .circuit import CirqCircuit @@ -31,34 +36,61 @@ ## CIRQ JOB ############################################################################### class CirqJob(QuantumJob): - def __init__(self) -> None: - self.ERROR_MSG = f"{self.__class__.__name__}" # TODO - raise NotImplementedError(self.ERROR_MSG) + def __init__( + self, + circuit:CirqCircuit, + backend:CirqBackend, + num_measurements:Optional[int]=None + ) -> None: + self.backend:CirqBackend = backend + self.circuit:CirqCircuit = circuit + self.num_measurements:int = num_measurements + + ############################### PUBLIC API ############################### @property def backend(self) -> CirqBackend: - raise NotImplementedError(self.ERROR_MSG) + return self._backend @backend.setter def backend(self, backend: CirqBackend) -> None: - raise NotImplementedError(self.ERROR_MSG) + self._backend: CirqBackend = backend @property def circuit(self) -> CirqCircuit: - raise NotImplementedError(self.ERROR_MSG) + return self._circuit @circuit.setter def circuit(self, circuit: CirqCircuit) -> None: - raise NotImplementedError(self.ERROR_MSG) + self._circuit: CirqCircuit = circuit @property def num_measurements(self) -> int: - raise NotImplementedError(self.ERROR_MSG) + return self._shots @num_measurements.setter def num_measurements(self, num_measurements: Optional[int]) -> None: - raise NotImplementedError(self.ERROR_MSG) + #Shots == Reptition....Used because of Qiskit analogy in whole qrand + num_measurements = ( + num_measurements + if isinstance(num_measurements, int) and 0 < num_measurements + else self.backend.max_measurements + ) + if self.backend.max_measurements < num_measurements: + warn( + f"Number of measurements unsupported by the job's Backend: \ + {self.backend.max_measurements}<{num_measurements}. \ + Using max_measurements instead.", + UserWarning, + ) + num_measurements=self._backend.max_measurements + self._shots, self._experiments = compute_bounded_factorization( + num_measurements, + self.backend.max_shots, + self.backend.max_experiments, + ) def execute(self) -> List[str]: - raise NotImplementedError(self.ERROR_MSG) + self.cirqcirc=self._circuit.createcircuit() + return self._backend.run(self.cirqcirc) \ No newline at end of file diff --git a/qrand/platforms/cirq/platform.py b/qrand/platforms/cirq/platform.py index 4530d02..842b217 100644 --- a/qrand/platforms/cirq/platform.py +++ b/qrand/platforms/cirq/platform.py @@ -1,55 +1,80 @@ -## _____ _____ -## | __ \| __ \ AUTHOR: Pedro Rivero -## | |__) | |__) | --------------------------------- -## | ___/| _ / DATE: May 20, 2021 -## | | | | \ \ --------------------------------- -## |_| |_| \_\ https://github.com/pedrorrivero +# _____ _____ +# | __ \| __ \ AUTHOR: Pedro Rivero +# | |__) | |__) | --------------------------------- +# | ___/| _ / DATE: May 20, 2021 +# | | | | \ \ --------------------------------- +# |_| |_| \_\ https://github.com/pedrorrivero ## -## Copyright 2021 Pedro Rivero +# Copyright 2021 Pedro Rivero ## -## Licensed under the Apache License, Version 2.0 (the "License"); -## you may not use this file except in compliance with the License. -## You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at ## -## http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 ## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from typing import Optional -from ..platform import QuantumPlatform, QuantumProtocol +from ...helpers import validate_type +from ...protocols import ProtocolResult, QuantumProtocol + +from ..platform import QuantumPlatform from .backend import CirqBackend from .circuit import CirqCircuit from .job import CirqJob - +from cirq_google import Engine +from cirq.sim import simulator ############################################################################### -## CIRQ PLATFORM +# CIRQ PLATFORM ############################################################################### + + class CirqPlatform(QuantumPlatform): - def __init__(self) -> None: - self.ERROR_MSG = f"{self.__class__.__name__}" # TODO - raise NotImplementedError(self.ERROR_MSG) + def __init__( + self, + engine: Optional[Engine] = None, + ) -> None: + if engine: + self.backend=CirqBackend(engine) + else: + self.backend = simulator ############################### PUBLIC API ############################### + @property + def backend(self) -> Engine: + return self._backend + + @backend.setter + def backend(self, backend: Engine) -> None: + validate_type(backend, Engine) + self._backend: Engine = backend + def create_circuit(self, num_qubits: int) -> CirqCircuit: - raise NotImplementedError(self.ERROR_MSG) + return CirqCircuit(num_qubits) + def create_job( # type: ignore self, circuit: CirqCircuit, backend: CirqBackend, num_measurements: Optional[int] = None, ) -> CirqJob: - raise NotImplementedError(self.ERROR_MSG) + return CirqJob(circuit,backend,num_measurements) + def fetch_random_bits(self, protocol: QuantumProtocol) -> str: - raise NotImplementedError(self.ERROR_MSG) + result: ProtocolResult = protocol.run(self) + return result.bitstring + + ##Doubts def retrieve_backend(self) -> CirqBackend: - raise NotImplementedError(self.ERROR_MSG) + return CirqBackend(self._backend)