├── tests ├── __init__.py ├── example.qasm ├── test_statevector_simulator.py ├── test_qasm_simulator.py └── case.py ├── requirements-dev.txt ├── requirements.txt ├── examples ├── aqua │ ├── README.md │ ├── 0.7_sto-3g.hdf5 │ ├── Grovers Algorithm for SAT.ipynb │ ├── Chemistry.ipynb │ ├── Quantum Support Vector Machine - Breast Cancer Prediction.ipynb │ ├── Traveling Salesman.ipynb │ └── Max-Cut.ipynb ├── qcgpu_backends.py ├── qcgpu_statevector.py └── qcgpu_qasm.py ├── .travis.yml ├── Makefile ├── qiskit_qcgpu_provider ├── simulatorerror.py ├── __init__.py ├── job.py ├── statevector_simulator.py └── qasm_simulator.py ├── setup.py ├── README.md ├── .gitignore ├── benchmark.py ├── LICENSE └── .pylintrc /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | nose -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | qcgpu 2 | qiskit -------------------------------------------------------------------------------- /examples/aqua/README.md: -------------------------------------------------------------------------------- 1 | # Aqua Demos 2 | 3 | These are demos for using Qiskit Aqua with QCGPU. -------------------------------------------------------------------------------- /examples/aqua/0.7_sto-3g.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/qiskit-qcgpu-provider/HEAD/examples/aqua/0.7_sto-3g.hdf5 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | os: osx 3 | 4 | git: 5 | depth: false 6 | 7 | install: 8 | - pip3 install pybind11 9 | - pip3 install -e . 10 | 11 | script: 12 | - make test -------------------------------------------------------------------------------- /tests/example.qasm: -------------------------------------------------------------------------------- 1 | OPENQASM 2.0; 2 | include "qelib1.inc"; 3 | qreg q[3]; 4 | qreg r[3]; 5 | h q; 6 | cx q, r; 7 | creg c[3]; 8 | creg d[3]; 9 | barrier q; 10 | measure q->c; 11 | measure r->d; 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: style lint test profile 2 | 3 | lint: 4 | python3 -m pylint -rn qiskit_qcgpu_provider 5 | 6 | style: 7 | python3 -m pycodestyle --max-line-length=120 qiskit_qcgpu_provider tests 8 | 9 | test: 10 | 11 | PYOPENCL_CTX='0' nosetests -------------------------------------------------------------------------------- /qiskit_qcgpu_provider/simulatorerror.py: -------------------------------------------------------------------------------- 1 | """ 2 | Exception for errors raised by QCGPU simulators 3 | """ 4 | 5 | from qiskit import QiskitError 6 | 7 | 8 | class QCGPUSimulatorError(QiskitError): 9 | """Base class for errors raised by simulators.""" 10 | 11 | def __init__(self, *message): 12 | """Set the error message""" 13 | super().__init__(*message) 14 | self.message = ' '.join(message) 15 | 16 | def __str__(self): 17 | """Return the message""" 18 | return repr(self.message) 19 | -------------------------------------------------------------------------------- /examples/qcgpu_backends.py: -------------------------------------------------------------------------------- 1 | """Usage examples for the QCGPU backends""" 2 | 3 | from qiskit_qcgpu_provider import QCGPUProvider 4 | 5 | Provider = QCGPUProvider() 6 | 7 | print(Provider.backends()) 8 | 9 | print(Provider.backends(name='statevector_simulator')) 10 | 11 | backend = Provider.get_backend('statevector_simulator') 12 | print(backend) 13 | 14 | # gets the name of the backend. 15 | print(backend.name()) 16 | 17 | # gets the status of the backend. 18 | print(backend.status()) 19 | 20 | # returns the provider of the backend 21 | print(backend.provider) 22 | 23 | # gets the configuration of the backend. 24 | print(backend.configuration()) 25 | 26 | # gets the properties of the backend. 27 | print(backend.properties()) 28 | -------------------------------------------------------------------------------- /examples/qcgpu_statevector.py: -------------------------------------------------------------------------------- 1 | """ 2 | In this example a Bell state is made. 3 | """ 4 | 5 | from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister 6 | from qiskit import execute 7 | from qiskit_qcgpu_provider import QCGPUProvider 8 | 9 | Provider = QCGPUProvider() 10 | 11 | # Create a Quantum Register with 2 qubits. 12 | q = QuantumRegister(2) 13 | # Create a Quantum Circuit with 2 Qubits 14 | qc = QuantumCircuit(q) 15 | 16 | # Add a H gate on qubit 0, putting this qubit in superposition. 17 | qc.h(q[0]) 18 | # Add a CX (CNOT) gate on control qubit 0 and target qubit 1, putting 19 | # the qubits in a Bell state. 20 | qc.cx(q[0], q[1]) 21 | 22 | 23 | # See a list of available local simulators 24 | print("QCGPU backends: ", Provider.backends()) 25 | backend_sim = Provider.get_backend('statevector_simulator') 26 | 27 | # Compile and run the Quantum circuit on a simulator backend 28 | job_sim = execute(qc, backend_sim) 29 | result_sim = job_sim.result() 30 | 31 | # Show the results 32 | print("Simulation Results: ", result_sim) 33 | print(result_sim.get_statevector(qc)) 34 | -------------------------------------------------------------------------------- /tests/test_statevector_simulator.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import math 3 | 4 | from qiskit_qcgpu_provider import QCGPUProvider 5 | from qiskit import execute, QuantumRegister, QuantumCircuit, BasicAer 6 | from qiskit.quantum_info import state_fidelity 7 | 8 | from .case import MyTestCase 9 | 10 | 11 | class TestStatevectorSimulator(MyTestCase): 12 | """Test the state vector simulator""" 13 | 14 | def test_computations(self): 15 | for n in range(2, 10): 16 | circ = self.random_circuit(n, 5) 17 | self._compare_outcomes(circ) 18 | 19 | 20 | def _compare_outcomes(self, circ): 21 | Provider = QCGPUProvider() 22 | backend_qcgpu = Provider.get_backend('statevector_simulator') 23 | statevector_qcgpu = execute(circ, backend_qcgpu).result().get_statevector() 24 | 25 | backend_qiskit = BasicAer.get_backend('statevector_simulator') 26 | statevector_qiskit = execute(circ, backend_qiskit).result().get_statevector() 27 | 28 | self.assertAlmostEqual(state_fidelity(statevector_qcgpu, statevector_qiskit), 1, 5) 29 | 30 | 31 | if __name__ == '__main__': 32 | unittest.main() 33 | -------------------------------------------------------------------------------- /examples/qcgpu_qasm.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example used in the README. In this example a Bell state is made. 3 | 4 | """ 5 | 6 | # Import the Qiskit 7 | from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, QiskitError 8 | from qiskit import execute 9 | from qiskit_qcgpu_provider import QCGPUProvider 10 | 11 | # Create a Quantum Register with 2 qubits. 12 | q = QuantumRegister(2) 13 | # Create a Classical Register with 2 bits. 14 | c = ClassicalRegister(2) 15 | # Create a Quantum Circuit 16 | qc = QuantumCircuit(q, c) 17 | 18 | # Add a H gate on qubit 0, putting this qubit in superposition. 19 | qc.h(q[0]) 20 | # Add a CX (CNOT) gate on control qubit 0 and target qubit 1, putting 21 | # the qubits in a Bell state. 22 | qc.cx(q[0], q[1]) 23 | # Add a Measure gate to see the state. 24 | qc.measure(q, c) 25 | 26 | # Get the QCGPU Provider 27 | Provider = QCGPUProvider() 28 | 29 | # See a list of available local simulators 30 | print("QCGPU backends: ", Provider.backends()) 31 | backend_sim = Provider.get_backend('qasm_simulator') 32 | 33 | # Compile and run the Quantum circuit on a simulator backend 34 | job_sim = execute(qc, backend_sim) 35 | result_sim = job_sim.result() 36 | 37 | # Show the results 38 | print(result_sim.get_counts(qc)) 39 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Setup for qiskit-qcgpu-provider. 3 | """ 4 | 5 | import setuptools 6 | 7 | with open("README.md", "r") as fh: 8 | LONG_DESCRIPTION = fh.read() 9 | 10 | setuptools.setup( 11 | name="qiskit-qcgpu-provider", 12 | version="0.2.0", 13 | author="Adam Kelly", 14 | author_email="adamkelly2201@gmail.com", 15 | description="An OpenCL based quantum computer simulator", 16 | long_description=LONG_DESCRIPTION, 17 | url="https://qcgpu.github.io", 18 | packages=setuptools.find_packages(), 19 | setup_requires=['qcgpu>=0.1.0', 'qiskit'], 20 | license="Apache 2.0", 21 | classifiers=[ 22 | "Environment :: Console", 23 | "License :: OSI Approved :: Apache Software License", 24 | "Intended Audience :: Developers", 25 | "Intended Audience :: Science/Research", 26 | "Operating System :: Microsoft :: Windows", 27 | "Operating System :: MacOS", 28 | "Operating System :: POSIX :: Linux", 29 | "Programming Language :: Python :: 3.5", 30 | "Programming Language :: Python :: 3.6", 31 | "Topic :: Scientific/Engineering", 32 | ], 33 | install_requires=['qcgpu>=0.1.0', 'qiskit'], 34 | tests_require=['nose'], 35 | python_requires=">=3.5" 36 | ) 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Qiskit QCGPU Provider 3 | 4 | This module contains [Qiskit](https://www.qiskit.org/) 5 | simulators using the OpenCL based [QCGPU](https://qcgpu.github.io) library. 6 | 7 | This provider adds two quantum circuit simulators, which are: 8 | 9 | * Statevector simulator - returns the statevector of a quantum circuit applied to the |0> state 10 | * Qasm simulator - simulates a qasm quantum circuit that has been compiled to run on the simulator. 11 | 12 | These simulation backends take advantage of the GPU or other OpenCL devices. 13 | 14 | ## Installation 15 | 16 | First of all, you will have to have some OpenCL installation installed already. 17 | 18 | You can install this module from PyPI using pip: 19 | 20 | ```bash 21 | $ pip install qiskit-qcgpu-provider 22 | ``` 23 | 24 | 25 | ## Usage 26 | 27 | The usage of this backend with Qiskit is shown in the [usage example](https://github.com/Qiskit/qiskit-qcgpu-provider/tree/master/examples) 28 | 29 | For more information on Qiskit and quantum simulations, look at the Qiskit tutorials and the [Qiskit instructions page](https://github.com/Qiskit/qiskit-terra) 30 | 31 | ## Benchmarking 32 | 33 | To benchmark this simulator against the `BasicAer` `qasm_simulator`, 34 | you can run 35 | 36 | ```bash 37 | $ python3 benchmark.py --samples 15 --qubits 5 --single True 38 | ``` 39 | 40 | ## License 41 | 42 | This project uses the [Apache License Version 2.0 software license.](https://www.apache.org/licenses/LICENSE-2.0) 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | .vscode 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ -------------------------------------------------------------------------------- /tests/test_qasm_simulator.py: -------------------------------------------------------------------------------- 1 | from qiskit_qcgpu_provider import QCGPUProvider 2 | from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit 3 | from qiskit.compiler import assemble 4 | 5 | import unittest 6 | 7 | from .case import MyTestCase 8 | 9 | 10 | class TestQasmSimulator(MyTestCase): 11 | """ 12 | Test the QASM Simulator 13 | """ 14 | 15 | def setUp(self): 16 | self.seed = 23423456 17 | self.sim = QCGPUProvider().get_backend('qasm_simulator') 18 | qasm_file = 'tests/example.qasm' 19 | circ = QuantumCircuit.from_qasm_file(qasm_file) 20 | self.qobj = assemble(circ, backend=self.sim) 21 | 22 | def test_qasm_simulator_single_show(self): 23 | shots = 1 24 | self.qobj.config.shots = shots 25 | result = self.sim.run(self.qobj).result() 26 | self.assertEqual(result.success, True) 27 | 28 | def test_qasm_simulator(self): 29 | """Test data counts output for single circuit run against reference.""" 30 | shots = 1024 31 | self.qobj.config.shots = shots 32 | result = self.sim.run(self.qobj).result() 33 | threshold = 0.04 * shots 34 | counts = result.get_counts() 35 | target = {'100 100': shots / 8, '011 011': shots / 8, 36 | '101 101': shots / 8, '111 111': shots / 8, 37 | '000 000': shots / 8, '010 010': shots / 8, 38 | '110 110': shots / 8, '001 001': shots / 8} 39 | self.assertDictAlmostEqual(counts, target, threshold) 40 | 41 | def test_memory(self): 42 | qr = QuantumRegister(4, 'qr') 43 | cr0 = ClassicalRegister(2, 'cr0') 44 | cr1 = ClassicalRegister(2, 'cr1') 45 | circ = QuantumCircuit(qr, cr0, cr1) 46 | circ.h(qr[0]) 47 | circ.cx(qr[0], qr[1]) 48 | circ.x(qr[3]) 49 | circ.measure(qr[0], cr0[0]) 50 | circ.measure(qr[1], cr0[1]) 51 | circ.measure(qr[2], cr1[0]) 52 | circ.measure(qr[3], cr1[1]) 53 | 54 | shots = 50 55 | qobj = assemble(circ, backend=self.sim, shots=shots, memory=True) 56 | result = self.sim.run(qobj).result() 57 | memory = result.get_memory() 58 | self.assertEqual(len(memory), shots) 59 | for mem in memory: 60 | self.assertIn(mem, ['10 00', '10 11']) 61 | 62 | 63 | if __name__ == '__main__': 64 | unittest.main() 65 | -------------------------------------------------------------------------------- /examples/aqua/Grovers Algorithm for SAT.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from qiskit_aqua import run_algorithm\n", 10 | "from qiskit_qcgpu_provider import QCGPUProvider\n", 11 | "\n", 12 | "sat_cnf = \"\"\"\n", 13 | "c Example DIMACS 3-sat\n", 14 | "p cnf 3 5\n", 15 | "-1 -2 -3 0\n", 16 | "1 -2 3 0\n", 17 | "1 2 -3 0\n", 18 | "1 -2 -3 0\n", 19 | "-1 2 3 0\n", 20 | "\"\"\"\n", 21 | "\n", 22 | "params = {\n", 23 | " \"problem\": { \"name\": \"search\" },\n", 24 | " \"algorithm\": { \"name\": \"Grover\" },\n", 25 | " \"oracle\": { \"name\": \"SAT\", \"cnf\": sat_cnf },\n", 26 | " \"backend\": { \"name\": \"qasm_simulator\" }\n", 27 | "}\n" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "metadata": {}, 34 | "outputs": [ 35 | { 36 | "name": "stdout", 37 | "output_type": "stream", 38 | "text": [ 39 | "CPU times: user 962 ms, sys: 28.2 ms, total: 991 ms\n", 40 | "Wall time: 1.08 s\n", 41 | "CPU times: user 302 ms, sys: 99.7 ms, total: 402 ms\n", 42 | "Wall time: 530 ms\n" 43 | ] 44 | } 45 | ], 46 | "source": [ 47 | "backend = QCGPUProvider().get_backend('qasm_simulator')\n", 48 | "%time result_qiskit = run_algorithm(params)\n", 49 | "%time result = run_algorithm(params, backend=backend)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 3, 55 | "metadata": {}, 56 | "outputs": [ 57 | { 58 | "name": "stdout", 59 | "output_type": "stream", 60 | "text": [ 61 | "[-1, -2, -3]\n" 62 | ] 63 | } 64 | ], 65 | "source": [ 66 | "print(result[\"result\"])" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [] 75 | } 76 | ], 77 | "metadata": { 78 | "kernelspec": { 79 | "display_name": "Python 3", 80 | "language": "python", 81 | "name": "python3" 82 | }, 83 | "language_info": { 84 | "codemirror_mode": { 85 | "name": "ipython", 86 | "version": 3 87 | }, 88 | "file_extension": ".py", 89 | "mimetype": "text/x-python", 90 | "name": "python", 91 | "nbconvert_exporter": "python", 92 | "pygments_lexer": "ipython3", 93 | "version": "3.6.6" 94 | } 95 | }, 96 | "nbformat": 4, 97 | "nbformat_minor": 2 98 | } 99 | -------------------------------------------------------------------------------- /qiskit_qcgpu_provider/__init__.py: -------------------------------------------------------------------------------- 1 | """Provider for local QCGPU backends.""" 2 | 3 | from collections import OrderedDict 4 | import logging 5 | 6 | from qiskit.providers import BaseProvider 7 | from qiskit.providers.providerutils import filter_backends 8 | 9 | from .simulatorerror import QCGPUSimulatorError 10 | from .statevector_simulator import QCGPUStatevectorSimulator 11 | from .qasm_simulator import QCGPUQasmSimulator 12 | 13 | logger = logging.getLogger(__name__) 14 | 15 | SIMULATORS = [ 16 | QCGPUStatevectorSimulator, 17 | QCGPUQasmSimulator 18 | ] 19 | 20 | 21 | class QCGPUProvider(BaseProvider): 22 | """Provider for local QCGPU backends.""" 23 | 24 | def __init__(self, *args, **kwargs): 25 | super().__init__(args, kwargs) 26 | 27 | self._backends = self._verify_backends() 28 | 29 | def backends(self, name=None, filters=None, **kwargs): 30 | backends = self._backends.values() 31 | 32 | if name: 33 | try: 34 | backends = [backend for backend in backends if backend.name() == name] 35 | except LookupError: 36 | pass 37 | 38 | return filter_backends(backends, filters=filters, **kwargs) 39 | 40 | def _verify_backends(self): 41 | res = OrderedDict() 42 | 43 | for backend_class in SIMULATORS: 44 | try: 45 | backend_instance = backend_class(provider=self) 46 | backend_name = backend_instance.name() 47 | res[backend_name] = backend_instance 48 | except QCGPUSimulatorError as err: 49 | logger.info(err) 50 | 51 | return res 52 | 53 | def _get_backend_instance(self, backend_class): 54 | try: 55 | backend_instance = backend_class(provider=self) 56 | except Exception as err: 57 | raise QCGPUSimulatorError( 58 | 'Backend {} could not be instantiated: {}'.format( 59 | backend_class, err)) 60 | 61 | return backend_instance 62 | 63 | def __str__(self): 64 | return 'QCGPU Provider' 65 | 66 | # from qiskit.backends import BaseProvider 67 | # from qiskit.backends.providerutils import filter_backends 68 | 69 | # from .statevector_simulator import QCGPUStatevectorSimulator 70 | # from .qasm_simulator import QCGPUQasmSimulator 71 | 72 | # import qcgpu 73 | 74 | # class QCGPUProvider(BaseProvider): 75 | # """Provider for local QCGPU backends.""" 76 | 77 | # def __init__(self, *args, **kwargs): 78 | # super().__init__(args, kwargs) 79 | 80 | # qcgpu.backend.create_context() 81 | 82 | # self._backends = [QCGPUQasmSimulator(provider=self), 83 | # QCGPUStatevectorSimulator(provider=self)] 84 | 85 | # def get_backend(self, name=None, **kwargs): 86 | # return super().get_backend(name=name, **kwargs) 87 | 88 | # def backends(self, name=None, filters=None, **kwargs): 89 | # # pylint: disable=arguments-differ 90 | # if name: 91 | # kwargs.update({'name': name}) 92 | 93 | # return filter_backends(self._backends, filters=filters, **kwargs) 94 | 95 | # def __str__(self): 96 | # return 'QCGPU Provider' 97 | -------------------------------------------------------------------------------- /examples/aqua/Chemistry.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from qiskit_chemistry import QiskitChemistry\n", 10 | "from qiskit import Aer" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 2, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "qiskit_chemistry_dict = {\n", 20 | " 'driver': {'name': 'HDF5'},\n", 21 | " 'HDF5': {'hdf5_input': '0.7_sto-3g.hdf5'},\n", 22 | " 'operator': {'name': 'hamiltonian'},\n", 23 | " 'algorithm': {'name': 'VQE'},\n", 24 | " 'optimizer': {'name': 'COBYLA'},\n", 25 | " 'variational_form': {'name': 'UCCSD'},\n", 26 | " 'initial_state': {'name': 'HartreeFock'}\n", 27 | "}" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 3, 33 | "metadata": {}, 34 | "outputs": [ 35 | { 36 | "name": "stdout", 37 | "output_type": "stream", 38 | "text": [ 39 | "CPU times: user 2.49 s, sys: 261 ms, total: 2.75 s\n", 40 | "Wall time: 3.23 s\n" 41 | ] 42 | } 43 | ], 44 | "source": [ 45 | "from qiskit import Aer\n", 46 | "backend = Aer.get_backend('statevector_simulator')\n", 47 | "\n", 48 | "solver = QiskitChemistry()\n", 49 | "%time result = solver.run(qiskit_chemistry_dict, backend=backend)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 4, 55 | "metadata": {}, 56 | "outputs": [ 57 | { 58 | "name": "stdout", 59 | "output_type": "stream", 60 | "text": [ 61 | "CPU times: user 1.2 s, sys: 295 ms, total: 1.5 s\n", 62 | "Wall time: 1.54 s\n" 63 | ] 64 | } 65 | ], 66 | "source": [ 67 | "from qiskit_qcgpu_provider import QCGPUProvider\n", 68 | "backend = QCGPUProvider().get_backend('statevector_simulator')\n", 69 | "\n", 70 | "solver = QiskitChemistry()\n", 71 | "%time result = solver.run(qiskit_chemistry_dict, backend=backend)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 5, 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "=== GROUND STATE ENERGY ===\n", 84 | " \n", 85 | "* Electronic ground state energy (Hartree): -1.892155909063\n", 86 | " - computed part: -1.892155909063\n", 87 | " - frozen energy part: 0.0\n", 88 | " - particle hole part: 0.0\n", 89 | "~ Nuclear repulsion energy (Hartree): 0.755967444171\n", 90 | "> Total ground state energy (Hartree): -1.136188464891\n", 91 | " Measured:: Num particles: 2.000, S: 0.000, M: 0.00000\n", 92 | " \n", 93 | "=== DIPOLE MOMENT ===\n", 94 | " \n", 95 | "* Electronic dipole moment (a.u.): [0.0 0.0 0.00029291]\n", 96 | " - computed part: [0.0 0.0 0.00029291]\n", 97 | " - frozen energy part: [0.0 0.0 0.0]\n", 98 | " - particle hole part: [0.0 0.0 0.0]\n", 99 | "~ Nuclear dipole moment (a.u.): [0.0 0.0 0.0]\n", 100 | "> Dipole moment (a.u.): [0.0 0.0 -0.00029291] Total: 0.00029291\n", 101 | " (debye): [0.0 0.0 -0.0007445] Total: 0.0007445\n" 102 | ] 103 | } 104 | ], 105 | "source": [ 106 | "for line in result['printable']:\n", 107 | " print(line)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [] 116 | } 117 | ], 118 | "metadata": { 119 | "kernelspec": { 120 | "display_name": "Python 3", 121 | "language": "python", 122 | "name": "python3" 123 | }, 124 | "language_info": { 125 | "codemirror_mode": { 126 | "name": "ipython", 127 | "version": 3 128 | }, 129 | "file_extension": ".py", 130 | "mimetype": "text/x-python", 131 | "name": "python", 132 | "nbconvert_exporter": "python", 133 | "pygments_lexer": "ipython3", 134 | "version": "3.6.6" 135 | } 136 | }, 137 | "nbformat": 4, 138 | "nbformat_minor": 2 139 | } 140 | -------------------------------------------------------------------------------- /benchmark.py: -------------------------------------------------------------------------------- 1 | import click 2 | import time 3 | import random 4 | import statistics 5 | import csv 6 | import os.path 7 | import math 8 | 9 | from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit 10 | from qiskit import QiskitError, execute, Aer 11 | 12 | from qiskit_qcgpu_provider import QCGPUProvider 13 | 14 | # Implementation of the Quantum Fourier Transform 15 | 16 | def construct_circuit(num_qubits): 17 | q = QuantumRegister(num_qubits) 18 | c = ClassicalRegister(num_qubits) 19 | circ = QuantumCircuit(q, c) 20 | 21 | # Quantum Fourier Transform 22 | for j in range(num_qubits): 23 | for k in range(j): 24 | circ.cu1(math.pi / float(2**(j - k)), q[j], q[k]) 25 | circ.h(q[j]) 26 | # circ.measure(q, c) 27 | 28 | return circ 29 | 30 | 31 | # Benchmarking functions 32 | # qiskit_backend = Aer.get_backend('qasm_simulator') 33 | # qcgpu_backend = QCGPUProvider().get_backend('qasm_simulator') 34 | qiskit_backend = Aer.get_backend('statevector_simulator') 35 | qcgpu_backend = QCGPUProvider().get_backend('statevector_simulator') 36 | 37 | 38 | def bench_qiskit(qc): 39 | start = time.time() 40 | job_sim = execute(qc, qiskit_backend) 41 | sim_result = job_sim.result() 42 | return time.time() - start 43 | 44 | 45 | def bench_qcgpu(qc): 46 | start = time.time() 47 | job_sim = execute(qc, qcgpu_backend) 48 | sim_result = job_sim.result() 49 | return time.time() - start 50 | 51 | # Reporting 52 | 53 | 54 | def create_csv(filename): 55 | file_exists = os.path.isfile(filename) 56 | csvfile = open(filename, 'a') 57 | 58 | headers = ['name', 'num_qubits', 'time'] 59 | writer = csv.DictWriter(csvfile, delimiter=',', lineterminator='\n', fieldnames=headers) 60 | 61 | if not file_exists: 62 | writer.writeheader() # file doesn't exist yet, write a header 63 | 64 | return writer 65 | 66 | 67 | def write_csv(writer, data): 68 | writer.writerow(data) 69 | 70 | 71 | @click.command() 72 | @click.option('--samples', default=5, help='Number of samples to take for each qubit.') 73 | @click.option('--qubits', default=5, help='How many qubits you want to test for') 74 | @click.option('--out', default='benchmark_data.csv', 75 | help='Where to store the CSV output of each test') 76 | @click.option( 77 | '--single', 78 | default=False, 79 | help='Only run the benchmark for a single amount of qubits, and print an analysis') 80 | @click.option('--burn', default=True, help='Burn the first few samples for accuracy') 81 | def benchmark(samples, qubits, out, single, burn): 82 | burn_count = 5 if burn else 0 83 | 84 | if single: 85 | functions = bench_qcgpu, bench_qiskit 86 | times = {f.__name__: [] for f in functions} 87 | 88 | names = [] 89 | means = [] 90 | 91 | qc = construct_circuit(qubits) 92 | 93 | # Run the benchmarks 94 | for i in range(samples + burn_count): 95 | progress = (i) / (samples + burn_count) 96 | if samples > 1: 97 | print("\rProgress: [{0:50s}] {1:.1f}%".format('#' * int(progress * 50), progress * 100), end="", flush=True) 98 | 99 | func = random.choice(functions) 100 | t = func(qc) 101 | 102 | if i >= burn_count: 103 | times[func.__name__].append(t) 104 | 105 | print('') 106 | 107 | for name, numbers in times.items(): 108 | print('FUNCTION:', name, 'Used', len(numbers), 'times') 109 | print('\tMEDIAN', statistics.median(numbers)) 110 | print('\tMEAN ', statistics.mean(numbers)) 111 | if len(numbers) > 1: 112 | print('\tSTDEV ', statistics.stdev(numbers)) 113 | 114 | return 115 | 116 | functions = bench_qcgpu, bench_qiskit 117 | 118 | writer = create_csv(out) 119 | 120 | for n in range(1, qubits): 121 | # Progress counter 122 | progress = (n + 1) / (qubits) 123 | print("\rProgress: [{0:50s}] {1:.1f}%".format('#' * int(progress * 50), progress * 100), end="", flush=True) 124 | 125 | # Construct the circuit 126 | qc = construct_circuit(n + 1) 127 | 128 | # Run the benchmarks 129 | for i in range(samples): 130 | func = random.choice(functions) 131 | t = func(qc) 132 | # times[func.__name__].append(t) 133 | write_csv(writer, {'name': func.__name__, 'num_qubits': n + 1, 'time': t}) 134 | 135 | 136 | if __name__ == '__main__': 137 | benchmark() 138 | -------------------------------------------------------------------------------- /qiskit_qcgpu_provider/job.py: -------------------------------------------------------------------------------- 1 | """This module implements the job class used by simulator backends. 2 | Taken mostly from https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/backends/builtinsimulators/simulatorsjob.py 3 | """ 4 | 5 | 6 | from concurrent import futures 7 | import logging 8 | import sys 9 | import functools 10 | 11 | from qiskit.providers import BaseJob, JobStatus, JobError 12 | from qiskit.qobj import validate_qobj_against_schema 13 | from qiskit.result import Result 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | # def requires_submit(func): 19 | # """ 20 | # Decorator to ensure that a submit has been performed before 21 | # calling the method. 22 | # Args: 23 | # func (callable): test function to be decorated. 24 | # Returns: 25 | # callable: the decorated function. 26 | # """ 27 | # @functools.wraps(func) 28 | # def _wrapper(self, *args, **kwargs): 29 | # if self._future is None: 30 | # raise JobError( 31 | # "Job not submitted yet!. You have to .submit() first!") 32 | # return func(self, *args, **kwargs) 33 | # return _wrapper 34 | 35 | 36 | class QCGPUJob(BaseJob): 37 | """QCGPUJob class. 38 | This is a mocking futures class, used only 39 | to fit the API. 40 | 41 | Attributes: 42 | _executor (futures.Executor): executor to handle asynchronous jobs 43 | """ 44 | 45 | if sys.platform in ['darwin', 'win32']: 46 | _executor = futures.ThreadPoolExecutor() 47 | else: 48 | _executor = futures.ProcessPoolExecutor() 49 | 50 | def __init__(self, backend, job_id, val, qobj): 51 | super().__init__(backend, job_id) 52 | self._val = val 53 | self._qobj = qobj 54 | self._future = None 55 | 56 | def submit(self): 57 | """Submit the job to the backend for execution. 58 | Raises: 59 | QobjValidationError: if the JSON serialization of the Qobj passed 60 | during construction does not validate against the Qobj schema. 61 | JobError: if trying to re-submit the job. 62 | # """ 63 | return 64 | # if self._future is not None: 65 | # raise JobError("We have already submitted the job!") 66 | 67 | # validate_qobj_against_schema(self._qobj) 68 | # self._future = self._executor.submit( 69 | # self._fn, self._job_id, self._qobj) 70 | 71 | def result(self, timeout=None): 72 | # pylint: disable=arguments-differ 73 | """Get job result. The behavior is the same as the underlying 74 | concurrent Future objects, 75 | https://docs.python.org/3/library/concurrent.futures.html#future-objects 76 | Args: 77 | timeout (float): number of seconds to wait for results. 78 | Returns: 79 | qiskit.Result: Result object 80 | Raises: 81 | concurrent.futures.TimeoutError: if timeout occurred. 82 | concurrent.futures.CancelledError: if job cancelled before completed. 83 | """ 84 | return self._val 85 | 86 | def cancel(self): 87 | return 88 | # return self._future.cancel() 89 | 90 | def status(self): 91 | """Gets the status of the job by querying the Python's future 92 | Returns: 93 | JobStatus: The current JobStatus 94 | Raises: 95 | JobError: If the future is in unexpected state 96 | concurrent.futures.TimeoutError: if timeout occurred. 97 | """ 98 | return JobStatus.DONE 99 | # # The order is important here 100 | # if self._future.running(): 101 | # _status = JobStatus.RUNNING 102 | # elif self._future.cancelled(): 103 | # _status = JobStatus.CANCELLED 104 | # elif self._future.done(): 105 | # _status = JobStatus.DONE if self._future.exception() is None else JobStatus.ERROR 106 | # else: 107 | # # Note: There is an undocumented Future state: PENDING, that seems to show up when 108 | # # the job is enqueued, waiting for someone to pick it up. We need to deal with this 109 | # # state but there's no public API for it, so we are assuming that if the job is not 110 | # # in any of the previous states, is PENDING, ergo INITIALIZING for 111 | # # us. 112 | # _status = JobStatus.INITIALIZING 113 | 114 | # return _status 115 | 116 | def backend(self): 117 | """Return the instance of the backend used for this job.""" 118 | return self._backend 119 | -------------------------------------------------------------------------------- /tests/case.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import math 4 | import numpy as np 5 | from numpy import random 6 | from scipy import linalg 7 | 8 | from qiskit.circuit.random import random_circuit 9 | 10 | class MyTestCase(unittest.TestCase): 11 | def random_circuit(self, n, depth): 12 | # return build_model_circuits(n, depth) 13 | return random_circuit(n_qubits=n, depth=depth) 14 | 15 | def assertDictAlmostEqual(self, dict1, dict2, delta=None, msg=None, 16 | places=None, default_value=0): 17 | """ 18 | Assert two dictionaries with numeric values are almost equal. 19 | 20 | Fail if the two dictionaries are unequal as determined by 21 | comparing that the difference between values with the same key are 22 | not greater than delta (default 1e-8), or that difference rounded 23 | to the given number of decimal places is not zero. If a key in one 24 | dictionary is not in the other the default_value keyword argument 25 | will be used for the missing value (default 0). If the two objects 26 | compare equal then they will automatically compare almost equal. 27 | 28 | Args: 29 | dict1 (dict): a dictionary. 30 | dict2 (dict): a dictionary. 31 | delta (number): threshold for comparison (defaults to 1e-8). 32 | msg (str): return a custom message on failure. 33 | places (int): number of decimal places for comparison. 34 | default_value (number): default value for missing keys. 35 | 36 | Raises: 37 | TypeError: raises TestCase failureException if the test fails. 38 | """ 39 | if dict1 == dict2: 40 | # Shortcut 41 | return 42 | if delta is not None and places is not None: 43 | raise TypeError("specify delta or places not both") 44 | 45 | if places is not None: 46 | success = True 47 | standard_msg = '' 48 | # check value for keys in target 49 | keys1 = set(dict1.keys()) 50 | for key in keys1: 51 | val1 = dict1.get(key, default_value) 52 | val2 = dict2.get(key, default_value) 53 | if round(abs(val1 - val2), places) != 0: 54 | success = False 55 | standard_msg += '(%s: %s != %s), ' % (safe_repr(key), 56 | safe_repr(val1), 57 | safe_repr(val2)) 58 | # check values for keys in counts, not in target 59 | keys2 = set(dict2.keys()) - keys1 60 | for key in keys2: 61 | val1 = dict1.get(key, default_value) 62 | val2 = dict2.get(key, default_value) 63 | if round(abs(val1 - val2), places) != 0: 64 | success = False 65 | standard_msg += '(%s: %s != %s), ' % (safe_repr(key), 66 | safe_repr(val1), 67 | safe_repr(val2)) 68 | if success is True: 69 | return 70 | standard_msg = standard_msg[:-2] + ' within %s places' % places 71 | 72 | else: 73 | if delta is None: 74 | delta = 1e-8 # default delta value 75 | success = True 76 | standard_msg = '' 77 | # check value for keys in target 78 | keys1 = set(dict1.keys()) 79 | for key in keys1: 80 | val1 = dict1.get(key, default_value) 81 | val2 = dict2.get(key, default_value) 82 | if abs(val1 - val2) > delta: 83 | success = False 84 | standard_msg += '(%s: %s != %s), ' % (safe_repr(key), 85 | safe_repr(val1), 86 | safe_repr(val2)) 87 | # check values for keys in counts, not in target 88 | keys2 = set(dict2.keys()) - keys1 89 | for key in keys2: 90 | val1 = dict1.get(key, default_value) 91 | val2 = dict2.get(key, default_value) 92 | if abs(val1 - val2) > delta: 93 | success = False 94 | standard_msg += '(%s: %s != %s), ' % (safe_repr(key), 95 | safe_repr(val1), 96 | safe_repr(val2)) 97 | if success is True: 98 | return 99 | standard_msg = standard_msg[:-2] + ' within %s delta' % delta 100 | 101 | msg = self._formatMessage(msg, standard_msg) 102 | raise self.failureException(msg) 103 | -------------------------------------------------------------------------------- /qiskit_qcgpu_provider/statevector_simulator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains an OpenCL based simulator. 3 | 4 | How to use this simulator: 5 | see examples/qcgpu_backends.py 6 | 7 | Advantages: 8 | 1. This backend takes advantage of the speedup that GPUs can give 9 | to parallel computation. 10 | 2. This backend works on every device that has an OpenCL implementation, 11 | which includes Macbooks, gaming computers, Nvidia and AMD cards etc. 12 | 3. Supports u gates. 13 | 14 | Limitations: 15 | 1. Memory on the GPU is usually a lot less than that of a full machine, 16 | which can limit the number of qubits simulated. However, you can use 17 | the program on the CPU. 18 | 2. As with all simulators, there is a limit to how accurate your results are. 19 | 3. Many OpenCL devices don't support doubles, thus the simulator is limited to 20 | using floats. 21 | """ 22 | 23 | import uuid 24 | import logging 25 | import time 26 | import numpy as np 27 | 28 | from qiskit.providers import BaseBackend 29 | from qiskit.result import Result 30 | from qiskit.result.models import ExperimentResult, ExperimentResultData 31 | from qiskit.providers.models import BackendConfiguration 32 | from qiskit.validation.base import Obj 33 | 34 | import qcgpu 35 | 36 | from .job import QCGPUJob 37 | from .simulatorerror import QCGPUSimulatorError 38 | 39 | logger = logging.getLogger(__name__) 40 | 41 | 42 | class QCGPUStatevectorSimulator(BaseBackend): 43 | """Contains an OpenCL based backend""" 44 | 45 | MAX_QUBITS_MEMORY = 30 # Should do something smarter here, 46 | # but needs to be implemented on the QCGPU side. 47 | # Will also depend on choosing the optimal backend. 48 | 49 | DEFAULT_CONFIGURATION = {'backend_name': 'statevector_simulator', 50 | 'backend_version': '1.0.0', 51 | 'n_qubits': MAX_QUBITS_MEMORY, 52 | 'url': 'https://qcgpu.github.io', 53 | 'simulator': True, 54 | 'local': True, 55 | 'conditional': False, 56 | 'open_pulse': False, 57 | 'memory': True, 58 | 'max_shots': 65536, 59 | 'description': 'An OpenCL based statevector simulator', 60 | 'coupling_map': None, 61 | 'basis_gates': ['u1', 62 | 'u2', 63 | 'u3', 64 | 'cx', 65 | 'id', 66 | 'x', 67 | 'y', 68 | 'z', 69 | 'h', 70 | 's', 71 | 't'], 72 | 'gates': [{'name': 'u1', 73 | 'parameters': ['lambda'], 74 | 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'}, 75 | {'name': 'u2', 76 | 'parameters': ['phi', 77 | 'lambda'], 78 | 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'}, 79 | {'name': 'u3', 80 | 'parameters': ['theta', 81 | 'phi', 82 | 'lambda'], 83 | 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'}, 84 | {'name': 'cx', 85 | 'parameters': ['c', 86 | 't'], 87 | 'qasm_def': 'gate cx c,t { CX c,t; }'}, 88 | {'name': 'id', 89 | 'parameters': ['a'], 90 | 'qasm_def': 'gate id a { U(0,0,0) a; }'}, 91 | {'name': 'x', 92 | 'parameters': ['a'], 93 | 'qasm_def': 'gate x a { u3(pi,0,pi) a; }'}, 94 | {'name': 'y', 95 | 'parameters': ['a'], 96 | 'qasm_def': 'gate y a { u3(pi,pi/2,pi/2) a; }'}, 97 | {'name': 'z', 98 | 'parameters': ['z'], 99 | 'qasm_def': 'gate z a { u1(pi) a; }'}, 100 | {'name': 'h', 101 | 'parameters': ['a'], 102 | 'qasm_def': 'gate h a { u2(0,pi) a; }'}, 103 | {'name': 's', 104 | 'parameters': ['a'], 105 | 'qasm_def': 'gate s a { u1(pi/2) a; }'}, 106 | {'name': 't', 107 | 'parameters': ['a'], 108 | 'qasm_def': 'gate t a { u1(pi/4) a; }'}, 109 | ]} 110 | 111 | def __init__(self, configuration=None, provider=None): 112 | configuration = configuration or BackendConfiguration.from_dict( 113 | self.DEFAULT_CONFIGURATION) 114 | super().__init__(configuration=configuration, provider=provider) 115 | 116 | self._configuration = configuration 117 | self._number_of_qubits = None 118 | self._statevector = None 119 | self._results = {} 120 | self._chop_threshold = 15 # chop to 10^-15 121 | 122 | def run(self, qobj): 123 | """Run qobj asynchronously. 124 | 125 | Args: 126 | qobj (Qobj): payload of the experiment 127 | 128 | Returns: 129 | QCGPUJob: derived from BaseJob 130 | """ 131 | 132 | qcgpu.backend._create_context() 133 | 134 | job_id = str(uuid.uuid4()) 135 | job = QCGPUJob(self, job_id, self._run_job(job_id, qobj), qobj) 136 | return job 137 | 138 | # @profile 139 | def _run_job(self, job_id, qobj): 140 | """Run experiments in qobj 141 | 142 | Args: 143 | job_id (str): unique id for the job. 144 | qobj (Qobj): job description 145 | 146 | Returns: 147 | Result: Result object 148 | """ 149 | self._validate(qobj) 150 | results = [] 151 | 152 | start = time.time() 153 | for experiment in qobj.experiments: 154 | results.append(self.run_experiment(experiment)) 155 | end = time.time() 156 | 157 | result = Result( 158 | backend_name=self.name(), 159 | backend_version=self._configuration.backend_version, 160 | qobj_id=qobj.qobj_id, 161 | job_id=job_id, 162 | success=True, 163 | results=results, 164 | time_taken=(end - start) 165 | ) 166 | 167 | return result 168 | 169 | # @profile 170 | def run_experiment(self, experiment): 171 | """Run an experiment (circuit) and return a single experiment result. 172 | 173 | Args: 174 | experiment (QobjExperiment): experiment from qobj experiments list 175 | 176 | Returns: 177 | dict: A dictionary of results. 178 | dict: A result dictionary 179 | 180 | Raises: 181 | QCGPUSimulatorError: If the number of qubits is too large, or another 182 | error occurs during execution. 183 | """ 184 | self._number_of_qubits = experiment.header.n_qubits 185 | self._statevector = 0 186 | 187 | start = time.time() 188 | 189 | try: 190 | sim = qcgpu.State(self._number_of_qubits) 191 | except OverflowError: 192 | raise QCGPUSimulatorError('too many qubits') 193 | 194 | for operation in experiment.instructions: 195 | name = operation.name 196 | qubits = operation.qubits 197 | params = [float(param) 198 | for param in getattr(operation, 'params', [])] 199 | 200 | if name == 'id': 201 | logger.info('Identity gates are ignored.') 202 | elif name == 'barrier': 203 | logger.info('Barrier gates are ignored.') 204 | elif name == 'u3': 205 | sim.u(qubits[0], *params) 206 | elif name == 'u2': 207 | sim.u2(qubits[0], *params) 208 | elif name == 'u1': 209 | sim.u1(qubits[0], *params) 210 | elif name == 'cx': 211 | sim.cx(*qubits) 212 | elif name == 'h': 213 | sim.h(qubits[0]) 214 | elif name == 'x': 215 | sim.x(qubits[0]) 216 | elif name == 'y': 217 | sim.y(qubits[0]) 218 | elif name == 'z': 219 | sim.z(qubits[0]) 220 | elif name == 's': 221 | sim.s(qubits[0]) 222 | elif name == 't': 223 | sim.t(qubits[0]) 224 | 225 | amps = [complex(z) 226 | for z in sim.amplitudes().round(self._chop_threshold)] 227 | 228 | end = time.time() 229 | 230 | # amps = np.stack((amps.real, amps.imag), axis=-1) 231 | 232 | return ExperimentResult( 233 | name=experiment.header.name, 234 | shots=1, 235 | success=True, 236 | data=ExperimentResultData(statevector=amps), 237 | time_taken=(end - start), 238 | header=Obj(name=experiment.header.name)) 239 | 240 | # @profile 241 | 242 | def _validate(self, qobj): 243 | """ 244 | Make sure that there is: 245 | 1. No shots 246 | 2. No measurements until the end 247 | """ 248 | if qobj.config.shots != 1: 249 | logger.info('"%s" only supports 1 shot. Setting shots=1.', 250 | self.name()) 251 | qobj.config.shots = 1 252 | 253 | for experiment in qobj.experiments: 254 | name = experiment.header.name 255 | 256 | if getattr(experiment.config, 'shots', 1) != 1: 257 | logger.info( 258 | '"%s" only supports 1 shot. Setting shots=1 for circuit "%s".', 259 | self.name(), name) 260 | experiment.config.shots = 1 261 | 262 | for operation in experiment.instructions: 263 | if operation.name in ['measure', 'reset']: 264 | raise QCGPUSimulatorError( 265 | 'Unsupported "{}" instruction "{}" in circuit "{}"'.format( 266 | self.name(), 267 | operation.name, 268 | name)) 269 | 270 | @staticmethod 271 | def name(): 272 | return 'statevector_simulator' 273 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Adam Kelly 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright 2018 Adam Kelly 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /qiskit_qcgpu_provider/qasm_simulator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains an OpenCL based simulator. 3 | 4 | How to use this simulator: 5 | see examples/qcgpu_backends.py 6 | 7 | Advantages: 8 | 1. This backend takes advantage of the speedup that GPUs can give 9 | to parallel computation. 10 | 2. This backend works on every device that has an OpenCL implementation, 11 | which includes Macbooks, gaming computers, Nvidia and AMD cards etc. 12 | 3. Supports u gates. 13 | 14 | Limitations: 15 | 1. Memory on the GPU is usually a lot less than that of a full machine, 16 | which can limit the number of qubits simulated. However, you can use 17 | the program on the CPU. 18 | 2. As with all simulators, there is a limit to how accurate your results are. 19 | 3. Many OpenCL devices don't support doubles, thus the simulator is limited to 20 | using floats. 21 | """ 22 | 23 | import uuid 24 | import logging 25 | import time 26 | from collections import Counter 27 | 28 | import numpy as np 29 | 30 | from qiskit.providers import BaseBackend 31 | from qiskit.result import Result 32 | from qiskit.providers.models import BackendConfiguration 33 | 34 | import qcgpu 35 | 36 | from .job import QCGPUJob 37 | from .simulatorerror import QCGPUSimulatorError 38 | 39 | logger = logging.getLogger(__name__) 40 | 41 | 42 | class QCGPUQasmSimulator(BaseBackend): 43 | """Contains an OpenCL based backend""" 44 | 45 | MAX_QUBITS_MEMORY = 30 # Should do something smarter here, 46 | # but needs to be implemented on the QCGPU side. 47 | # Will also depend on choosing the optimal backend. 48 | 49 | DEFAULT_CONFIGURATION = {'backend_name': 'qasm_simulator', 50 | 'backend_version': '1.0.0', 51 | 'n_qubits': MAX_QUBITS_MEMORY, 52 | 'url': 'https://qcgpu.github.io', 53 | 'simulator': True, 54 | 'local': True, 55 | 'conditional': False, 56 | 'open_pulse': False, 57 | 'memory': True, 58 | 'max_shots': 65536, 59 | 'description': 'An OpenCL based qasm simulator', 60 | 'coupling_map': None, 61 | 'basis_gates': ['u1', 62 | 'u2', 63 | 'u3', 64 | 'cx', 65 | 'id', 66 | 'x', 67 | 'y', 68 | 'z', 69 | 'h', 70 | 's', 71 | 't'], 72 | 'gates': [{'name': 'u1', 73 | 'parameters': ['lambda'], 74 | 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'}, 75 | {'name': 'u2', 76 | 'parameters': ['phi', 77 | 'lambda'], 78 | 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'}, 79 | {'name': 'u3', 80 | 'parameters': ['theta', 81 | 'phi', 82 | 'lambda'], 83 | 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'}, 84 | {'name': 'cx', 85 | 'parameters': ['c', 86 | 't'], 87 | 'qasm_def': 'gate cx c,t { CX c,t; }'}, 88 | {'name': 'id', 89 | 'parameters': ['a'], 90 | 'qasm_def': 'gate id a { U(0,0,0) a; }'}, 91 | {'name': 'x', 92 | 'parameters': ['a'], 93 | 'qasm_def': 'gate x a { u3(pi,0,pi) a; }'}, 94 | {'name': 'y', 95 | 'parameters': ['a'], 96 | 'qasm_def': 'gate y a { u3(pi,pi/2,pi/2) a; }'}, 97 | {'name': 'z', 98 | 'parameters': ['z'], 99 | 'qasm_def': 'gate z a { u1(pi) a; }'}, 100 | {'name': 'h', 101 | 'parameters': ['a'], 102 | 'qasm_def': 'gate h a { u2(0,pi) a; }'}, 103 | {'name': 's', 104 | 'parameters': ['a'], 105 | 'qasm_def': 'gate s a { u1(pi/2) a; }'}, 106 | {'name': 't', 107 | 'parameters': ['a'], 108 | 'qasm_def': 'gate t a { u1(pi/4) a; }'}, 109 | ]} 110 | 111 | def __init__(self, configuration=None, provider=None): 112 | configuration = configuration or BackendConfiguration.from_dict( 113 | self.DEFAULT_CONFIGURATION) 114 | super().__init__(configuration=configuration, provider=provider) 115 | 116 | self._configuration = configuration 117 | self._number_of_qubits = None 118 | self._number_of_cbits = None 119 | self._statevector = None 120 | self._results = {} 121 | self._shots = {} 122 | self._local_random = np.random.RandomState() 123 | self._sample_measure = False 124 | self._chop_threshold = 15 # chop to 10^-15 125 | 126 | #@profile 127 | def run(self, qobj): 128 | """Run qobj asynchronously. 129 | 130 | Args: 131 | qobj (Qobj): payload of the experiment 132 | 133 | Returns: 134 | QCGPUJob: derived from BaseJob 135 | """ 136 | qcgpu.backend._create_context() 137 | 138 | job_id = str(uuid.uuid4()) 139 | job = QCGPUJob(self, job_id, self._run_job(job_id, qobj), qobj) 140 | return job 141 | #@profile 142 | def _run_job(self, job_id, qobj): 143 | """Run experiments in qobj 144 | 145 | Args: 146 | job_id (str): unique id for the job. 147 | qobj (Qobj): job description 148 | 149 | Returns: 150 | Result: Result object 151 | """ 152 | self._shots = qobj.config.shots 153 | self._memory = qobj.config.memory 154 | self._qobj_config = qobj.config 155 | results = [] 156 | 157 | start = time.time() 158 | for experiment in qobj.experiments: 159 | results.append(self.run_experiment(experiment)) 160 | end = time.time() 161 | 162 | result = { 163 | 'backend_name': self.name(), 164 | 'backend_version': self._configuration.backend_version, 165 | 'qobj_id': qobj.qobj_id, 166 | 'job_id': job_id, 167 | 'results': results, 168 | 'status': 'COMPLETED', 169 | 'success': True, 170 | 'time_taken': (end - start), 171 | 'header': qobj.header.to_dict() 172 | } 173 | 174 | return Result.from_dict(result) 175 | 176 | #@profile 177 | def run_experiment(self, experiment): 178 | """Run an experiment (circuit) and return a single experiment result. 179 | 180 | Args: 181 | experiment (QobjExperiment): experiment from qobj experiments list 182 | 183 | Returns: 184 | dict: A dictionary of results. 185 | dict: A result dictionary 186 | 187 | Raises: 188 | QCGPUSimulatorError: If the number of qubits is too large, or another 189 | error occurs during execution. 190 | """ 191 | self._number_of_qubits = experiment.header.n_qubits 192 | self._number_of_cbits = experiment.header.memory_slots 193 | self._classical_state = 0 194 | self._statevector = 0 195 | 196 | if hasattr(experiment.config, 'seed'): 197 | seed = experiment.config.seed 198 | elif hasattr(self._qobj_config, 'seed'): 199 | seed = self._qobj_config.seed 200 | else: 201 | # For compatibility on Windows force dyte to be int32 202 | # and set the maximum value to be (2 ** 31) - 1 203 | seed = np.random.randint(2147483647, dtype='int32') 204 | self._local_random.seed(seed) 205 | 206 | self._can_sample(experiment) 207 | 208 | if not self._sample_measure: 209 | raise QCGPUSimulatorError('Measurements are only supported at the end') 210 | 211 | experiment = experiment.to_dict() 212 | # qcgpu.backend.create_context() 213 | 214 | samples = [] 215 | 216 | start = time.time() 217 | 218 | try: 219 | sim = qcgpu.State(self._number_of_qubits) 220 | except OverflowError: 221 | raise QCGPUSimulatorError('too many qubits') 222 | 223 | for operation in experiment['instructions']: 224 | params = operation.get('params', []) 225 | name = operation['name'] 226 | 227 | if name == 'id': 228 | logger.info('Identity gates are ignored.') 229 | elif name == 'barrier': 230 | logger.info('Barrier gates are ignored.') 231 | elif name == 'u3': 232 | sim.u(operation['qubits'][0], *params) 233 | elif name == 'u2': 234 | sim.u2(operation['qubits'][0], *params) 235 | elif name == 'u1': 236 | sim.u1(operation['qubits'][0], *params) 237 | elif name == 'cx': 238 | sim.cx(*operation['qubits']) 239 | elif name == 'h': 240 | sim.h(operation['qubits'][0]) 241 | elif name == 'x': 242 | sim.x(operation['qubits'][0]) 243 | elif name == 'y': 244 | sim.y(operation['qubits'][0]) 245 | elif name == 'z': 246 | sim.z(operation['qubits'][0]) 247 | elif name == 's': 248 | sim.s(operation['qubits'][0]) 249 | elif name == 't': 250 | sim.t(operation['qubits'][0]) 251 | elif name == 'measure': 252 | samples.append((operation['qubits'][0], operation['memory'][0])) 253 | 254 | if self._number_of_cbits > 0: 255 | memory = self._add_sample_measure(samples, sim, self._shots) 256 | else: 257 | memory = [] 258 | 259 | end = time.time() 260 | 261 | # amps = sim.amplitudes().round(self._chop_threshold) 262 | # amps = np.stack((amps.real, amps.imag), axis=-1) 263 | 264 | data = {'counts': dict(Counter(memory))} 265 | 266 | if self._memory: 267 | data['memory'] = memory 268 | 269 | return { 270 | 'name': experiment['header']['name'], 271 | 'shots': self._shots, 272 | 'data': data, 273 | 'seed': seed, 274 | 'status': 'DONE', 275 | 'success': True, 276 | 'time_taken': (end - start), 277 | 'header': experiment['header'] 278 | } 279 | 280 | #@profile 281 | def _add_sample_measure(self, measure_params, sim, num_samples): 282 | """Generate memory samples from current statevector. 283 | Taken almost straight from the terra source code. 284 | 285 | Args: 286 | measure_params (list): List of (qubit, clbit) values for 287 | measure instructions to sample. 288 | num_samples (int): The number of memory samples to generate. 289 | Returns: 290 | list: A list of memory values in hex format. 291 | """ 292 | probabilities = np.reshape(sim.probabilities(), self._number_of_qubits * [2]) 293 | 294 | # Get unique qubits that are actually measured 295 | measured_qubits = list(set([qubit for qubit, clbit in measure_params])) 296 | num_measured = len(measured_qubits) 297 | 298 | 299 | # Axis for numpy.sum to compute probabilities 300 | axis = list(range(self._number_of_qubits)) 301 | 302 | for qubit in reversed(measured_qubits): 303 | # Remove from largest qubit to smallest so list position is correct 304 | # with respect to position from end of the list 305 | axis.remove(self._number_of_qubits - 1 - qubit) 306 | 307 | 308 | probabilities = np.reshape(np.sum(probabilities, 309 | axis=tuple(axis)), 310 | 2 ** num_measured) 311 | 312 | # Normalize probabilities when the ammount do not sum 1 because of numeric error 313 | normalized_probabilitites = [] 314 | prob_sum = sum(probabilities) 315 | 316 | for i in probabilities: 317 | normalized_probabilitites.append(i/prob_sum) 318 | 319 | # Generate samples on measured qubits 320 | samples = self._local_random.choice(range(2 ** num_measured), 321 | num_samples, p=normalized_probabilitites) 322 | # Convert to bit-strings 323 | memory = [] 324 | for sample in samples: 325 | classical_state = self._classical_state 326 | for qubit, cbit in measure_params: 327 | qubit_outcome = int((sample & (1 << qubit)) >> qubit) 328 | bit = 1 << cbit 329 | classical_state = (classical_state & (~bit)) | (qubit_outcome << cbit) 330 | value = bin(classical_state)[2:] 331 | memory.append(hex(int(value, 2))) 332 | return memory 333 | 334 | #@profile 335 | def _can_sample(self, experiment): 336 | """Determine if sampling can be used for an experiment 337 | 338 | Args: 339 | experiment (QobjExperiment): a qobj experiment 340 | """ 341 | measure_flags = {} 342 | if hasattr(experiment.config, 'allows_measure_sampling'): 343 | self._sample_measure = experiment.config.allows_measure_sampling 344 | else: 345 | 346 | for instruction in experiment.instructions: 347 | if instruction.name == "reset": 348 | measure_flags[instruction.qubits[0]] = False 349 | self._sample_measure = False 350 | return 351 | 352 | if measure_flags.get(instruction.qubits[0], False): 353 | if instruction.name not in ["measure", "barrier", "id", "u0"]: 354 | for qubit in instruction.qubits: 355 | measure_flags[qubit] = False 356 | return 357 | elif instruction.name == "measure": 358 | for qubit in instruction.qubits: 359 | measure_flags[qubit] = True 360 | 361 | self._sample_measure = True 362 | 363 | for key, value in measure_flags.items(): 364 | if value == False: 365 | self._sample_measure = False 366 | return 367 | 368 | @staticmethod 369 | def name(): 370 | return 'qasm_simulator' 371 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # Specify a configuration file. 4 | #rcfile= 5 | 6 | # Python code to execute, usually for sys.path manipulation such as 7 | # pygtk.require(). 8 | #init-hook= 9 | 10 | # Add files or directories to the blacklist. They should be base names, not 11 | # paths. 12 | ignore=CVS 13 | 14 | # Add files or directories matching the regex patterns to the blacklist. The 15 | # regex matches against base names, not paths. 16 | ignore-patterns= 17 | 18 | # Pickle collected data for later comparisons. 19 | persistent=yes 20 | 21 | # List of plugins (as comma separated values of python modules names) to load, 22 | # usually to register additional checkers. 23 | load-plugins=pylint.extensions.docparams # enable checking of docstring args 24 | 25 | # Use multiple processes to speed up Pylint. 26 | jobs=1 27 | 28 | # Allow loading of arbitrary C extensions. Extensions are imported into the 29 | # active Python interpreter and may run arbitrary code. 30 | unsafe-load-any-extension=no 31 | 32 | # A comma-separated list of package or module names from where C extensions may 33 | # be loaded. Extensions are loading into the active Python interpreter and may 34 | # run arbitrary code 35 | extension-pkg-whitelist=numpy 36 | 37 | # Allow optimization of some AST trees. This will activate a peephole AST 38 | # optimizer, which will apply various small optimizations. For instance, it can 39 | # be used to obtain the result of joining multiple strings with the addition 40 | # operator. Joining a lot of strings can lead to a maximum recursion error in 41 | # Pylint and this flag can prevent that. It has one side effect, the resulting 42 | # AST will be different than the one from reality. This option is deprecated 43 | # and it will be removed in Pylint 2.0. 44 | optimize-ast=no 45 | 46 | 47 | [MESSAGES CONTROL] 48 | 49 | # Only show warnings with the listed confidence levels. Leave empty to show 50 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED 51 | confidence= 52 | 53 | # Enable the message, report, category or checker with the given id(s). You can 54 | # either give multiple identifier separated by comma (,) or put this option 55 | # multiple time (only on the command line, not in the configuration file where 56 | # it should appear only once). See also the "--disable" option for examples. 57 | #enable= 58 | 59 | # Disable the message, report, category or checker with the given id(s). You 60 | # can either give multiple identifiers separated by comma (,) or put this 61 | # option multiple times (only on the command line, not in the configuration 62 | # file where it should appear only once).You can also use "--disable=all" to 63 | # disable everything first and then reenable specific checks. For example, if 64 | # you want to run only the similarities checker, you can use "--disable=all 65 | # --enable=similarities". If you want to run only the classes checker, but have 66 | # no Warning level messages displayed, use"--disable=all --enable=classes 67 | # --disable=W" 68 | disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,import-star-module-level,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,long-suffix,old-ne-operator,old-octal-literal,suppressed-message,useless-suppression, 69 | no-self-use, # disabled as it is too verbose 70 | fixme, # disabled as TODOs would show up as warnings 71 | protected-access, # disabled as we don't follow the public vs private 72 | # convention strictly 73 | duplicate-code, # disabled as it is too verbose 74 | # disable the "too-many/few-..." refactoring hints 75 | too-many-lines, too-many-branches, too-many-locals, too-many-nested-blocks, 76 | too-many-statements, too-many-instance-attributes, too-many-arguments, 77 | too-many-public-methods, too-few-public-methods 78 | 79 | 80 | 81 | [REPORTS] 82 | 83 | # Set the output format. Available formats are text, parseable, colorized, msvs 84 | # (visual studio) and html. You can also give a reporter class, eg 85 | # mypackage.mymodule.MyReporterClass. 86 | output-format=text 87 | 88 | # Put messages in a separate file for each module / package specified on the 89 | # command line instead of printing them on stdout. Reports (if any) will be 90 | # written in a file name "pylint_global.[txt|html]". This option is deprecated 91 | # and it will be removed in Pylint 2.0. 92 | files-output=no 93 | 94 | # Tells whether to display a full report or only the messages 95 | reports=yes 96 | 97 | # Python expression which should return a note less than 10 (10 is the highest 98 | # note). You have access to the variables errors warning, statement which 99 | # respectively contain the number of errors / warnings messages and the total 100 | # number of statements analyzed. This is used by the global evaluation report 101 | # (RP0004). 102 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 103 | 104 | # Template used to display messages. This is a python new-style format string 105 | # used to format the message information. See doc for all details 106 | #msg-template= 107 | 108 | 109 | [BASIC] 110 | 111 | # Good variable names which should always be accepted, separated by a comma 112 | good-names=i,j,k,n,m,ex,Run,_,logger 113 | 114 | # Bad variable names which should always be refused, separated by a comma 115 | bad-names=foo,bar,toto,tutu,tata 116 | 117 | # Colon-delimited sets of names that determine each other's naming style when 118 | # the name regexes allow several styles. 119 | name-group= 120 | 121 | # Include a hint for the correct naming format with invalid-name 122 | include-naming-hint=no 123 | 124 | # List of decorators that produce properties, such as abc.abstractproperty. Add 125 | # to this list to register other decorators that produce valid properties. 126 | property-classes=abc.abstractproperty 127 | 128 | # Regular expression matching correct module names 129 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 130 | 131 | # Naming hint for module names 132 | module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 133 | 134 | # Regular expression matching correct constant names 135 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 136 | 137 | # Naming hint for constant names 138 | const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 139 | 140 | # Regular expression matching correct class names 141 | class-rgx=[A-Z_][a-zA-Z0-9]+$ 142 | 143 | # Naming hint for class names 144 | class-name-hint=[A-Z_][a-zA-Z0-9]+$ 145 | 146 | # Regular expression matching correct function names 147 | function-rgx=[a-z_][a-z0-9_]{2,30}$ 148 | 149 | # Naming hint for function names 150 | function-name-hint=[a-z_][a-z0-9_]{2,30}$ 151 | 152 | # Regular expression matching correct method names 153 | method-rgx=[a-z_][a-z0-9_]{2,30}$ 154 | 155 | # Naming hint for method names 156 | method-name-hint=[a-z_][a-z0-9_]{2,30}$ 157 | 158 | # Regular expression matching correct attribute names 159 | attr-rgx=[a-z_][a-z0-9_]{2,30}$ 160 | 161 | # Naming hint for attribute names 162 | attr-name-hint=[a-z_][a-z0-9_]{2,30}$ 163 | 164 | # Regular expression matching correct argument names 165 | argument-rgx=[a-z_][a-z0-9_]{2,30}$ 166 | 167 | # Naming hint for argument names 168 | argument-name-hint=[a-z_][a-z0-9_]{2,30}$ 169 | 170 | # Regular expression matching correct variable names 171 | variable-rgx=[a-z_][a-z0-9_]{2,30}$ 172 | 173 | # Naming hint for variable names 174 | variable-name-hint=[a-z_][a-z0-9_]{2,30}$ 175 | 176 | # Regular expression matching correct class attribute names 177 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 178 | 179 | # Naming hint for class attribute names 180 | class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 181 | 182 | # Regular expression matching correct inline iteration names 183 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ 184 | 185 | # Naming hint for inline iteration names 186 | inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ 187 | 188 | # Regular expression which should only match function or class names that do 189 | # not require a docstring. 190 | no-docstring-rgx=^_ 191 | 192 | # Minimum line length for functions/classes that require docstrings, shorter 193 | # ones are exempt. 194 | docstring-min-length=-1 195 | 196 | 197 | [ELIF] 198 | 199 | # Maximum number of nested blocks for function / method body 200 | max-nested-blocks=5 201 | 202 | 203 | [FORMAT] 204 | 205 | # Maximum number of characters on a single line. 206 | max-line-length=100 207 | 208 | # Regexp for a line that is allowed to be longer than the limit. 209 | ignore-long-lines=^\s*(# )??$ 210 | 211 | # Allow the body of an if to be on the same line as the test if there is no 212 | # else. 213 | single-line-if-stmt=no 214 | 215 | # List of optional constructs for which whitespace checking is disabled. `dict- 216 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 217 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 218 | # `empty-line` allows space-only lines. 219 | no-space-check=trailing-comma,dict-separator 220 | 221 | # Maximum number of lines in a module 222 | max-module-lines=1000 223 | 224 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 225 | # tab). 226 | indent-string=' ' 227 | 228 | # Number of spaces of indent required inside a hanging or continued line. 229 | indent-after-paren=4 230 | 231 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 232 | expected-line-ending-format= 233 | 234 | 235 | [LOGGING] 236 | 237 | # Logging modules to check that the string format arguments are in logging 238 | # function parameter format 239 | logging-modules=logging 240 | 241 | 242 | [MISCELLANEOUS] 243 | 244 | # List of note tags to take in consideration, separated by a comma. 245 | notes=FIXME,XXX,TODO 246 | 247 | 248 | [SIMILARITIES] 249 | 250 | # Minimum lines number of a similarity. 251 | min-similarity-lines=4 252 | 253 | # Ignore comments when computing similarities. 254 | ignore-comments=yes 255 | 256 | # Ignore docstrings when computing similarities. 257 | ignore-docstrings=yes 258 | 259 | # Ignore imports when computing similarities. 260 | ignore-imports=no 261 | 262 | 263 | [SPELLING] 264 | 265 | # Spelling dictionary name. Available dictionaries: none. To make it working 266 | # install python-enchant package. 267 | spelling-dict= 268 | 269 | # List of comma separated words that should not be checked. 270 | spelling-ignore-words= 271 | 272 | # A path to a file that contains private dictionary; one word per line. 273 | spelling-private-dict-file= 274 | 275 | # Tells whether to store unknown words to indicated private dictionary in 276 | # --spelling-private-dict-file option instead of raising a message. 277 | spelling-store-unknown-words=no 278 | 279 | 280 | [TYPECHECK] 281 | 282 | # Tells whether missing members accessed in mixin class should be ignored. A 283 | # mixin class is detected if its name ends with "mixin" (case insensitive). 284 | ignore-mixin-members=yes 285 | 286 | # List of module names for which member attributes should not be checked 287 | # (useful for modules/projects where namespaces are manipulated during runtime 288 | # and thus existing member attributes cannot be deduced by static analysis. It 289 | # supports qualified module names, as well as Unix pattern matching. 290 | ignored-modules=matplotlib.cm,sympy 291 | 292 | # List of class names for which member attributes should not be checked (useful 293 | # for classes with dynamically set attributes). This supports the use of 294 | # qualified names. 295 | ignored-classes=optparse.Values,thread._local,_thread._local 296 | 297 | # List of members which are set dynamically and missed by pylint inference 298 | # system, and so shouldn't trigger E1101 when accessed. Python regular 299 | # expressions are accepted. 300 | generated-members=self.circuit.*,qcs.h,qc1.h,qc2.cx,qc.h,self.u1,self.cx,self.ccx,trial_circuit 301 | # self.circuit.*: false positives when self.circuit == QuantumCircuit, as it 302 | # provides a "__getitem__" dynamic method and gate definition is dynamic. 303 | # For qiskit.extensions.standard, self.foo is also needed. 304 | 305 | # List of decorators that produce context managers, such as 306 | # contextlib.contextmanager. Add to this list to register other decorators that 307 | # produce valid context managers. 308 | contextmanager-decorators=contextlib.contextmanager 309 | 310 | 311 | [VARIABLES] 312 | 313 | # Tells whether we should check for unused import in __init__ files. 314 | init-import=no 315 | 316 | # A regular expression matching the name of dummy variables (i.e. expectedly 317 | # not used). 318 | dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy 319 | 320 | # List of additional names supposed to be defined in builtins. Remember that 321 | # you should avoid to define new builtins when possible. 322 | additional-builtins= 323 | 324 | # List of strings which can identify a callback function by name. A callback 325 | # name must start or end with one of those strings. 326 | callbacks=cb_,_cb 327 | 328 | # List of qualified module names which can have objects that can redefine 329 | # builtins. 330 | redefining-builtins-modules=six.moves,future.builtins 331 | 332 | 333 | [CLASSES] 334 | 335 | # List of method names used to declare (i.e. assign) instance attributes. 336 | defining-attr-methods=__init__,__new__,setUp 337 | 338 | # List of valid names for the first argument in a class method. 339 | valid-classmethod-first-arg=cls 340 | 341 | # List of valid names for the first argument in a metaclass class method. 342 | valid-metaclass-classmethod-first-arg=mcs 343 | 344 | # List of member names, which should be excluded from the protected access 345 | # warning. 346 | exclude-protected=_asdict,_fields,_replace,_source,_make 347 | 348 | 349 | [DESIGN] 350 | 351 | # Maximum number of arguments for function / method 352 | max-args=8 353 | 354 | # Argument names that match this expression will be ignored. Default to name 355 | # with leading underscore 356 | ignored-argument-names=_.* 357 | 358 | # Maximum number of locals for function / method body 359 | max-locals=15 360 | 361 | # Maximum number of return / yield for function / method body 362 | max-returns=6 363 | 364 | # Maximum number of branch for function / method body 365 | max-branches=12 366 | 367 | # Maximum number of statements in function / method body 368 | max-statements=50 369 | 370 | # Maximum number of parents for a class (see R0901). 371 | max-parents=7 372 | 373 | # Maximum number of attributes for a class (see R0902). 374 | max-attributes=10 375 | 376 | # Minimum number of public methods for a class (see R0903). 377 | min-public-methods=2 378 | 379 | # Maximum number of public methods for a class (see R0904). 380 | max-public-methods=35 381 | 382 | # Maximum number of boolean expressions in a if statement 383 | max-bool-expr=5 384 | 385 | 386 | [IMPORTS] 387 | 388 | # Deprecated modules which should not be used, separated by a comma 389 | deprecated-modules=optparse 390 | 391 | # Create a graph of every (i.e. internal and external) dependencies in the 392 | # given file (report RP0402 must not be disabled) 393 | import-graph= 394 | 395 | # Create a graph of external dependencies in the given file (report RP0402 must 396 | # not be disabled) 397 | ext-import-graph= 398 | 399 | # Create a graph of internal dependencies in the given file (report RP0402 must 400 | # not be disabled) 401 | int-import-graph= 402 | 403 | # Force import order to recognize a module as part of the standard 404 | # compatibility libraries. 405 | known-standard-library= 406 | 407 | # Force import order to recognize a module as part of a third party library. 408 | known-third-party=enchant 409 | 410 | # Analyse import fallback blocks. This can be used to support both Python 2 and 411 | # 3 compatible code, which means that the block might have code that exists 412 | # only in one or another interpreter, leading to false positives when analysed. 413 | analyse-fallback-blocks=no 414 | 415 | 416 | [EXCEPTIONS] 417 | 418 | # Exceptions that will emit a warning when being caught. Defaults to 419 | # "Exception" 420 | overgeneral-exceptions=Exception 421 | -------------------------------------------------------------------------------- /examples/aqua/Quantum Support Vector Machine - Breast Cancer Prediction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction\n", 8 | "\n", 9 | "A support vector machine (SVM) is a type of machine learning algorithm. It's purpose is to do classification problems. It can handle continuous and categorical variables.\n", 10 | "The classification method is to construct a hyperplane in multidimensional space to separate different classes.\n", 11 | "SVM generates this hyperplane iteratively, trying to minimize the error.\n", 12 | "\n", 13 | "\"Support" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "# Data\n", 21 | "\n", 22 | "First, a dataset must be prepared. In this example, the Breast Cancer Wisconsin dataset is used. This is a dataset provided by scikit learn.\n", 23 | "\n", 24 | "The data has 30 features (mean radius, mean texture, ....), and is classified in two ways, malignant and benign.\n", 25 | "\n", 26 | "First, some python packages must be imported to do the dataprocessing with." 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 1, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "import numpy as np\n", 36 | "import scipy\n", 37 | "from scipy.linalg import expm\n", 38 | "import matplotlib.pyplot as plt\n", 39 | "from mpl_toolkits.mplot3d import Axes3D\n", 40 | "from sklearn import datasets\n", 41 | "from sklearn.model_selection import train_test_split\n", 42 | "from sklearn.preprocessing import StandardScaler, MinMaxScaler\n", 43 | "from sklearn.decomposition import PCA" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "With these packages, the dataset can now be loaded." 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 2, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "def breast_cancer(training_size, test_size, n, PLOT_DATA=True):\n", 60 | " class_labels = [r'Benign', r'Malignant']\n", 61 | " \n", 62 | " # First the dataset must be imported.\n", 63 | " cancer = datasets.load_breast_cancer()\n", 64 | " \n", 65 | " # To find if the classifier is accurate, a common strategy is\n", 66 | " # to divide the dataset into a training set and a test set.\n", 67 | " # Here the data is divided into 70% training, 30% testing.\n", 68 | " X_train, X_test, Y_train, Y_test = train_test_split(cancer.data, cancer.target, test_size=0.3, random_state=109)\n", 69 | " \n", 70 | " # Now the dataset's features will be standardized\n", 71 | " # to fit a normal distribution.\n", 72 | " scaler = StandardScaler().fit(X_train)\n", 73 | " X_train = scaler.transform(X_train)\n", 74 | " X_test = scaler.transform(X_test)\n", 75 | " \n", 76 | " # To be able to use this data with the given\n", 77 | " # number of qubits, the data must be broken down from\n", 78 | " # 30 dimensions to `n` dimensions.\n", 79 | " # This is done with Principal Component Analysis (PCA),\n", 80 | " # which finds patterns while keeping variation.\n", 81 | " pca = PCA(n_components=n).fit(X_train)\n", 82 | " X_train = pca.transform(X_train)\n", 83 | " X_test = pca.transform(X_test)\n", 84 | "\n", 85 | " # The last step in the data processing is\n", 86 | " # to scale the data to be between -1 and 1\n", 87 | " samples = np.append(X_train, X_test, axis=0)\n", 88 | " minmax_scale = MinMaxScaler((-1, 1)).fit(samples)\n", 89 | " X_train = minmax_scale.transform(X_train)\n", 90 | " X_test = minmax_scale.transform(X_test)\n", 91 | "\n", 92 | " # Now some sample should be picked to train the model from\n", 93 | " training_input = {key: (X_train[Y_train == k, :])[:training_size] for k, key in enumerate(class_labels)}\n", 94 | " test_input = {key: (X_train[Y_train == k, :])[training_size:(\n", 95 | " training_size+test_size)] for k, key in enumerate(class_labels)}\n", 96 | "\n", 97 | " if PLOT_DATA:\n", 98 | " for k in range(0, 2):\n", 99 | " x_axis_data = X_train[Y_train == k, 0][:training_size]\n", 100 | " y_axis_data = X_train[Y_train == k, 1][:training_size]\n", 101 | " \n", 102 | " label = 'Malignant' if k is 1 else 'Benign'\n", 103 | " plt.scatter(x_axis_data, y_axis_data, label=label)\n", 104 | "\n", 105 | " plt.title(\"Breast Cancer Dataset (Dimensionality Reduced With PCA)\")\n", 106 | " plt.legend()\n", 107 | " plt.show()\n", 108 | " \n", 109 | "\n", 110 | " return X_train, training_input, test_input, class_labels" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "# Algorithm Preperation\n", 118 | "\n", 119 | "With the dataset prepared, the dataset can now be used.\n", 120 | "Here, the data is split up so that the algorithm input can be generated." 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 3, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "data": { 130 | "image/png": "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\n", 131 | "text/plain": [ 132 | "
" 133 | ] 134 | }, 135 | "metadata": { 136 | "needs_background": "light" 137 | }, 138 | "output_type": "display_data" 139 | }, 140 | { 141 | "name": "stdout", 142 | "output_type": "stream", 143 | "text": [ 144 | "{'Benign': 0, 'Malignant': 1}\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "from qiskit_aqua.utils import split_dataset_to_data_and_labels\n", 150 | "\n", 151 | "n = 2 # How many features to use (dimensionality)\n", 152 | "training_dataset_size = 20\n", 153 | "testing_dataset_size = 10\n", 154 | "\n", 155 | "sample_Total, training_input, test_input, class_labels = breast_cancer(training_dataset_size, testing_dataset_size, n)\n", 156 | "\n", 157 | "datapoints, class_to_label = split_dataset_to_data_and_labels(test_input)\n", 158 | "print(class_to_label)\n" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "The algorithm inputs are initialized before.\n", 166 | "To build the Support Vector Machine, a feature map has to be initialized.\n", 167 | "\n", 168 | "It is in this section that QCGPU, the software developed in this project is used. Because of the integration with IBM's software, it can be used seamlessly instead of there simulation backend, providing a speedup.\n", 169 | "\n", 170 | "This speedup means that algorithms such as this can be tested quicker, allowing for faster prototyping and thus better algorithms overall." 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 5, 176 | "metadata": {}, 177 | "outputs": [ 178 | { 179 | "name": "stdout", 180 | "output_type": "stream", 181 | "text": [ 182 | "CPU times: user 1min 10s, sys: 4.27 s, total: 1min 15s\n", 183 | "Wall time: 1min 46s\n", 184 | "CPU times: user 1min 2s, sys: 5.27 s, total: 1min 7s\n", 185 | "Wall time: 1min 30s\n" 186 | ] 187 | } 188 | ], 189 | "source": [ 190 | "from qiskit_aqua.input import SVMInput\n", 191 | "from qiskit_qcgpu_provider import QCGPUProvider\n", 192 | "from qiskit_aqua import run_algorithm\n", 193 | "\n", 194 | "params = {\n", 195 | " 'problem': {'name': 'svm_classification', 'random_seed': 10598},\n", 196 | " 'algorithm': { 'name': 'QSVM.Kernel' },\n", 197 | " 'backend': {'name': 'qasm_simulator', 'shots': 1024},\n", 198 | " 'feature_map': {'name': 'SecondOrderExpansion', 'depth': 2, 'entanglement': 'linear'}\n", 199 | "}\n", 200 | "\n", 201 | "backend = QCGPUProvider().get_backend('qasm_simulator')\n", 202 | "\n", 203 | "algo_input = SVMInput(training_input, test_input, datapoints[0])\n", 204 | "%time result = run_algorithm(params, algo_input)\n", 205 | "%time result = run_algorithm(params, algo_input, backend=backend)" 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": {}, 211 | "source": [ 212 | "# Running The Algorithm\n", 213 | "\n", 214 | "With everything setup, the algorithm can now be run.\n", 215 | "The run method does everything, including the training testing \n", 216 | "and prediciton on unlabeled data. We can also find data such as the\n", 217 | "success ratio.\n", 218 | "\n", 219 | "The trained model is stored in the `svm` variable.\n", 220 | "This is them used to predict some datapoints, which is then analyzed in the next section" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 6, 226 | "metadata": {}, 227 | "outputs": [ 228 | { 229 | "name": "stdout", 230 | "output_type": "stream", 231 | "text": [ 232 | "ground truth: [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1]\n", 233 | "prediction: [0 1 0 0 0 1 0 0 1 0 1 1 0 1 1 0 1 1 1 1]\n", 234 | "predicted class: ['Benign', 'Malignant', 'Benign', 'Benign', 'Benign', 'Malignant', 'Benign', 'Benign', 'Malignant', 'Benign', 'Malignant', 'Malignant', 'Benign', 'Malignant', 'Malignant', 'Benign', 'Malignant', 'Malignant', 'Malignant', 'Malignant']\n", 235 | "accuracy: 0.75\n" 236 | ] 237 | } 238 | ], 239 | "source": [ 240 | "print(\"ground truth: {}\".format(datapoints[1]))\n", 241 | "print(\"prediction: {}\".format(result['predicted_labels']))\n", 242 | "print(\"predicted class: {}\".format(result['predicted_classes']))\n", 243 | "print(\"accuracy: {}\".format(result['testing_accuracy']))" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": null, 249 | "metadata": {}, 250 | "outputs": [], 251 | "source": [] 252 | } 253 | ], 254 | "metadata": { 255 | "kernelspec": { 256 | "display_name": "Python 3", 257 | "language": "python", 258 | "name": "python3" 259 | }, 260 | "language_info": { 261 | "codemirror_mode": { 262 | "name": "ipython", 263 | "version": 3 264 | }, 265 | "file_extension": ".py", 266 | "mimetype": "text/x-python", 267 | "name": "python", 268 | "nbconvert_exporter": "python", 269 | "pygments_lexer": "ipython3", 270 | "version": "3.6.6" 271 | } 272 | }, 273 | "nbformat": 4, 274 | "nbformat_minor": 2 275 | } 276 | -------------------------------------------------------------------------------- /examples/aqua/Traveling Salesman.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "The travelling salesman problem is a famous problem in computer science. It was stated by William Rowan Hamilton, who is most famous for his contributions to optics, mechanics and algebra, notably for the invention of quaternions.\n", 8 | "\n" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 3, 14 | "metadata": {}, 15 | "outputs": [], 16 | "source": [ 17 | "import matplotlib.pyplot as ply\n", 18 | "%matplotlib inline\n", 19 | "\n", 20 | "import networkx as nx\n", 21 | "import numpy as np\n", 22 | "\n", 23 | "from qiskit_aqua.translators.ising import tsp\n", 24 | "from qiskit_aqua.input import EnergyInput\n", 25 | "from qiskit_aqua import run_algorithm\n", 26 | "from qiskit_qcgpu_provider import QCGPUProvider" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 4, 32 | "metadata": {}, 33 | "outputs": [ 34 | { 35 | "data": { 36 | "image/png": "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\n", 37 | "text/plain": [ 38 | "
" 39 | ] 40 | }, 41 | "metadata": {}, 42 | "output_type": "display_data" 43 | } 44 | ], 45 | "source": [ 46 | "locations = 3\n", 47 | "\n", 48 | "problem = tsp.random_tsp(locations)\n", 49 | "positions = {k: v for k, v in enumerate(problem.coord)}\n", 50 | "\n", 51 | "G = nx.Graph()\n", 52 | "G.add_nodes_from(np.arange(0, locations, 1))\n", 53 | "nx.draw(G, with_labels=True, pos=positions)" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 5, 59 | "metadata": {}, 60 | "outputs": [ 61 | { 62 | "data": { 63 | "image/png": "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ECgqQvOkJBy+fx8toqLw1Vdfwc3NDU2aNEFaWhpDlIiIzEL+jTQkBJg9GygoeHhTDoDxAE4AaArgEwDv6D2k2MEBSxs3xmsHD+Lll1+26LhERGRf5H2ONCXliRAFgH8CcARwC8CPAIYB6AbgpT/vr1tejo/z86GoV8+S0xIRkR2S96HdTz7RHrbVkw/gIIAlABoA+AeAtwBEPvZQRVGR9vFERERmJN8gzc7WLix67MjzJWhrdCe927oB+Pnxx0sSEBMD3L5t1jGJiMi+yTdIIyIM3vwAQMPHbmsEIM/QNysUlT4PERGRKcg3SNXqJ1fnQns49/5jt90H8Iyh5ygsBDQa089GRET0J/kG6b17Bm/uBKAMwG96t6XhPwuNnpCba9KxiIiI9Mk3SBs1MnhzfQA+AD6AduFRPIDDAP63sudp3NgMwxEREWnJN0jd3QEnJ4N3bQJQCOA5AP4AQlBJI3V2BtzczDUhERGRjDdkyM4G2rUzeJ60ypycgKwsoFkz081FRESkR76N9LnngCFDtCtva0KhAIYOZYgSEZFZybeRAtqdjTw8ntjZqErq1QNiY4FevUw+FhERkY58GykA9O4NBAdrQ7E66tXTPo4hSkREZibvvXYBYPJk7X9nz9ZeF/q0Aq1QaBcYBQf/53FERERmJO9Du/pSU7V758bEaANTfw/ePz+PFEOHAvPns4kSEZHFWE+Q6ty+rd32T6PRbrbQuLH2EhelkguLiIjI4qwvSImIiGRE3ouNiIiIZI5BSkREZAQGKRERkREYpEREREZgkBIRERmBQUpERGQEBikREZERGKRERERGYJASEREZgUFKRERkBAYpERGRERikRERERmCQEhERGYFBSkREZAQGKRERkREYpEREREZgkBIRERmBQUpERGQEBikREZERGKRERERGYJASEREZgUFKRERkBAYpERGRERikRERERmCQEhERGYFBSkREZIT/B4Isd9BJe325AAAAAElFTkSuQmCC\n", 64 | "text/plain": [ 65 | "
" 66 | ] 67 | }, 68 | "metadata": {}, 69 | "output_type": "display_data" 70 | } 71 | ], 72 | "source": [ 73 | "best_distance, best_order = brute_force(problem.w, problem.dim)\n", 74 | "draw(G, best_order, positions)" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "# Solving Using The Variational Quantum Eigensolver" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 6, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "operator, offset = tsp.get_tsp_qubitops(problem)\n", 91 | "algorithm_input = EnergyInput(operator)\n", 92 | "\n", 93 | "algorithm_parameters = {\n", 94 | " 'problem': { 'name': 'ising', 'random_seed': 23 },\n", 95 | " 'algorithm': { 'name': 'VQE', 'operator_mode': 'matrix' },\n", 96 | " 'optimizer': { 'name': 'SPSA', 'max_trials':100 },\n", 97 | " 'variational_form': {'name': 'RY', 'depth': 5, 'entanglement': 'linear'}\n", 98 | "}" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 7, 104 | "metadata": {}, 105 | "outputs": [ 106 | { 107 | "name": "stdout", 108 | "output_type": "stream", 109 | "text": [ 110 | "CPU times: user 32.1 s, sys: 127 ms, total: 32.3 s\n", 111 | "Wall time: 36.8 s\n", 112 | "CPU times: user 22.9 s, sys: 241 ms, total: 23.1 s\n", 113 | "Wall time: 23.1 s\n" 114 | ] 115 | } 116 | ], 117 | "source": [ 118 | "backend = QCGPUProvider().get_backend('statevector_simulator')\n", 119 | "%time result_qiskit = run_algorithm(algorithm_parameters, algorithm_input)\n", 120 | "%time result = run_algorithm(algorithm_parameters, algorithm_input, backend=backend)\n" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "Look at that! It completed it 13 seconds quicker, just by using the GPU!" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 8, 133 | "metadata": {}, 134 | "outputs": [ 135 | { 136 | "name": "stdout", 137 | "output_type": "stream", 138 | "text": [ 139 | "feasible: True\n", 140 | "solution: [2, 1, 0]\n", 141 | "solution objective: 203.0\n" 142 | ] 143 | }, 144 | { 145 | "data": { 146 | "image/png": "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ECgqQvOkJBy+fx8toqLw1Vdfwc3NDU2aNEFaWhpDlIiIzEL+jTQkBJg9GygoeHhTDoDxAE4AaArgEwDv6D2k2MEBSxs3xmsHD+Lll1+26LhERGRf5H2ONCXliRAFgH8CcARwC8CPAIYB6AbgpT/vr1tejo/z86GoV8+S0xIRkR2S96HdTz7RHrbVkw/gIIAlABoA+AeAtwBEPvZQRVGR9vFERERmJN8gzc7WLix67MjzJWhrdCe927oB+Pnxx0sSEBMD3L5t1jGJiMi+yTdIIyIM3vwAQMPHbmsEIM/QNysUlT4PERGRKcg3SNXqJ1fnQns49/5jt90H8Iyh5ygsBDQa089GRET0J/kG6b17Bm/uBKAMwG96t6XhPwuNnpCba9KxiIiI9Mk3SBs1MnhzfQA+AD6AduFRPIDDAP63sudp3NgMwxEREWnJN0jd3QEnJ4N3bQJQCOA5AP4AQlBJI3V2BtzczDUhERGRjDdkyM4G2rUzeJ60ypycgKwsoFkz081FRESkR76N9LnngCFDtCtva0KhAIYOZYgSEZFZybeRAtqdjTw8ntjZqErq1QNiY4FevUw+FhERkY58GykA9O4NBAdrQ7E66tXTPo4hSkREZibvvXYBYPJk7X9nz9ZeF/q0Aq1QaBcYBQf/53FERERmJO9Du/pSU7V758bEaANTfw/ePz+PFEOHAvPns4kSEZHFWE+Q6ty+rd32T6PRbrbQuLH2EhelkguLiIjI4qwvSImIiGRE3ouNiIiIZI5BSkREZAQGKRERkREYpEREREZgkBIRERmBQUpERGQEBikREZERGKRERERGYJASEREZgUFKRERkBAYpERGRERikRERERmCQEhERGYFBSkREZAQGKRERkREYpEREREZgkBIRERmBQUpERGQEBikREZERGKRERERGYJASEREZgUFKRERkBAYpERGRERikRERERmCQEhERGYFBSkREZIT/B4Isd9BJe325AAAAAElFTkSuQmCC\n", 147 | "text/plain": [ 148 | "
" 149 | ] 150 | }, 151 | "metadata": {}, 152 | "output_type": "display_data" 153 | } 154 | ], 155 | "source": [ 156 | "#print('tsp objective:', result['energy'] + offset)\n", 157 | "x = tsp.sample_most_likely(result['eigvecs'][0])\n", 158 | "print('feasible:', tsp.tsp_feasible(x))\n", 159 | "z = tsp.get_tsp_solution(x)\n", 160 | "print('solution:', z)\n", 161 | "print('solution objective:', tsp.tsp_value(z, problem.w))\n", 162 | "draw(G, z, positions)" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 1, 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [ 171 | "# Utitlity Functions\n", 172 | "def draw(G, order, positions):\n", 173 | " G2 = G.copy()\n", 174 | " n = len(order)\n", 175 | " for i in range(n):\n", 176 | " j = (i + 1) % n\n", 177 | " G2.add_edge(order[i], order[j])\n", 178 | " nx.draw(G2, pos=positions, with_labels=True)\n", 179 | " \n", 180 | "# Classically solve the problem using a brute-force method\n", 181 | "from itertools import permutations\n", 182 | "\n", 183 | "def brute_force(weights, N):\n", 184 | " a = list(permutations(range(1, N)))\n", 185 | " best_distance = None\n", 186 | " for i in a:\n", 187 | " distance = 0\n", 188 | " pre_j = 0\n", 189 | " for j in i:\n", 190 | " distance += weights[j, pre_j]\n", 191 | " pre_j = j\n", 192 | " distance += weights[pre_j, 0]\n", 193 | " order = (0,) + i\n", 194 | " if best_distance is None or distance < best_distance:\n", 195 | " best_order = order\n", 196 | " best_distance = distance\n", 197 | " \n", 198 | " return best_distance, best_order" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 2, 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "import warnings\n", 208 | "warnings.filterwarnings('ignore')" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [] 217 | } 218 | ], 219 | "metadata": { 220 | "kernelspec": { 221 | "display_name": "Python 3", 222 | "language": "python", 223 | "name": "python3" 224 | }, 225 | "language_info": { 226 | "codemirror_mode": { 227 | "name": "ipython", 228 | "version": 3 229 | }, 230 | "file_extension": ".py", 231 | "mimetype": "text/x-python", 232 | "name": "python", 233 | "nbconvert_exporter": "python", 234 | "pygments_lexer": "ipython3", 235 | "version": "3.6.6" 236 | } 237 | }, 238 | "nbformat": 4, 239 | "nbformat_minor": 2 240 | } 241 | -------------------------------------------------------------------------------- /examples/aqua/Max-Cut.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import matplotlib.pyplot as ply\n", 10 | "%matplotlib inline\n", 11 | "\n", 12 | "import networkx as nx\n", 13 | "import numpy as np\n", 14 | "\n", 15 | "from qiskit_aqua.translators.ising import maxcut\n", 16 | "from qiskit_aqua.input import EnergyInput\n", 17 | "from qiskit_aqua import run_algorithm\n", 18 | "from qiskit_qcgpu_provider import QCGPUProvider" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 3, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "nodes = 5\n", 28 | "edges = [\n", 29 | " # Tuple (i, j, weight), \n", 30 | " # where (i, j) is an edge.\n", 31 | " (0, 1, 1.0),\n", 32 | " (0, 2, 1.0),\n", 33 | " (1, 2, 1.0),\n", 34 | " (1, 4, 1.0),\n", 35 | " (3, 4, 1.0),\n", 36 | " (2, 3, 1.0)\n", 37 | "]" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 4, 43 | "metadata": {}, 44 | "outputs": [ 45 | { 46 | "data": { 47 | "image/png": "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LF++nIiICIoXL05oaCgtW7Z0+SLKyMjgueeeo1+/frz99tum40gOkZSUxPPPP8+OHTuoUqWK6ThWodIVuQtpaWm8++67TJw4kaeeeorQ0FCaNWvm8mX7V4cOHeLFF1/k0KFDlClTxnQccXHp6ek0bNiQnj170q9fP9NxrEalK3IbV69eZd68eUyZMgUvLy9CQkJo2LCh6VjGhISEkJSUxMcff2w6iri40NBQDh48yOrVq13qh1uVrshNXLx48c8dfxo3bkxISIjT3ePVFlJTU6lVqxYRERG8/vrrpuOIi9q5cycdO3YkLi7OoW8icz9cc9WHyH06f/48o0ePpkKFCnz11Vds3bqVjz/+WIX7uwIFCjB//nwGDBjAb7/9ZjqOuKCLFy/StWtX5s2b53KFCxrpigDZO/5MmTKFRYsW0b59e4YPH67LY26jX79+pKenM3/+fNNRxMV0794dd3d35syZYzqKTWikKznayZMnGTBgANWqVePatWvExcUxb948Fe4dTJgwgQ0bNrB161bTUcSFrFixgj179jBlyhTTUWxGpSs50tdff42vry+1a9emUKFCHDlyhGnTpjncjj+OqkiRIsyaNYtevXqRnJxsOo64gB9//JEBAwawbNkyChYsaDqOzah0JUdJSEigc+fONGzYkLJly/L1118zYcIElzx3ZGuvvvoq9erVY8yYMaajiJPLysqie/fuDB48mLp165qOY1M6pys5QmxsLGFhYezevZvAwED8/f0pXLiw6VhO78yZM9SsWZM1a9a4/DdLsZ0pU6bwySefsG3bNqfcI/deqHTFpe3YsYOwsDASExMZNmwYvr6+Tr0BgSNasmQJkZGRxMbGkjdvXtNxxMkcOnSIFi1asG/fPp566inTcWxO08viciwWC1988QVNmjShR48edOjQgePHjzNw4EAVrg106dKFMmXKMHnyZNNRxMmkpKTg4+PDlClTckThgka64kKysrJYvXo148eP58qVK4SEhPDGG2+41I4/jur777+nTp06fPnll1SuXNl0HHESQ4YM4ZdffuHDDz90qbtO3Y5KV5xeZmYmH3/8MWFhYeTJk4fQ0FDatm3rsjv+OKrp06fzn//8h23btunPXu5o48aN+Pn5ERcXR4kSJUzHsRuVrjit9PR0li1bRkREBKVKlSIkJARvb+8c8xOzo8nMzOT555+nW7du+Pv7m44jDuzcuXN4eXmxZMkSmjdvbjqOXal0xemkpqb+ueNPxYoVCQ0NpUmTJipbB5CYmEjTpk05ePAgjz/+uOk44oAsFgvt27enfPnyREZGmo5jd5oDEqdx9epVoqKiqFChAmvXruXDDz9k06ZNNG3aVIXrIKpXr86AAQPw9/dHP8/Lzbz77rscP36csLAw01GMUOmKw7t48SJhYWE89dRT7NmzhzVr1vD555/z3HPPmY4mNzFy5Ei+/fZbPvroI9NRxMEcP36c4cOHs2zZMvLnz286jhEqXXFY586dIzQ0lAoVKnDs2DG2bdvGihUrqFWrlulochv58uVjwYIFBAQEcP78edNxxEFkZGTQpUsXQkNDqVGjhuk4xqh0xeH88ssvBAUFUalSJc6dO8e+fft47733qFq1qulocpeee+453njjDQICAkxHEQcRFhZGkSJFGDhwoOkoRql0xWF899139OvXj+rVq5OVlUV8fDxz5syhfPnypqPJfRg/fjw7duxg/fr1pqOIYXv27GHWrFm8++67Of5yspz9uxeHcOzYMXr27EmdOnUoVqwYR48eJTo6mscee8x0NHkAhQoVYu7cufTt25crV66YjiOGXL58mS5dujB79mweffRR03GM0yVDYszhw4cJDw9ny5YtDBgwgIEDB1K8eHHTscTKevToQdGiRZk2bZrpKGKAr68vAAsXLjScxDGodMXuYmJiCAsLY+/evQQGBtK3b1/t+OPCfv31V2rUqMHKlStp0KCB6ThiR//9738ZNmwYBw8e1Nf47zS9LHazfft2Xn75Zdq3b0+LFi04ceIEQ4cO1RejiytRogRTp07Fz8+PtLQ003HETn7++Wf69evH0qVL9TX+Fxrpik1ZLBY2btxIWFgYP//8MyNHjqRr167ky5fPdDSxI4vFQtu2baldu7Y2vc8BsrKy8Pb2pkGDBvr/fR2VrthEVlYWn332GWFhYaSkpDBq1Cg6deqkHX9ysJ9++olatWrxv//9j+rVq5uOIzY0ffp0li9fzs6dO/U1fx2VrlhVZmYmK1asIDw8nPz58xMSEkKbNm1y/GUCkm3u3Lm8++67fPnll+TOndt0HLGBhIQEmjVrxu7du6lYsaLpOA5HpStWce3aNZYuXcqECRN46KGHCA0N5eWXX9Y9keVvsrKyaNasGe3atWPw4MGm44iV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3Qtnz9oooIiIfal0xXYWL77lU5eA0UDUnY7h5nbb44iIOBOVrtjO4cN/vyzoL/4J+AKP3+kYKSkQH2/lYCIiZuicrtjOxYs3fTgO2AQcvNvjXLhgpUAiImapdMV2iha96cP/A74Dyv7+6ytAJvAVcOBmbyhe3OrRRERM0PSy2I6nJxQocMPDvYHjZI9444C+QCtgw82O4e4OdtwaUETElnSdrtjOmTNQrtwtz+v+YQzwDTdepwtkl/bJk3d1T2YREUenka7YzkMPZd9L2c3tti8bwy0K180NWrZU4YqIy9BIV2zrDnekui0PD9i2DerWtXosERETNNIV26pXL3vzAg+Pe3ufh0f2+1S4IuJCtHpZbO+P3YKCg7Ovu72PXYZERFyBppfFfmJjs+/FvHZtdrn+9Z7Mf+yn27Jl9n66GuGKiAtS6Yr9nT2bfWvH+PjsG18UL559WVCPHlo0JSIuTaUrIiJiJ1pIJSIiYicqXRERETtR6YqIiNiJSldERMROVLoiIiJ2otIVERGxE5WuiIiInah0RURE7ESlKyIiYicqXRERETtR6YqIiNiJSldERMROVLoiIiJ2otIVERGxE5WuiIiInah0RURE7ESlKyIiYicqXRERETtR6YqIiNiJSldERMROVLoiIiJ2otIVERGxE5WuiIiInah0RURE7ESlKyIiYicqXRERETtR6YqIiNiJSldERMROVLoiIiJ2otIVERGxE5WuiIiInah0RURE7OT/AMGODbparfN0AAAAAElFTkSuQmCC\n", 48 | "text/plain": [ 49 | "
" 50 | ] 51 | }, 52 | "metadata": {}, 53 | "output_type": "display_data" 54 | } 55 | ], 56 | "source": [ 57 | "G = nx.Graph()\n", 58 | "G.add_nodes_from(np.arange(0, nodes, 1))\n", 59 | "G.add_weighted_edges_from(edges)\n", 60 | "nx.draw(G, with_labels=True)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 5, 66 | "metadata": {}, 67 | "outputs": [ 68 | { 69 | "data": { 70 | "text/plain": [ 71 | "array([[0., 1., 1., 0., 0.],\n", 72 | " [1., 0., 1., 0., 1.],\n", 73 | " [1., 1., 0., 1., 0.],\n", 74 | " [0., 0., 1., 0., 1.],\n", 75 | " [0., 1., 0., 1., 0.]])" 76 | ] 77 | }, 78 | "execution_count": 5, 79 | "metadata": {}, 80 | "output_type": "execute_result" 81 | } 82 | ], 83 | "source": [ 84 | "# Computing the weight matrix\n", 85 | "weights = np.zeros([nodes, nodes])\n", 86 | "for i in range(nodes):\n", 87 | " for j in range(nodes):\n", 88 | " edge_data = G.get_edge_data(i, j, default = None)\n", 89 | " # check if there is no edge\n", 90 | " if edge_data != None:\n", 91 | " weights[i, j] = edge_data['weight']\n", 92 | "weights" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 6, 98 | "metadata": {}, 99 | "outputs": [ 100 | { 101 | "name": "stdout", 102 | "output_type": "stream", 103 | "text": [ 104 | "Optimal Solution: case = [0, 1, 0, 1, 0], cost = 5.0\n" 105 | ] 106 | } 107 | ], 108 | "source": [ 109 | "# Calculate the max cut\n", 110 | "best = 0\n", 111 | "for b in range(2**nodes):\n", 112 | " x = [int(t) for t in reversed(list(bin(b)[2:].zfill(nodes)))]\n", 113 | " cost = 0\n", 114 | " for i in range(nodes):\n", 115 | " for j in range(nodes):\n", 116 | " cost = cost + weights[i,j]*x[i]*(1-x[j])\n", 117 | " if best < cost:\n", 118 | " best = cost\n", 119 | " xbest_brute = x \n", 120 | " # print('case = ' + str(x)+ ' cost = ' + str(cost))\n", 121 | " \n", 122 | "print('Optimal Solution: case = ' + str(xbest_brute) + ', cost = ' + str(best))" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 7, 128 | "metadata": {}, 129 | "outputs": [ 130 | { 131 | "data": { 132 | "image/png": "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YqLw8HBq1qxJRkaG1aUACl3ba9myJW+//TYbNmxg7969NGvWjEmTJpGenm51aeJndutFiZjKTu0aha4hWrRoweuvv86XX37JgQMHaN68OePGjbPNf0jie3Y6UIiYzE4TWIWuYRITE3n55Zf5+uuvOXToEC1atOCBBx7g4MGDVpcmPqblZRHfsNNYUuga6pxzzmH27Nls3ryZ7OxskpKSGD16NL/++qvVpYmP2Gl2LmIyO40lha7hzjrrLJ577jm2bNlCUVERycnJ3Hvvvfz8889WlyZVZKcDhYjJ7NSqUegGicaNG/P000+zdetWQkNc3AnlAAAPPUlEQVRDad26NX/729/48ccfrS5NzpCdDhQiJrPTBFahG2QaNmzIP//5T3bs2EGNGjW46KKLuPPOO9m3b5/VpUkl2akPJWIyha74Xb169Xj00UfZuXMn8fHxtGvXjttuu429e/daXZpUkJ0OFCIms9MEVqEb5BISEpg2bRq7d++mUaNGtG/fnsGDB7N7926rS5PTUOiK+IadxpJCt5qIi4tjypQp7Nmzh3PPPZfOnTtzyy23sGPHDqtLk3LY6UAhYjI7XR+h0K1m6tSpw8SJE0lLS6Nly5Zccskl3HjjjWzdutXq0qSUuLg4MjMzKS4utroUEaOVTGDdbrfVpSh0q6vY2FjGjh1LWloaF110ET179uT666/nu+++s7o0+UNYWBi1a9fWlp8iVRQTE4PD4bDFI1MVutVcrVq1eOCBB9i7dy8dO3akd+/eXHPNNWzatMnq0gQtMYv4il3GkkJXAKhRowajR48mLS2N7t27c+WVV3LVVVeRmppqdWnVml0OFCKms0tfV6ErJ4mJieGee+4hLS3t+FnvX/7yF7744gurS6uW7HKgEDGdXSawCl3xKioqihEjRrBnzx769u1L//79+fOf/8y///1vq0urVux0f6GIyRS6YoTIyEjuuOMOdu3axQ033MDAgQPp2bMn69ats7q0asEuBwoR09llAqvQlQqJiIjgtttuY+fOnQwcOJDbbruN7t278+mnn9riMvxgpdAV8Q27jCWFrlRKeHg4gwYNYvv27QwbNowRI0bQtWtXli9frvD1A/V0RXzDLmNJoStnJCwsjJtvvpmtW7dy9913M2rUKDp27MjixYsVvj5klyUxEdPpTFeCQmhoKP3792fLli38/e9/56GH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\n", 133 | "text/plain": [ 134 | "
" 135 | ] 136 | }, 137 | "metadata": {}, 138 | "output_type": "display_data" 139 | } 140 | ], 141 | "source": [ 142 | "# Plot the optimal solution\n", 143 | "colors = ['r' if xbest_brute[i] == 0 else 'b' for i in range(nodes)]\n", 144 | "nx.draw(G, node_color=colors, with_labels=True)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "## Solving Using The Variational Quantum Eigensolver" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 8, 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [ 160 | "operator, offset = maxcut.get_maxcut_qubitops(weights)\n", 161 | "algorithm_input = EnergyInput(operator)\n", 162 | "\n", 163 | "algorithm_parameters = {\n", 164 | " 'problem': { 'name': 'ising', 'random_seed': 3242 },\n", 165 | " 'algorithm': { 'name': 'VQE', 'operator_mode': 'matrix' },\n", 166 | " 'optimizer': { 'name': 'SPSA', 'max_trials': 300 },\n", 167 | " 'variational_form': {'name': 'RY', 'depth': 5, 'entanglement': 'linear'}\n", 168 | "}" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 12, 174 | "metadata": {}, 175 | "outputs": [ 176 | { 177 | "name": "stdout", 178 | "output_type": "stream", 179 | "text": [ 180 | "CPU times: user 29.4 s, sys: 219 ms, total: 29.6 s\n", 181 | "Wall time: 29.6 s\n", 182 | "CPU times: user 29.2 s, sys: 239 ms, total: 29.5 s\n", 183 | "Wall time: 29.5 s\n" 184 | ] 185 | } 186 | ], 187 | "source": [ 188 | "backend = QCGPUProvider().get_backend('statevector_simulator')\n", 189 | "%time result_qiskit = run_algorithm(algorithm_parameters, algorithm_input, backend=backend)\n", 190 | "%time result = run_algorithm(algorithm_parameters, algorithm_input, backend=backend)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 15, 196 | "metadata": {}, 197 | "outputs": [ 198 | { 199 | "name": "stdout", 200 | "output_type": "stream", 201 | "text": [ 202 | "energy: -1.99894259727831\n", 203 | "maxcut objective: -4.99894259727831\n", 204 | "solution: [0. 1. 0. 1. 0.]\n", 205 | "solution objective: 5.0\n" 206 | ] 207 | } 208 | ], 209 | "source": [ 210 | "x = maxcut.sample_most_likely(result['eigvecs'][0])\n", 211 | "print('energy:', result['energy'])\n", 212 | "print('maxcut objective:', result['energy'] + offset)\n", 213 | "print('solution:', maxcut.get_graph_solution(x))\n", 214 | "print('solution objective:', maxcut.maxcut_value(x, weights))" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 16, 220 | "metadata": {}, 221 | "outputs": [ 222 | { 223 | "data": { 224 | "image/png": "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rx/79u1j5cqVlC5d2upIIuJtzpyBrKzLPvQrMB9IvJbnucL09M1S6cpNO3r0KN26daNSpUokJCR4/YbmImKRYsVy3OBiEbAbqHzhv88AmcBm4F8Tybm4J3Tub78hHm3btm0EBgYSHBzM5MmTVbgiYp2aNXPcCOMp4A+yR7y/AgOAEGDe5S6uVi2XAqp05Sb88ssvtGzZkldffZXhw4fj64It1EREclSzJtSqddmHCgO3XfSnKFAQKHPphUWLwosv5lpErV6WGzJp0iSGDBnC5MmTuf/++62OIyKSbdo07E88gU9y8o39/VtugSNHwN/fubku0He6cl3sdjvvvPMO48aNY+HChdSuXdvqSCIiDnsbNKBwaiqluIGp3MKF4YUXcq1wQdPLch3S0tLo168fs2fPZuXKlSpcEclTZsyYQeP77iPqmWfwud7jAQsVgrZt4bXXcifcBRrpyjU5efIkYWFhlChRgkWLFlGkSBGrI4mIANm7Ug0dOpRZs2YxY8YMAgMD4dFHoXXr7NuIcrhv16FIEQgNhR9/zPXj/TTSlavauXMngYGBBAQEEBkZqcIVkTxj+/btBAYGsn//fhITE7MLF6BOHdiyBV56CUqWzL6d6GL58mWfLNSkCUycCFOmgAsOZNFCKrmilStX0r17d9544w0GDRpkdRwREYdJkyYxePBg3n77bZ5++umcD1VJT4dZs+CXX7IXSRUsCHfcAb17w913uzSzSldyFBERwcCBAxk/fjwhISFWxxERAeDMmTM888wzrFy5kp9//pn69etbHemaaXpZ/sVut/PRRx8xZMgQ4uLiVLgikmds2LCBRo0aAbB27Vq3KlzQQiq5RHp6uuMT5IoVK7j99qtuDS4ikuvsdjtfffUVb731FiNHjqRv375WR7ohKl1xOH36ND169MDX15elS5dS7NKFByIiFjh58iRPPPEEO3fuZNmyZW59gpmmlwWAffv2ERQURNWqVYmOjlbhikiesGLFCgICAqhYsSIrVqxw68IFla4A69atIzAwkEceeYQvv/ySfPk0ASIi1srKyuKDDz6ga9eufPbZZ4waNcojDlTRb1cvFx0dzWOPPcbXX39NeHi41XFERDh8+DAPP/wwZ8+eZe3atVSuXPnqf8lNaKTrxb744gv69+/P7NmzVbgikifMnz+fgIAAGjVqxKJFizyqcEEjXa+UmZnJkCFDiI+PZ9myZdx5551WRxIRL5eRkcFbb73F+PHj+fHHHz329DKVrpc5e/YsDz74IMnJySxbtoySJUtaHUlEvNzevXvp3bs3RYsWZf369ZQrV87qSLlG08te5ODBg7Rs2ZJSpUoxd+5cFa6IWG7GjBk0btyYLl26MGfOHI8uXNBI12ts3LiR0NBQnnzySV5//fWc9ygVEXGB1NRUhg4dSnR09P9OBvICKl0vEBcXR58+ffjss8948MEHrY4jIl5u+/bt9OzZk6pVq5KYmOhVs26aXvZw3333HQ8//DCRkZEqXBGx3MSJE7nvvvt46qmnME3TqwoXNNL1WFlZWbz22mtERkayePFit9/FRUTc28UnA82fP5969epZHckSGul6oJSUFHr16sXSpUs9Yts0EXFvf58M5OPjw7p167y2cEGl63GOHj1K27Zt8fPzY/78+ZQuXdrqSCLipex2O2PGjOH+++/njTfeYNy4cRQpUsTqWJbS9LIH2bp1KyEhIfTq1Yt3330XX199phIRa5w8eZLHH3+c3bt3u/3JQM6k38oe4pdffqFVq1a8/vrrDB8+XIUrIpZZvnw5AQEBVKpUSV9xXUIjXQ8wceJEXnjhBSZPnuyxW6eJSN6XlZXFRx99xKeffsq3335L165drY6U56h03Zjdbuftt99m/PjxLFy4kNq1a1sdSUS81OHDh+nbty/nzp1jzZo1HndQgbNoDtJNnT9/nkceeYTY2FhWrlypwhURy8THxxMQEEDTpk098mQgZ9JI1w2dOHGCsLAwSpYsyaJFiyhcuLDVkUTEC6Wnp/PWW28xYcIEJk6cSNu2ba2OlOdppOtm/vjjD5o3b07Dhg0xTVOFKyKW2LNnD8HBwaxfv57169ercK+RSteNrFixgqCgIJ599lk++eQT/Pz8rI4kIl5o+vTpNGnShK5duxIbG+vxJwM5k6aX3URERAQDBw5k/PjxhISEWB1HRLxQamoqL774IjExMcycOZNmzZpZHcntqHTzOLvdzkcffcTo0aOJj4+nfv36VkcSES+0bds2evXqxV133UViYiK33HKL1ZHckqaX87D09HT69+/PlClTWLFihQpXRCzx448/EhQURP/+/YmIiFDh3gSNdPOov/76iwceeAA/Pz+WLFlCsWLFrI4kIl7mzJkzDBo0iNWrV5OQkEDdunWtjuT2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\n", 225 | "text/plain": [ 226 | "
" 227 | ] 228 | }, 229 | "metadata": {}, 230 | "output_type": "display_data" 231 | } 232 | ], 233 | "source": [ 234 | "colors = ['r' if maxcut.get_graph_solution(x)[i] == 0 else 'b' for i in range(nodes)]\n", 235 | "nx.draw(G, node_color=colors, with_labels=True)" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 1, 241 | "metadata": {}, 242 | "outputs": [], 243 | "source": [ 244 | "import warnings\n", 245 | "warnings.filterwarnings('ignore')" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [] 254 | } 255 | ], 256 | "metadata": { 257 | "kernelspec": { 258 | "display_name": "Python 3", 259 | "language": "python", 260 | "name": "python3" 261 | }, 262 | "language_info": { 263 | "codemirror_mode": { 264 | "name": "ipython", 265 | "version": 3 266 | }, 267 | "file_extension": ".py", 268 | "mimetype": "text/x-python", 269 | "name": "python", 270 | "nbconvert_exporter": "python", 271 | "pygments_lexer": "ipython3", 272 | "version": "3.6.6" 273 | } 274 | }, 275 | "nbformat": 4, 276 | "nbformat_minor": 2 277 | } 278 | --------------------------------------------------------------------------------