├── Qcover ├── research │ ├── __init__.py │ ├── GHZ_Generate.py │ └── QAOA_Generate.py ├── compiler │ ├── .DS_Store │ └── __init__.py ├── version.py ├── simulator │ ├── __init__.py │ └── qton.py ├── applications │ ├── __init__.py │ ├── sherrington_kirkpatrick.py │ ├── max_cut.py │ ├── number_partition.py │ ├── graph_color.py │ ├── common.py │ ├── minimum_vertex_cover.py │ ├── max_2_sat.py │ ├── quadratic_assignment.py │ ├── set_packing.py │ └── set_partitioning.py ├── optimizers │ ├── optimizer.py │ ├── __init__.py │ ├── Simulated_Annealing.py │ ├── COBYLA.py │ ├── Gradient_Descent.py │ ├── SLSQP.py │ ├── L_BFGS_B.py │ ├── SHGO.py │ ├── SPSA.py │ ├── Interp.py │ └── Fourier.py ├── examples │ ├── test.cpp │ ├── g002201.txt │ ├── makemqlib.py │ ├── g002216.txt │ ├── g000036.txt │ └── g002212.txt ├── __init__.py ├── backends │ ├── __init__.py │ ├── backend.py │ └── circuitbytensor.py ├── exceptions.py └── utils.py ├── tests ├── cobyla.png ├── fourier.png ├── RQAOA vs QAOA.png ├── test_compiler_res.png ├── test_compiler_graph.png ├── .vscode │ └── launch.json ├── test_readme.py ├── quafu_test.py ├── test_library.py ├── qton_test.py ├── test_qcover_send.py ├── test_qaoa.py ├── RQAOA_test.py ├── test_compiler_send.py ├── draw_figure.py ├── pros_tests.py ├── spinmodel.py ├── fourier_test.py ├── problems_tests.py ├── GradientDescent_qiskit.py └── max_2_sat_test.py ├── paper data ├── paper_fig.pdf ├── data │ ├── qiskit_p1_nd3.h5 │ ├── sk_wtensor_p1.h5 │ ├── sk_decomp_tensor_p1.h5 │ ├── stetevector_p1_nd3.h5 │ ├── Qcover_wtensor_p1_nd3.h5 │ ├── Qcover_decomp_tensor_p1_nd3.h5 │ ├── graphcolor_wtensor_p1_nd3_cln3.h5 │ └── graphcolor_decomp_tensor_p1_nd3_cln3.h5 ├── data_pap │ ├── qiskit_p1_nd3.h5 │ ├── sk_wtensor_p1.h5 │ ├── stetevector_p1_nd3.h5 │ ├── sk_decomp_tensor_p1.h5 │ ├── Qcover_wtensor_p1_nd3.h5 │ ├── Qcover_wtensor_p2_nd3.h5 │ ├── Qcover_wtensor_p3_nd3.h5 │ ├── Qcover_wtensor_p4_nd3.h5 │ ├── Qcover_decomp_tensor_p1_nd3.h5 │ ├── Qcover_decomp_tensor_p2_nd3.h5 │ ├── Qcover_decomp_tensor_p3_nd3.h5 │ ├── Qcover_decomp_tensor_p4_nd3.h5 │ ├── graphcolor_wtensor_p1_nd3_cln3.h5 │ └── graphcolor_decomp_tensor_p1_nd3_cln3.h5 ├── 3-regular_graph │ ├── state_vector.py │ ├── qiskit_qaoa_test.py │ ├── decomp_tensor.py │ └── wholetensor.py ├── sk_problem │ ├── decomp_tensor.py │ └── wholetensor.py └── graph_coloring │ ├── decomp_tensor.py │ └── wholetensor.py ├── resources ├── Qcover_label.png ├── Qcover_label1.jpg ├── Qcover_label2.jpg ├── Qcover_label3.jpg ├── Qcover_label4.gif ├── Qcover_label5.png ├── Qcover_label7.gif └── Qcover_label_readme.png ├── requirements.txt ├── .vscode └── launch.json ├── setup.py └── .gitignore /Qcover/research/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cobyla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/tests/cobyla.png -------------------------------------------------------------------------------- /tests/fourier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/tests/fourier.png -------------------------------------------------------------------------------- /tests/RQAOA vs QAOA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/tests/RQAOA vs QAOA.png -------------------------------------------------------------------------------- /Qcover/compiler/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/Qcover/compiler/.DS_Store -------------------------------------------------------------------------------- /paper data/paper_fig.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/paper_fig.pdf -------------------------------------------------------------------------------- /resources/Qcover_label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/resources/Qcover_label.png -------------------------------------------------------------------------------- /resources/Qcover_label1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/resources/Qcover_label1.jpg -------------------------------------------------------------------------------- /resources/Qcover_label2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/resources/Qcover_label2.jpg -------------------------------------------------------------------------------- /resources/Qcover_label3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/resources/Qcover_label3.jpg -------------------------------------------------------------------------------- /resources/Qcover_label4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/resources/Qcover_label4.gif -------------------------------------------------------------------------------- /resources/Qcover_label5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/resources/Qcover_label5.png -------------------------------------------------------------------------------- /resources/Qcover_label7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/resources/Qcover_label7.gif -------------------------------------------------------------------------------- /tests/test_compiler_res.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/tests/test_compiler_res.png -------------------------------------------------------------------------------- /tests/test_compiler_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/tests/test_compiler_graph.png -------------------------------------------------------------------------------- /Qcover/version.py: -------------------------------------------------------------------------------- 1 | """Define version number here and read it from setup.py automatically""" 2 | __version__ = "2.6.0" 3 | -------------------------------------------------------------------------------- /paper data/data/qiskit_p1_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data/qiskit_p1_nd3.h5 -------------------------------------------------------------------------------- /paper data/data/sk_wtensor_p1.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data/sk_wtensor_p1.h5 -------------------------------------------------------------------------------- /resources/Qcover_label_readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/resources/Qcover_label_readme.png -------------------------------------------------------------------------------- /paper data/data_pap/qiskit_p1_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/qiskit_p1_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/sk_wtensor_p1.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/sk_wtensor_p1.h5 -------------------------------------------------------------------------------- /paper data/data/sk_decomp_tensor_p1.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data/sk_decomp_tensor_p1.h5 -------------------------------------------------------------------------------- /paper data/data/stetevector_p1_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data/stetevector_p1_nd3.h5 -------------------------------------------------------------------------------- /paper data/data/Qcover_wtensor_p1_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data/Qcover_wtensor_p1_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/stetevector_p1_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/stetevector_p1_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/sk_decomp_tensor_p1.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/sk_decomp_tensor_p1.h5 -------------------------------------------------------------------------------- /Qcover/compiler/__init__.py: -------------------------------------------------------------------------------- 1 | from Qcover.compiler.compilerforQAOA import CompilerForQAOA 2 | from Qcover.compiler.hardware_library import BuildLibrary -------------------------------------------------------------------------------- /paper data/data/Qcover_decomp_tensor_p1_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data/Qcover_decomp_tensor_p1_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/Qcover_wtensor_p1_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/Qcover_wtensor_p1_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/Qcover_wtensor_p2_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/Qcover_wtensor_p2_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/Qcover_wtensor_p3_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/Qcover_wtensor_p3_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/Qcover_wtensor_p4_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/Qcover_wtensor_p4_nd3.h5 -------------------------------------------------------------------------------- /paper data/data/graphcolor_wtensor_p1_nd3_cln3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data/graphcolor_wtensor_p1_nd3_cln3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/Qcover_decomp_tensor_p1_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/Qcover_decomp_tensor_p1_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/Qcover_decomp_tensor_p2_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/Qcover_decomp_tensor_p2_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/Qcover_decomp_tensor_p3_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/Qcover_decomp_tensor_p3_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/Qcover_decomp_tensor_p4_nd3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/Qcover_decomp_tensor_p4_nd3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/graphcolor_wtensor_p1_nd3_cln3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/graphcolor_wtensor_p1_nd3_cln3.h5 -------------------------------------------------------------------------------- /paper data/data/graphcolor_decomp_tensor_p1_nd3_cln3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data/graphcolor_decomp_tensor_p1_nd3_cln3.h5 -------------------------------------------------------------------------------- /paper data/data_pap/graphcolor_decomp_tensor_p1_nd3_cln3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BAQIS-Quantum/Qcover/HEAD/paper data/data_pap/graphcolor_decomp_tensor_p1_nd3_cln3.h5 -------------------------------------------------------------------------------- /Qcover/simulator/__init__.py: -------------------------------------------------------------------------------- 1 | from .qton import Qcircuit, Qcodes 2 | import warnings 3 | warnings.filterwarnings("ignore") 4 | 5 | __all__ = [ 6 | # 'Simulator', 7 | 'Qcircuit', 8 | 'Qcodes' 9 | ] -------------------------------------------------------------------------------- /Qcover/applications/__init__.py: -------------------------------------------------------------------------------- 1 | from .common import * 2 | from .graph_color import GraphColoring 3 | from .max_cut import MaxCut 4 | from .number_partition import NumberPartition 5 | from .max_2_sat import Max2Sat 6 | -------------------------------------------------------------------------------- /Qcover/optimizers/optimizer.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | import logging 3 | import numpy as np 4 | 5 | from scipy import optimize as opt 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | class Optimizer: 10 | def __init__(self, *args): 11 | pass 12 | 13 | def optimize(self, objective_function): 14 | pass -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cirq==1.1.0 2 | cirq-aqt==1.1.0 3 | cirq-core==1.1.0 4 | cirq-google==1.1.0 5 | cirq-ionq==1.1.0 6 | cirq-pasqal==1.1.0 7 | cirq-rigetti==1.1.0 8 | cirq-web==1.1.0 9 | networkx==2.8.8 10 | projectq==0.8.0 11 | pyquafu==0.3.6 12 | qiskit==0.42.1 13 | qiskit-aer==0.12.0 14 | qiskit-ibmq-provider==0.20.2 15 | qiskit-terra==0.23.3 16 | quimb==1.4.2 17 | qulacs==0.6.0 18 | opt_einsum==3.3.0 19 | autoray==0.6.3 20 | 21 | -------------------------------------------------------------------------------- /tests/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: 当前文件", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${file}", 12 | "console": "integratedTerminal", 13 | "justMyCode": true 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /Qcover/examples/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | int main(){ 8 | string t = "test.txt"; 9 | ifstream graph; 10 | graph.open(t.c_str()); 11 | string g; 12 | while(graph >> g) 13 | { 14 | string s = "python downloadGraph.py " + g + " > " + g; //download zip, every zip include a txt 15 | // string s = "unzip " + g //unzip zips to txt to get graph instance 16 | // string s = "rm -r " + g // delete zips 17 | system(s.c_str()); 18 | } 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /Qcover/optimizers/__init__.py: -------------------------------------------------------------------------------- 1 | from .optimizer import Optimizer 2 | from .COBYLA import COBYLA 3 | from .SLSQP import SLSQP 4 | from .L_BFGS_B import L_BFGS_B 5 | from .Gradient_Descent import GradientDescent 6 | from .Interp import Interp 7 | from .Fourier import Fourier 8 | from .SPSA import SPSA 9 | from .SHGO import SHGO 10 | 11 | __all__ = ['Optimizer', 12 | 'COBYLA', 13 | 'SLSQP', 14 | 'L_BFGS_B', 15 | 'GradientDescent', 16 | 'Interp', 17 | 'Fourier', 18 | 'SPSA', 19 | 'SHGO' 20 | ] -------------------------------------------------------------------------------- /Qcover/__init__.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=invalid-name,wrong-import-position 2 | 3 | """Main QCover public functionality.""" 4 | 5 | import pkgutil 6 | 7 | # Allow extending this namespace. Please note that currently this line needs 8 | # to be placed *before* the wrapper imports or any non-import code AND *before* 9 | # importing the package you want to allow extensions for (in this case `backends`). 10 | __path__ = pkgutil.extend_path(__path__, __name__) 11 | __version__ = '2.5.0' 12 | __license__ = 'Apache-2.0 License' 13 | 14 | from Qcover.applications import * 15 | from Qcover.backends import * 16 | from Qcover.optimizers import * 17 | 18 | 19 | -------------------------------------------------------------------------------- /Qcover/backends/__init__.py: -------------------------------------------------------------------------------- 1 | from .backend import Backend 2 | from .circuitbyqiskit import CircuitByQiskit 3 | from .circuitbyprojectq import CircuitByProjectq 4 | from .circuitbycirq import CircuitByCirq 5 | from .circuitbyqulacs import CircuitByQulacs 6 | # from .circuitbytket import CircuitByTket 7 | from .circuitbytensor import CircuitByTensor 8 | from .circuitbyqton import CircuitByQton 9 | from .circuitbyquafu import CircuitByQuafu 10 | import warnings 11 | warnings.filterwarnings("ignore") 12 | 13 | __all__ = [ 14 | 'Backend', 15 | 'CircuitByCirq', 16 | 'CircuitByQiskit', 17 | 'CircuitByProjectq', 18 | 'CircuitByTensor', 19 | 'CircuitByQulacs', 20 | 'CircuitByQton', 21 | 'CircuitByQuafu' 22 | ] 23 | -------------------------------------------------------------------------------- /tests/test_readme.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | from Qcover.core import Qcover 3 | from Qcover.backends import CircuitByQulacs 4 | from Qcover.optimizers import COBYLA 5 | 6 | ising_g = nx.Graph() 7 | nodes = [(0, 3), (1, 2), (2, 1), (3, 1)] 8 | edges = [(0, 1, 1), (0, 2, 1), (3, 1, 2), (2, 3, 3)] 9 | for nd in nodes: 10 | u, w = nd[0], nd[1] 11 | ising_g.add_node(int(u), weight=int(w)) 12 | for ed in edges: 13 | u, v, w = ed[0], ed[1], ed[2] 14 | ising_g.add_edge(int(u), int(v), weight=int(w)) 15 | 16 | p = 2 17 | optc = COBYLA(options={'tol': 1e-3, 'disp': True}) 18 | ts_bc = CircuitByQulacs() 19 | qc = Qcover(ising_g, p=p, optimizer=optc, backend=ts_bc) 20 | res = qc.run() 21 | print("the result of problem is:\n", res) 22 | qc.backend.optimization_visualization() -------------------------------------------------------------------------------- /Qcover/backends/backend.py: -------------------------------------------------------------------------------- 1 | """Backend interface""" 2 | 3 | from enum import IntEnum 4 | import logging 5 | from abc import ABC, abstractmethod 6 | import numpy as np 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | # pylint: disable=invalid-name 11 | 12 | class Backend(ABC): 13 | """Base class for backend.""" 14 | 15 | # @abstractmethod 16 | # def __init__(self, *args): 17 | # pass 18 | 19 | @abstractmethod 20 | def __init__(self, 21 | p: int = 1, 22 | nodes_weight: list = None, 23 | edges_weight: list = None, 24 | is_parallel: bool = None) -> None: 25 | self._p = p 26 | self._nodes_weight = nodes_weight 27 | self._edges_weight = edges_weight 28 | self._is_parallel = False if is_parallel is None else is_parallel 29 | 30 | self._element_to_graph = None 31 | self._pargs = None 32 | self._element_expectation = dict() 33 | 34 | @abstractmethod 35 | def get_expectation(self, *args): 36 | pass 37 | 38 | @abstractmethod 39 | def expectation_calculation(self): 40 | pass 41 | 42 | @abstractmethod 43 | def optimization_visualization(self): 44 | pass 45 | -------------------------------------------------------------------------------- /Qcover/examples/g002201.txt: -------------------------------------------------------------------------------- 1 | # NAME: es30fst14. 2 | # COMMENT: 33d32945 STP File, STP Format Version 1.00, Section Comment, Name "es30fst14", Creator "Martin Zachariasen, David Warme", Remark "Reduced rectilinear graph from FST generator GeoSteiner", End 3 | 53 58 4 | 1 31 1452341 5 | 30 32 1016178 6 | 24 34 1024932 7 | 24 33 1043837 8 | 3 32 1151263 9 | 3 31 1622974 10 | 20 31 239346 11 | 27 35 1553491 12 | 27 32 435052 13 | 12 33 1598280 14 | 33 34 18905 15 | 34 36 1327831 16 | 28 35 803655 17 | 36 37 235903 18 | 36 38 94192 19 | 37 39 94192 20 | 7 37 260670 21 | 26 38 18905 22 | 26 41 294414 23 | 26 33 1422023 24 | 38 39 235903 25 | 38 40 167073 26 | 40 42 127341 27 | 6 40 235903 28 | 6 39 167073 29 | 2 43 651909 30 | 2 35 1793606 31 | 22 41 894466 32 | 41 42 18905 33 | 41 44 712418 34 | 42 44 693513 35 | 23 48 1383482 36 | 43 49 1672669 37 | 25 43 87792 38 | 18 45 922034 39 | 18 44 57754 40 | 10 47 93619 41 | 19 45 1004382 42 | 45 46 135299 43 | 46 47 656505 44 | 4 48 1280514 45 | 4 47 493106 46 | 15 49 1868441 47 | 15 48 1335838 48 | 49 51 602097 49 | 8 50 95126 50 | 8 46 553976 51 | 50 52 1793718 52 | 14 50 76666 53 | 16 17 234230 54 | 17 51 2245718 55 | 29 53 2108835 56 | 29 51 384915 57 | 13 52 1507708 58 | 21 53 647629 59 | 9 52 1607732 60 | 11 53 1927420 61 | 5 9 683186 62 | -------------------------------------------------------------------------------- /tests/quafu_test.py: -------------------------------------------------------------------------------- 1 | # from quafu import User 2 | # user = User() 3 | # user.save_apitoken('csEFB6U-Kn5f-x5Dh3kk2DX6eEf4JqAaI20TmP-YHAO.0nM2IDOxQDN2YTM6ICc4VmIsIyMyEjI6ICZpJye.9JiN1IzUIJiOicGbhJCLiQ1VKJiOiAXe0Jye') 4 | 5 | import numpy as np 6 | from quafu import QuantumCircuit 7 | import matplotlib.pyplot as plt 8 | 9 | q = QuantumCircuit(5) 10 | q.x(0) 11 | q.x(1) 12 | q.cnot(2, 1) 13 | q.ry(1, np.pi/2) 14 | q.rx(2, np.pi) 15 | q.rz(3, 0.1) 16 | q.cz(2, 3) 17 | measures = [0, 1, 2, 3] 18 | cbits = [0, 1, 2, 3] 19 | q.measure(measures, cbits=cbits) 20 | q.draw_circuit(width=4) 21 | 22 | qc = QuantumCircuit(4) 23 | test_ghz = """OPENQASM 2.0; 24 | include "qelib1.inc"; 25 | qreg q[4]; 26 | h q[0]; 27 | cx q[0],q[1]; 28 | cx q[0],q[2]; 29 | cx q[0],q[3]; 30 | """ 31 | qc.from_openqasm(test_ghz) 32 | qc.draw_circuit() 33 | 34 | from quafu import Task 35 | task = Task() 36 | task.load_account() 37 | task.config(backend="ScQ-P10", shots=2000, compile=True) 38 | res = task.send(q) 39 | 40 | print(res.counts) #counts 41 | print(res.amplitudes) #amplitude 42 | res.plot_amplitudes() 43 | 44 | from quafu import simulate 45 | simu_res = simulate(q, output="amplitudes") 46 | simu_res.plot_amplitudes(full=True) 47 | 48 | res = task.send(qc) 49 | res.plot_amplitudes() 50 | 51 | simu_res = simulate(qc) 52 | simu_res.plot_amplitudes(full=True) 53 | plt.show() -------------------------------------------------------------------------------- /paper data/3-regular_graph/state_vector.py: -------------------------------------------------------------------------------- 1 | import os 2 | from tests.core_without_RQAOA import * 3 | from Qcover.backends import CircuitByQiskit 4 | from Qcover.applications.max_cut import MaxCut 5 | 6 | from time import time 7 | import numpy as np 8 | import h5py 9 | 10 | import networkx as nx 11 | 12 | nd = 3 13 | p = 1 14 | # num_nodes_list = np.array([10,14,26, 50,100,300,500, 800, 1000,10000]) 15 | num_nodes_list = np.array([10,14]) 16 | time_statevector = np.zeros(len(num_nodes_list), dtype=float) 17 | cy_ind = 0 18 | max_step = 1 19 | 20 | for num_nodes in num_nodes_list: 21 | mxt = MaxCut(node_num = num_nodes, node_degree=nd) 22 | mc_mat = nx.adj_matrix(mxt.graph).A 23 | g = mxt.run() 24 | 25 | quimb_bc = CircuitByQiskit(expectation_calc_method="statevector") 26 | optc = COBYLA(maxiter=1, tol=1e-6, disp=True) 27 | qc = Qcover(g, p=p, optimizer=optc, backend=quimb_bc) 28 | st = time() 29 | res = qc.run() 30 | time_statevector[cy_ind] = time() - st 31 | cy_ind += 1 32 | 33 | dirs = '../data' 34 | if not os.path.exists(dirs): 35 | os.makedirs(dirs) 36 | 37 | if len(num_nodes_list) == 1: 38 | filename = '../data/statevector_p%i_nd%i_nodesnum%i.h5'%(p, nd, num_nodes_list[0]) 39 | else: 40 | filename = '../data/stetevector_p%i_nd%i.h5'%(p, nd) 41 | data = h5py.File(filename, 'w') 42 | 43 | data['time_statevector'] = time_statevector 44 | data['num_nodes_list'] = num_nodes_list 45 | data['maxiter'] = max_step 46 | data['p'] = p 47 | data['nd'] = nd 48 | data.close() 49 | 50 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // { 2 | // // Use IntelliSense to learn about possible attributes. 3 | // // Hover to view descriptions of existing attributes. 4 | // // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | // "version": "0.2.0", 6 | // "configurations": [ 7 | // { 8 | // "name": "Python: Current File", 9 | // "type": "python", 10 | // "request": "launch", 11 | // "program": "${file}", 12 | // "console": "integratedTerminal", 13 | // "justMyCode": true, 14 | // "python": "D:\\Anaconda\\envs\\qiskitv\\python", 15 | // "env": {"PYTHONPATH":"${workspaceRoot}"}, 16 | // "envFile": "${workspaceFolder}/.env" 17 | // } 18 | // ] 19 | // } 20 | 21 | { 22 | // Use IntelliSense to learn about possible attributes. 23 | // Hover to view descriptions of existing attributes. 24 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 25 | "version": "0.2.0", 26 | "configurations": [ 27 | { 28 | "name": "Python", 29 | "type": "python", 30 | "request": "launch", 31 | "stopOnEntry": false, 32 | "python": "${command:python.interpreterPath}", 33 | "program": "${file}", 34 | "cwd": "${workspaceRoot}", 35 | "env": {"PYTHONPATH":"${workspaceRoot}"}, 36 | "envFile": "${workspaceRoot}/.env", 37 | "console": "externalTerminal" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /paper data/sk_problem/decomp_tensor.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from tests.core_without_RQAOA import * 4 | 5 | from Qcover.backends import CircuitByTensor 6 | from Qcover.applications.sherrington_kirkpatrick import SherringtonKirkpatrick 7 | 8 | 9 | from time import time 10 | import numpy as np 11 | import h5py 12 | 13 | p = 1 14 | opt = 'greedy' 15 | # num_nodes_list = np.arange(4,64,4) 16 | num_nodes_list = np.array([10,12]) 17 | time_qcover_tensor = np.zeros(len(num_nodes_list), dtype=float) 18 | exp_qcover_tensor = np.zeros_like(time_qcover_tensor) 19 | parametr_f_qcovertensor = np.zeros([len(num_nodes_list),2, p], dtype=float) 20 | 21 | cy_ind = 0 22 | max_step = 1 23 | for num_nodes in num_nodes_list: 24 | 25 | sk = SherringtonKirkpatrick(num_nodes) 26 | g = sk.run() 27 | 28 | quimb_bc = CircuitByTensor(contract_opt='greedy') 29 | optc = COBYLA(maxiter=1, tol=1e-6, disp=True) 30 | qc = Qcover(g, p=p, optimizer=optc, backend=quimb_bc) 31 | st = time() 32 | res = qc.run() 33 | time_qcover_tensor[cy_ind] = time() - st 34 | 35 | cy_ind += 1 36 | dirs = '../data' 37 | 38 | if not os.path.exists(dirs): 39 | os.makedirs(dirs) 40 | 41 | if len(num_nodes_list) == 1: 42 | filename = '../data/sk_decomp_tensor_p%i_nodesnum%i.h5'%(p, num_nodes_list[0]) 43 | else: 44 | filename = '../data/sk_decomp_tensor_p%i.h5'%p 45 | data = h5py.File(filename, 'w') 46 | data['time_qcover_tensor'] = time_qcover_tensor 47 | data['num_nodes_list'] = num_nodes_list 48 | data['maxiter'] = max_step 49 | data['p'] = p 50 | data.close() 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /paper data/3-regular_graph/qiskit_qaoa_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import networkx as nx 4 | import numpy as np 5 | import h5py 6 | 7 | from qiskit.optimization.applications.ising import max_cut 8 | from qiskit.aqua import aqua_globals 9 | from qiskit.algorithms.optimizers import COBYLA 10 | from qiskit.aqua.algorithms import QAOA 11 | from qiskit import Aer 12 | 13 | from Qcover.applications import MaxCut 14 | 15 | 16 | 17 | 18 | nd = 3 19 | 20 | p = 1 21 | 22 | # num_nodes_list = np.array([4,6,8,10,12, 14]) 23 | num_nodes_list = np.array([4,6]) 24 | time_qiskit = np.zeros_like(num_nodes_list, dtype='float') 25 | 26 | # nx.draw_networkx(mxt.graph) 27 | # plt.show() 28 | 29 | cy_ind = 0 30 | for num_nodes in num_nodes_list: 31 | mxt = MaxCut(node_num=num_nodes, node_degree=nd) 32 | mc_mat = nx.adjacency_matrix(mxt.graph).A 33 | qubit_op, offset = max_cut.get_operator(mc_mat) # 34 | aqua_globals.random_seed = 10598 35 | qaoa = QAOA(qubit_op, optimizer=COBYLA(maxiter=30), p=1, 36 | quantum_instance=Aer.get_backend('statevector_simulator')) 37 | st = time.time() 38 | result = qaoa.compute_minimum_eigenvalue() 39 | ed = time.time() 40 | time_qiskit[cy_ind] = ed - st 41 | cy_ind += 1 42 | 43 | dirs = '../data' 44 | if not os.path.exists(dirs): 45 | os.makedirs(dirs) 46 | if len(num_nodes_list) == 1: 47 | filename = '../data/qiskit_p%i_nd%i_nodesnum%i.h5' % (p, nd, num_nodes_list[0]) 48 | else: 49 | filename = '../data/qiskit_p%i_nd%i.h5' % (p, nd) 50 | data = h5py.File(filename, 'w') 51 | 52 | data['time_qiskit'] = time_qiskit 53 | data['p'] = p 54 | data['num_nodes_list'] = num_nodes_list 55 | data['nd'] = nd 56 | -------------------------------------------------------------------------------- /paper data/3-regular_graph/decomp_tensor.py: -------------------------------------------------------------------------------- 1 | from tests.core_without_RQAOA import * 2 | import os 3 | 4 | from Qcover.backends import CircuitByTensor 5 | from Qcover.applications.max_cut import MaxCut 6 | 7 | 8 | from time import time 9 | import numpy as np 10 | import h5py 11 | 12 | import networkx as nx 13 | 14 | nd = 3 15 | p = 1 16 | #num_nodes_list = np.array([10, 14, 26, 50, 100,300,500,800,1000]) 17 | num_nodes_list = np.array([10,12]) 18 | time_qcover_tensor = np.zeros(len(num_nodes_list), dtype=float) 19 | exp_qcover_tensor = np.zeros_like(time_qcover_tensor) 20 | parametr_f_qcovertensor = np.zeros([len(num_nodes_list),2, p], dtype=float) 21 | 22 | cy_ind = 0 23 | max_step = 1 24 | for num_nodes in num_nodes_list: 25 | mxt = MaxCut(node_num = num_nodes, node_degree=nd) 26 | mc_mat = nx.adj_matrix(mxt.graph).A 27 | g = mxt.run() 28 | 29 | #for the p = 4, we choose contract_opt = 'greedy-rf' 30 | quimb_bc = CircuitByTensor(contract_opt='greedy') 31 | optc = COBYLA(maxiter=1, tol=1e-6, disp=True) 32 | qc = Qcover(g, p=p, optimizer=optc, backend=quimb_bc) 33 | st = time() 34 | res = qc.run() 35 | time_qcover_tensor[cy_ind] = time() - st 36 | 37 | cy_ind += 1 38 | dirs = '../data' 39 | 40 | if not os.path.exists(dirs): 41 | os.makedirs(dirs) 42 | 43 | if len(num_nodes_list) == 1: 44 | filename = '../data/Qcover_decomp_tensor_p%i_nd%i_nodesnum%i.h5'%(p, nd, num_nodes_list[0]) 45 | else: 46 | filename = '../data/Qcover_decomp_tensor_p%i_nd%i.h5'%(p, nd) 47 | data = h5py.File(filename, 'w') 48 | data['time_qcover_tensor'] = time_qcover_tensor 49 | data['maxiter'] = max_step 50 | data['nd'] = nd 51 | data['p'] = p 52 | data.close() 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Qcover/applications/sherrington_kirkpatrick.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import os 14 | import sys 15 | import time 16 | import logging 17 | import random 18 | import numpy as np 19 | import networkx as nx 20 | import matplotlib.pyplot as plt 21 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_regular_graph 22 | 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | 27 | class SherringtonKirkpatrick: 28 | """ 29 | Sherrington Kirkpatrick problem: The Sherrington-Kirkpatrick model is a special instance of a spin glass. 30 | it's defined on the complete graph with wij randomly chosen to be ±1 31 | """ 32 | def __init__(self, node_num: int = 5): 33 | self._node_num = node_num 34 | 35 | @property 36 | def node_num(self): 37 | return self._node_num 38 | 39 | def run(self): 40 | ising_mat = np.zeros((self._node_num, self._node_num)) #, dtype=float 41 | for i in range(self._node_num): 42 | for j in range(self._node_num): 43 | if i <= j: 44 | ising_mat[i][j] = np.random.choice([1, -1], 1) 45 | else: 46 | ising_mat[i][j] = ising_mat[j][i] 47 | sk_graph = get_weights_graph(ising_mat) 48 | return sk_graph 49 | -------------------------------------------------------------------------------- /Qcover/optimizers/Simulated_Annealing.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Optional 3 | import logging 4 | import numpy as np 5 | from scipy import optimize as opt 6 | from Qcover.optimizers import Optimizer 7 | from Qcover.exceptions import ArrayShapeError 8 | import warnings 9 | warnings.filterwarnings("ignore") 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | class SimulatedAnnealing(Optimizer): 15 | def __init__(self, 16 | options: dict = None, # {'maxiter':300, 'disp':True, 'rhobeg': 1.0, 'tol':1e-6}, 17 | initial_point: Optional[np.ndarray] = None) -> None: 18 | """ 19 | Args: 20 | options: some optional setting parameters such as: 21 | maxiter: Maximum number of function evaluations. 22 | disp: Set to True to print convergence messages. 23 | rhobeg: Reasonable initial changes to the variables. 24 | tol: Final accuracy in the optimization (not precisely guaranteed). 25 | This is a lower bound on the size of the trust region. 26 | """ 27 | super().__init__() 28 | self._p = None 29 | self._options = options 30 | self._initial_point = initial_point 31 | 32 | def _minimize(self, loss): 33 | pass 34 | 35 | def optimize(self, objective_function): 36 | if self._initial_point is None: 37 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 38 | else: 39 | try: 40 | if len(self._initial_point) != 2 * self._p: 41 | raise ArrayShapeError("The shape of initial parameters is not match with p") 42 | except ArrayShapeError as e: 43 | print(e) 44 | sys.exit() 45 | 46 | return self._minimize(objective_function) -------------------------------------------------------------------------------- /Qcover/examples/makemqlib.py: -------------------------------------------------------------------------------- 1 | import time 2 | import networkx as nx 3 | from Qcover.applications import MaxCut 4 | from Qcover.optimizers import Fourier, COBYLA 5 | from Qcover.backends import CircuitByQulacs, CircuitByTensor, CircuitByQiskit 6 | from Qcover.core import Qcover 7 | 8 | G = nx.Graph() 9 | node_num, edge_num = 0, 0 10 | i = 0 11 | # str = input("输入文件名:") 12 | str = "g002212.txt" # #"g002201.txt" 13 | with open(str) as f: 14 | for line in f: 15 | if line.startswith('#'): 16 | continue 17 | data = line.split() 18 | if i == 0: 19 | node_num, edge_num = data 20 | G.add_nodes_from(range(1, int(node_num) + 1)) #, weight=1 21 | else: 22 | u, v, w = data 23 | G.add_edge(int(u), int(v), weight=int(w)) 24 | i = i + 1 25 | 26 | p = 1 27 | mxt = MaxCut(G) 28 | ising_g = mxt.run()[0] 29 | if float(edge_num)/float(node_num) > 5.3: 30 | bc = CircuitByTensor() 31 | else: 32 | bc = CircuitByQulacs() 33 | # qiskit_bc = CircuitByQiskit(expectation_calc_method="statevector") 34 | 35 | optc = COBYLA(options={'tol': 1e-6, 'disp': True}) 36 | optf = Fourier(p=p, q=4, r=0, alpha=0.6, optimize_method="COBYLA", options={'tol': 1e-3, 'disp': False}) 37 | 38 | qc_c = Qcover(ising_g, p, 39 | optimizer=optc, 40 | backend=bc) # qiskit_bc 41 | st = time.time() 42 | res_c = qc_c.run(mode="RQAOA", node_threshold=1) # True 43 | ed = time.time() 44 | print("time cost by RQAOA is:", ed - st) 45 | print("solution is:", res_c) 46 | 47 | #107s 20个点直接搜 48 | #35s 0个点直接搜 49 | 50 | # qc_f = Qcover(ising_g, p, 51 | # optimizer=optf, 52 | # backend=bc) # ts_bc 53 | # 54 | # st = time.time() 55 | # res_f = qc_f.run(is_parallel=False) # True 56 | # ed = time.time() 57 | # print("time cost:", ed - st) 58 | # print("expectation is:", res_f['Expectation of Hamiltonian']) 59 | -------------------------------------------------------------------------------- /Qcover/examples/g002216.txt: -------------------------------------------------------------------------------- 1 | # NAME: bqp50_2.txt 2 | # COMMENT: Processed from file bqp50.txt from https://files.nyu.edu/jeb21/public/jeb/orlib/bqpinfo.html 3 | 50 120 4 | 1 6 83 5 | 1 19 -56 6 | 1 39 46 7 | 2 6 -82 8 | 2 14 -94 9 | 2 25 -32 10 | 2 32 15 11 | 2 42 30 12 | 2 50 14 13 | 3 9 -37 14 | 3 12 -72 15 | 3 15 -63 16 | 3 25 18 17 | 3 46 -82 18 | 3 49 28 19 | 4 5 85 20 | 4 6 97 21 | 4 9 10 22 | 4 25 -55 23 | 4 29 -8 24 | 4 31 -10 25 | 4 38 58 26 | 4 41 48 27 | 4 43 49 28 | 4 47 -67 29 | 5 7 -45 30 | 5 19 -75 31 | 5 25 36 32 | 5 30 -61 33 | 5 37 3 34 | 5 42 98 35 | 5 45 40 36 | 6 19 -33 37 | 6 28 -36 38 | 7 17 16 39 | 7 36 -2 40 | 8 19 42 41 | 9 11 -63 42 | 9 14 86 43 | 9 19 10 44 | 9 20 86 45 | 9 24 -23 46 | 9 36 69 47 | 9 44 -67 48 | 9 45 -86 49 | 9 50 90 50 | 10 11 4 51 | 11 28 90 52 | 11 36 -7 53 | 12 15 12 54 | 12 23 90 55 | 12 31 10 56 | 13 18 77 57 | 13 24 -12 58 | 13 25 47 59 | 13 42 89 60 | 13 44 -84 61 | 14 19 34 62 | 14 25 67 63 | 15 21 86 64 | 15 28 -12 65 | 15 33 -78 66 | 15 43 9 67 | 15 45 -73 68 | 15 46 -91 69 | 16 28 16 70 | 16 42 27 71 | 16 48 -54 72 | 17 45 -39 73 | 18 26 -31 74 | 18 30 16 75 | 18 39 -68 76 | 18 47 -39 77 | 18 49 -29 78 | 19 21 2 79 | 19 33 -81 80 | 19 36 -9 81 | 20 29 -13 82 | 20 36 -29 83 | 20 40 83 84 | 20 48 -18 85 | 21 39 84 86 | 21 41 -59 87 | 21 48 89 88 | 21 50 -93 89 | 23 49 67 90 | 24 36 -81 91 | 24 39 45 92 | 25 36 54 93 | 26 30 74 94 | 26 39 -96 95 | 26 46 -71 96 | 27 37 4 97 | 27 45 -76 98 | 27 46 41 99 | 30 42 -73 100 | 31 43 99 101 | 32 34 -40 102 | 32 43 93 103 | 33 40 79 104 | 33 50 67 105 | 34 38 82 106 | 34 39 -87 107 | 35 37 50 108 | 35 39 17 109 | 36 37 -72 110 | 36 38 78 111 | 36 41 -26 112 | 36 43 -62 113 | 36 48 15 114 | 37 46 65 115 | 37 50 -89 116 | 39 40 -81 117 | 39 43 -48 118 | 41 44 17 119 | 41 45 79 120 | 41 48 79 121 | 44 49 71 122 | 47 50 50 123 | 49 50 30 124 | -------------------------------------------------------------------------------- /tests/test_library.py: -------------------------------------------------------------------------------- 1 | import json 2 | import networkx as nx 3 | import matplotlib.pyplot as plt 4 | from Qcover.compiler.hardware_library import BuildLibrary 5 | from quafu import User 6 | import ast 7 | 8 | 9 | # build library 10 | user = User() 11 | user.save_apitoken("IB2Vz-3bqNNRHPCIw1RcdBMgPq8LNeGZe4KbBYDE_0A.9BzN5MjMwkjN2EjOiAHelJCLzITM6ICZpJye.9JiN1IzUIJiOicGbhJCLiQ1VKJiOiAXe0Jye") 12 | # user.save_apitoken("IK5TmYrX6v3gIt_AMW4JsQKHWH_-ZrFQSY793FZiZoH.QfyUDN3IzN1YjNxojIwhXZiwiI4YjI6ICZpJye.9JiN1IzUIJiOicGbhJCLiQ1VKJiOiAXe0Jye") 13 | backend = "ScQ-P10" 14 | BL = BuildLibrary(backend=backend, fidelity_threshold=96) 15 | BL.build_substructure_library() 16 | 17 | 18 | # test read library 19 | with open('LibSubstructure_'+backend+'.txt', 'r', encoding='utf-8') as f: 20 | substructure_data = eval(f.read()) 21 | 22 | 23 | sub_G = nx.MultiDiGraph() 24 | sub_G.add_edges_from(substructure_data['substructure_dict'][4][0]) 25 | pos = nx.spring_layout(sub_G) 26 | nx.draw(sub_G, pos=pos, with_labels=True, node_color='r', node_size=500, edge_color='b', width=1, font_size=16) 27 | plt.show() 28 | 29 | 30 | 31 | # # new code add linear coupling 32 | # qubits = 5 33 | # sub_clist = [qubits[0:2] for qubits in substructure_data['substructure_dict'][qubits][0]] 34 | # for sub in substructure_data['substructure_dict'][qubits]: 35 | # sublist = [qubits[0:2] for qubits in sub] 36 | # G = nx.Graph() 37 | # G.add_edges_from(sublist) 38 | # node_degree = dict(G.degree) 39 | # sort_degree = sorted(node_degree.items(), key=lambda kv: kv[1], reverse=True) 40 | # if sort_degree[0][1]==2: 41 | # print('subclist',sub) 42 | # sub_clist = sublist 43 | # break 44 | # G = nx.Graph() 45 | # G.add_edges_from(sub_clist) 46 | # pos = nx.spring_layout(G) 47 | # nx.draw(G, pos=pos, with_labels=True, node_color='r', node_size=150, edge_color='b', 48 | # width=1, font_size=9) 49 | # plt.show() 50 | # # # new code add linear coupling -------------------------------------------------------------------------------- /tests/qton_test.py: -------------------------------------------------------------------------------- 1 | import time 2 | from Qcover import COBYLA 3 | from Qcover.core import Qcover 4 | from Qcover.applications import MaxCut 5 | 6 | p = 1 7 | node_num = [10, 50, 100, 500, 1000] 8 | node_d = [3, 4, 5, 6] 9 | for i in node_num: 10 | pt, st = [], [] 11 | pv, sv = [], [] 12 | for nd in node_d: 13 | mxt = MaxCut(node_num=i, node_degree=nd) 14 | g, shift = mxt.run() 15 | from Qcover.backends import CircuitByQton 16 | qt = CircuitByQton(expectation_calc_method="tensor") # 17 | optc = COBYLA(options={'tol': 1e-3, 'disp': True}) 18 | qct = Qcover(g, p, 19 | optimizer=optc, 20 | backend=qt) # qiskit_bc, ,qulacs_bc 21 | 22 | t1 = time.time() 23 | sol = qct.run(is_parallel=False, mode='QAQA') # True 24 | t2 = time.time() 25 | st.append(t2 - t1) 26 | 27 | t1 = time.time() 28 | sol = qct.run(is_parallel=True, mode='QAQA') # 29 | t2 = time.time() 30 | pt.append(t2 - t1) 31 | 32 | qcv = Qcover(g, p, 33 | optimizer=COBYLA(options={'tol': 1e-3, 'disp': True}), 34 | backend=CircuitByQton()) # qiskit_bc, ,qulacs_bc 35 | t1 = time.time() 36 | sol = qcv.run(is_parallel=False, mode='QAQA') # True 37 | t2 = time.time() 38 | sv.append(t2 - t1) 39 | 40 | t1 = time.time() 41 | sol = qcv.run(is_parallel=True, mode='QAQA') # 42 | t2 = time.time() 43 | pv.append(t2 - t1) 44 | 45 | import matplotlib.pyplot as plt 46 | plt.figure() 47 | plt.plot(node_d, pt, "ob-", label="parallel tensor") 48 | plt.plot(node_d, st, "^r-", label="serial tensor") 49 | plt.plot(node_d, pv, "*g-", label="parallel statevector") 50 | plt.plot(node_d, sv, "dy-", label="serial statevector") 51 | plt.ylabel('Time cost') 52 | plt.xlabel('node degree') 53 | plt.title("node is %d" % i) 54 | plt.legend() 55 | plt.savefig('/public/home/humengjun/Qcover/result_log/qton_tensor/%d.png' % i) 56 | plt.close('all') -------------------------------------------------------------------------------- /tests/test_qcover_send.py: -------------------------------------------------------------------------------- 1 | 2 | # if __name__ == "__main__": 3 | from Qcover.core import Qcover 4 | from Qcover.backends import CircuitByCirq, CircuitByQulacs 5 | from Qcover.optimizers import COBYLA 6 | import networkx as nx 7 | import matplotlib.pyplot as plt 8 | 9 | p = 1 10 | g = nx.Graph() 11 | nodes = [(0, 0), (1, 0), (2, 0)] 12 | edges = [(0, 1, 1), (1, 2, 1)] 13 | 14 | # nodes = [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0)] 15 | # edges = [(0, 1, -1), (1, 2, -1), (2, 3, -1), (3, 4, -1), (4, 0, -1)] 16 | 17 | for nd in nodes: 18 | u, w = nd[0], nd[1] 19 | g.add_node(int(u), weight=int(w)) 20 | for ed in edges: 21 | u, v, w = ed[0], ed[1], ed[2] 22 | g.add_edge(int(u), int(v), weight=int(w)) 23 | 24 | # bc = CircuitByQiskit() 25 | # bc = CircuitByProjectq() 26 | # bc = CircuitByCirq() 27 | # bc = CircuitByQton() 28 | bc = CircuitByQulacs() 29 | # bc = CircuitByTensor() 30 | optc = COBYLA(options={'tol': 1e-3, 'disp': True}) 31 | qc = Qcover(g, p=p, optimizer=optc, backend=bc) 32 | res = qc.run() 33 | optimal_params = res['Optimal parameter value'] 34 | print('optimal_params:', optimal_params) 35 | 36 | # draw weighted graph 37 | new_labels = dict(map(lambda x: ((x[0], x[1]), str(x[2]['weight'])), g.edges(data=True))) 38 | pos = nx.spring_layout(g) 39 | nx.draw_networkx(g, pos=pos, node_size=500, font_size=15, node_color='y') 40 | # nx.draw_networkx_edge_labels(g, pos=pos, edge_labels=new_labels, font_size=15) 41 | nx.draw_networkx_edges(g, pos, width=2, edge_color='g', arrows=False) 42 | plt.show() 43 | 44 | 45 | # compiler and send to quafu cloud 46 | from Qcover.compiler import CompilerForQAOA 47 | BD_token = "QU9FimE2tOuY_AtN0QvwwjbNqeeMR-4jWWwXXQ9mRFh.9lTNwgzN4cjN2EjOiAHelJCLzITM6ICZpJye.9JiN1IzUIJiOicGbhJCLiQ1VKJiOiAXe0Jye" 48 | # token = "IK5TmYrX6v3gIt_AMW4JsQKHWH_-ZrFQSY793FZiZoH.QfyUDN3IzN1YjNxojIwhXZiwiI4YjI6ICZpJye.9JiN1IzUIJiOicGbhJCLiQ1VKJiOiAXe0Jye" 49 | cloud_backend = "ScQ-P10" 50 | shots = 1024 51 | qaoa_compiler = CompilerForQAOA(g, p=p, optimal_params=optimal_params, apitoken=BD_token, cloud_backend=cloud_backend) 52 | solver = qaoa_compiler.run(shots=shots) 53 | counts_energy = qaoa_compiler.results_processing(solver) 54 | qaoa_compiler.visualization(counts_energy) 55 | 56 | -------------------------------------------------------------------------------- /tests/test_qaoa.py: -------------------------------------------------------------------------------- 1 | 2 | from Qcover.core import Qcover 3 | from Qcover.backends import CircuitByCirq, CircuitByProjectq, CircuitByQulacs 4 | from Qcover.optimizers import COBYLA 5 | import networkx as nx 6 | import matplotlib.pyplot as plt 7 | 8 | p = 1 9 | g = nx.Graph() 10 | 11 | node_num, edge_num = 6, 7 12 | # nodes, edges = Qcover.generate_graph_data(node_num, edge_num) 13 | 14 | nodes = {(0, 5), (1, 1), (2, 8), (3, 3)} 15 | edges = {(0, 1, 1), (1, 2, 1), (3, 0, 6)} 16 | 17 | # nodes = {(0,1), (1,3), (2,2), (3,1), (4,0), (5,3)} 18 | # edges = {(0,1,-1), (1,2,-4), (2,3,2), (3,4,-2), (4,5,-1), (1,3,0), (2,4,3)} 19 | 20 | for nd in nodes: 21 | u, w = nd[0], nd[1] 22 | g.add_node(int(u), weight=int(w)) 23 | for ed in edges: 24 | u, v, w = ed[0], ed[1], ed[2] 25 | g.add_edge(int(u), int(v), weight=int(w)) 26 | 27 | 28 | # bc = CircuitByQiskit() 29 | # bc = CircuitByProjectq() 30 | # bc = CircuitByCirq() 31 | # bc = CircuitByQton() 32 | bc = CircuitByQulacs() 33 | # bc = CircuitByTensor() 34 | optc = COBYLA(options={'tol': 1e-3, 'disp': True}) 35 | qc = Qcover(g, p=p, optimizer=optc, backend=bc) 36 | res = qc.run() 37 | optimal_params = res['Optimal parameter value'] 38 | print('optimal_params:', optimal_params) 39 | 40 | # draw weighted graph 41 | new_labels = dict(map(lambda x: ((x[0], x[1]), str(x[2]['weight'])), g.edges(data=True))) 42 | pos = nx.spring_layout(g) 43 | # pos = nx.circular_layout(g) 44 | nx.draw_networkx(g, pos=pos, node_size=400, font_size=13, node_color='y') 45 | # nx.draw_networkx_edge_labels(g, pos=pos, edge_labels=new_labels, font_size=15) 46 | nx.draw_networkx_edges(g, pos, width=2, edge_color='g', arrows=False) 47 | plt.show() 48 | 49 | 50 | 51 | # compiler and send to quafu cloud 52 | from Qcover.compiler import CompilerForQAOA 53 | BD_token = "QU9FimE2tOuY_AtN0QvwwjbNqeeMR-4jWWwXXQ9mRFh.9lTNwgzN4cjN2EjOiAHelJCLzITM6ICZpJye.9JiN1IzUIJiOicGbhJCLiQ1VKJiOiAXe0Jye" 54 | # token = "EE4QgBwaMIKF5w2xDg_pclZeDGKTAJZZq4sN6aKKdqC.QfxYzM0MDO1YjNxojIwhXZiwiI4YjI6ICZpJye.9JiN1IzUIJiOicGbhJCLiQ1VKJiOiAXe0Jye" 55 | cloud_backend = "ScQ-P10" 56 | shots = 5000 57 | qaoa_compiler = CompilerForQAOA(g, p=p, optimal_params=optimal_params, apitoken=BD_token, cloud_backend=cloud_backend) 58 | quafu_solver = qaoa_compiler.run(shots=shots) 59 | counts_energy = qaoa_compiler.results_processing(quafu_solver) 60 | qaoa_compiler.visualization(counts_energy) 61 | -------------------------------------------------------------------------------- /Qcover/optimizers/COBYLA.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Optional 3 | import logging 4 | import numpy as np 5 | from scipy import optimize as opt 6 | from Qcover.optimizers import Optimizer 7 | from Qcover.exceptions import ArrayShapeError 8 | import warnings 9 | warnings.filterwarnings("ignore") 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | class COBYLA(Optimizer): 15 | """ 16 | COBYLA: a numerical optimization method for constrained problems 17 | 18 | based on scipy.optimize.minimize COBYLA. 19 | For further detail, please refer to 20 | https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html 21 | """ 22 | 23 | # pylint: disable=unused-argument 24 | def __init__(self, 25 | options: dict = None, #{'maxiter':300, 'disp':True, 'rhobeg': 1.0, 'tol':1e-6}, 26 | initial_point: Optional[np.ndarray] = None) -> None: 27 | """ 28 | Args: 29 | options: some optional setting parameters such as: 30 | maxiter: Maximum number of function evaluations. 31 | disp: Set to True to print convergence messages. 32 | rhobeg: Reasonable initial changes to the variables. 33 | tol: Final accuracy in the optimization (not precisely guaranteed). 34 | This is a lower bound on the size of the trust region. 35 | """ 36 | super().__init__() 37 | self._p = None 38 | self._options = options 39 | self._initial_point = initial_point 40 | 41 | def optimize(self, objective_function): 42 | if self._initial_point is None: 43 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 44 | else: 45 | try: 46 | if len(self._initial_point) != 2 * self._p: 47 | raise ArrayShapeError("The shape of initial parameters is not match with p") 48 | except ArrayShapeError as e: 49 | print(e) 50 | sys.exit() 51 | 52 | res = opt.minimize(objective_function, 53 | x0=np.array(self._initial_point), 54 | args=self._p, 55 | method='COBYLA', 56 | jac=opt.rosen_der, 57 | options=self._options) 58 | 59 | return res.x, res.fun, res.nfev -------------------------------------------------------------------------------- /tests/RQAOA_test.py: -------------------------------------------------------------------------------- 1 | import time 2 | from Qcover import COBYLA 3 | from Qcover.applications import MaxCut 4 | from Qcover.backends import CircuitByQulacs 5 | from Qcover.core import Qcover 6 | 7 | T = 20 8 | p = 1 9 | t_qaoa = [] 10 | t_rqaoa = [] 11 | exp_qaoa = [] 12 | exp_rqaoa = [] 13 | 14 | for iter in range(T): 15 | mxt = MaxCut(node_num=100, node_degree=3) 16 | ising_g, shift = mxt.run() 17 | qc = Qcover(ising_g, p, 18 | optimizer=COBYLA(options={'tol': 1e-6, 'disp': False}), 19 | backend=CircuitByQulacs()) 20 | 21 | st = time.time() 22 | sol = qc.run(mode='QAQA') 23 | ed = time.time() 24 | t_qaoa.append(ed - st) 25 | exp_qaoa.append(sol["Expectation of Hamiltonian"]) 26 | print("time cost by QAOA is:", ed - st) 27 | print("expectation value by QAOA is:", sol["Expectation of Hamiltonian"]) 28 | 29 | res_g = ising_g.copy() 30 | rqc = Qcover(ising_g, p, 31 | optimizer=COBYLA(options={'tol': 1e-6, 'disp': False}), 32 | backend=CircuitByQulacs()) 33 | 34 | st = time.time() 35 | sol = rqc.run(mode='RQAQA', node_threshold=1) 36 | ed = time.time() 37 | t_rqaoa.append(ed - st) 38 | 39 | exph = 0 40 | for (x, y) in res_g.nodes.data('weight', default=0): 41 | exph = exph + y * (sol[x] * 2 - 1) 42 | for (u, v, c) in res_g.edges.data('weight', default=0): 43 | exph = exph + c * (sol[u] * 2 - 1) * (sol[v] * 2 - 1) 44 | 45 | exp_rqaoa.append(exph) 46 | print("time cost by RQAOA is:", ed - st) 47 | print("expectation value by RQAOA is:", exph) 48 | 49 | import matplotlib.pyplot as plt 50 | plt.figure(1) 51 | plt.plot(range(T), t_qaoa, "ob-", label="QAOA") 52 | plt.plot(range(T), t_rqaoa, "^r-", label="RQAOA") 53 | plt.ylabel('Time cost') 54 | plt.xlabel('iteration id') 55 | plt.title("comparison of time taken by QAOA with RQAOA") 56 | plt.legend() 57 | plt.savefig('E:/Working_projects/QAOA/QCover/result_log/maxcut_time_large.png') # maxcut_serial 58 | plt.show() 59 | 60 | plt.figure(1) 61 | plt.plot(range(T), exp_qaoa, "ob-", label="QAOA") 62 | plt.plot(range(T), exp_rqaoa, "^r-", label="RQAOA") 63 | plt.ylabel('Expectation value') 64 | plt.xlabel('iteration id') 65 | plt.title("comparison of expectation value calculated by QAOA with RQAOA") 66 | plt.legend() 67 | plt.savefig('/public/home/humengjun/Qcover/result_log/tc.png') 68 | plt.show() -------------------------------------------------------------------------------- /Qcover/optimizers/Gradient_Descent.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | from typing import Iterator, Optional, Union, Callable, Dict, Any 4 | from Qcover.exceptions import ArrayShapeError 5 | 6 | class GradientDescent: 7 | 8 | def __init__(self, 9 | options: dict = None, # {'maxiter':300, 'learning_rate':0.0005, 'tol':1e-6, 'perturbation':0.1}, 10 | initial_point: Optional[np.ndarray] = None) -> None: 11 | 12 | super().__init__() 13 | 14 | self._p = None 15 | self._options = options 16 | self._initial_point = initial_point 17 | 18 | def _minimize(self, loss): 19 | if 'learning_rate' in self._options: 20 | lr = self._options["learning_rate"] 21 | else: 22 | lr = 0.01 23 | 24 | if 'tol' in self._options: 25 | tol = self._options['tol'] 26 | else: 27 | tol = 1e-6 28 | 29 | if 'maxiter' in self._options: 30 | maxiter = self._options['maxiter'] 31 | else: 32 | maxiter = 500 33 | 34 | eps = 0.01 if "perturbation" not in self._options else self._options['perturbation'] 35 | 36 | # prepare some initials 37 | x = np.asarray(self._initial_point) 38 | nfevs = 0 39 | 40 | for _ in range(1, maxiter + 1): 41 | delta = np.random.random() - 0.5 42 | # delta = np.random.randint(0, 1, size=len(self._initial_point)) - 0.5 43 | diff = loss(x + delta) - loss(x - delta) # 44 | grad = 0.5 * diff / delta # 45 | 46 | x_next = x - lr * grad 47 | stepsize = np.linalg.norm(grad) #the distance between grad to zero 48 | x = x_next 49 | nfevs += 1 50 | 51 | # check termination 52 | if stepsize < tol: 53 | break 54 | 55 | return x, loss(x), nfevs 56 | 57 | def optimize(self, objective_function): 58 | if self._initial_point is None: 59 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 60 | else: 61 | try: 62 | if len(self._initial_point) != 2 * self._p: 63 | raise ArrayShapeError("The shape of initial parameters is not match with p") 64 | except ArrayShapeError as e: 65 | print(e) 66 | sys.exit() 67 | 68 | return self._minimize(objective_function) 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Qcover/optimizers/SLSQP.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Optional 3 | import logging 4 | import numpy as np 5 | from scipy import optimize as opt 6 | from Qcover.optimizers import Optimizer 7 | from Qcover.exceptions import ArrayShapeError 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | class SLSQP(Optimizer): 13 | """ 14 | SLSQP: a numerical optimization method for constrained problems 15 | 16 | based on scipy.optimize.minimize SLSQP. 17 | For further detail, please refer to 18 | https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html 19 | """ 20 | 21 | # pylint: disable=unused-argument 22 | def __init__(self, 23 | options: dict = None, 24 | initial_point: Optional[np.ndarray] = None) -> None: 25 | """ 26 | Args: 27 | options: some optional setting parameters such as: 28 | maxiter: Maximum number of function evaluations. 29 | disp: Set to True to print convergence messages. 30 | rhobeg: Reasonable initial changes to the variables. 31 | tol: Final accuracy in the optimization (not precisely guaranteed). 32 | This is a lower bound on the size of the trust region. 33 | """ 34 | super().__init__() 35 | self._p = None 36 | self._options = options 37 | self._initial_point = initial_point 38 | 39 | def optimize(self, objective_function): 40 | if self._initial_point is None: 41 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 42 | else: 43 | try: 44 | if len(self._initial_point) != 2 * self._p: 45 | raise ArrayShapeError("The shape of initial parameters is not match with p") 46 | except ArrayShapeError as e: 47 | print(e) 48 | sys.exit() 49 | 50 | res = opt.minimize(objective_function, 51 | x0=np.array(self._initial_point), 52 | args=self._p, 53 | method='SLSQP', 54 | jac=None, 55 | options=self._options 56 | ) 57 | 58 | return res.x, res.fun, res.nfev 59 | 60 | # opts = SLSQP(options={'maxiter': 100, 61 | # 'ftol': 1e-06, 62 | # 'iprint': 1, 63 | # 'disp': False, 64 | # 'eps': 1.4901161193847656e-08, 65 | # 'finite_diff_rel_step': None}) -------------------------------------------------------------------------------- /Qcover/optimizers/L_BFGS_B.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Optional 3 | import logging 4 | import numpy as np 5 | from scipy import optimize as opt 6 | from Qcover.optimizers import Optimizer 7 | from Qcover.exceptions import ArrayShapeError 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | class L_BFGS_B(Optimizer): 13 | """ 14 | L-BFGS-B: a numerical optimization method for constrained problems 15 | 16 | based on scipy.optimize.minimize L-BFGS-B. 17 | For further detail, please refer to 18 | https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html 19 | """ 20 | 21 | # pylint: disable=unused-argument 22 | def __init__(self, 23 | options: dict = None, 24 | initial_point: Optional[np.ndarray] = None) -> None: 25 | """ 26 | Args: 27 | options: some optional setting parameters such as: 28 | maxiter: Maximum number of function evaluations. 29 | disp: Set to True to print convergence messages. 30 | rhobeg: Reasonable initial changes to the variables. 31 | tol: Final accuracy in the optimization (not precisely guaranteed). 32 | This is a lower bound on the size of the trust region. 33 | """ 34 | super().__init__() 35 | self._p = None 36 | self._options = options 37 | self._initial_point = initial_point 38 | 39 | def optimize(self, objective_function): 40 | if self._initial_point is None: 41 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 42 | else: 43 | try: 44 | if len(self._initial_point) != 2 * self._p: 45 | raise ArrayShapeError("The shape of initial parameters is not match with p") 46 | except ArrayShapeError as e: 47 | print(e) 48 | sys.exit() 49 | 50 | res = opt.minimize( 51 | objective_function, 52 | x0=np.array(self._initial_point), 53 | args=self._p, 54 | method='L-BFGS-B', 55 | jac=None, 56 | options=self._options 57 | ) 58 | return res.x, res.fun, res.nfev 59 | 60 | 61 | # optl = L_BFGS_B(options={'disp': None, 62 | # 'maxcor': 10, 63 | # 'ftol': 2.220446049250313e-09, 64 | # 'gtol': 1e-05, 65 | # 'eps': 1e-08, 66 | # 'maxfun': 15000, 67 | # 'maxiter': 15000, 68 | # 'iprint': - 1, 69 | # 'maxls': 20, 70 | # 'finite_diff_rel_step': None}) # 71 | -------------------------------------------------------------------------------- /Qcover/exceptions.py: -------------------------------------------------------------------------------- 1 | """Exceptions for errors raised by Qcover.""" 2 | 3 | from typing import Optional 4 | import warnings 5 | 6 | 7 | class QcoverError(Exception): 8 | """Base class for errors raised by Qcover.""" 9 | 10 | def __init__(self, *message): 11 | """Set the error message.""" 12 | super().__init__(" ".join(message)) 13 | self.message = " ".join(message) 14 | 15 | def __str__(self): 16 | """Return the message.""" 17 | return repr(self.message) 18 | 19 | 20 | class GraphTypeError(QcoverError): 21 | """Raised when a sequence subscript is out of range.""" 22 | 23 | def __init__(self, msg): #, *args 24 | """Set the error message.""" 25 | # warnings.warn( 26 | # "The problem is transformed into a dense graph, which is difficult to be solved effectively by Qcover", 27 | # stacklevel=2, 28 | # ) 29 | # self.message = "The problem is transformed into a dense graph, " \ 30 | # "which is difficult to be solved effectively by Qcover" 31 | self.name = "GraphTypeError: " 32 | self.message = msg 33 | super().__init__(msg) 34 | 35 | def __str__(self): 36 | return self.name + self.message 37 | # print("The problem is transformed into a dense graph, which is difficult to be solved effectively by Qcover") 38 | 39 | 40 | class ArrayShapeError(QcoverError): 41 | """Raised when a sequence subscript is out of range.""" 42 | 43 | def __init__(self, msg): #, *args 44 | """Set the error message.""" 45 | # warnings.warn( 46 | # "The problem is transformed into a dense graph, which is difficult to be solved effectively by Qcover", 47 | # stacklevel=2, 48 | # ) 49 | # self.message = "The problem is transformed into a dense graph, " \ 50 | # "which is difficult to be solved effectively by Qcover" 51 | self.name = "ArrayShapeError: " 52 | self.message = msg 53 | super().__init__(msg) 54 | 55 | def __str__(self): 56 | return self.name + self.message 57 | # print("The problem is transformed into a dense graph, which is difficult to be solved effectively by Qcover") 58 | 59 | 60 | class UserConfigError(QcoverError): 61 | """Raised when an error is encountered reading a user config file.""" 62 | def __init__(self, msg): 63 | self.name = "UserConfigError: " 64 | self.message = msg 65 | super().__init__(msg) 66 | 67 | def __str__(self): 68 | return self.name + self.message 69 | 70 | 71 | class OptimizerConfigError(QcoverError): 72 | def __init__(self, msg): 73 | self.name = "OptimizerConfigError: " 74 | self.message = msg 75 | super().__init__(msg) 76 | 77 | def __str__(self): 78 | return self.name + self.message -------------------------------------------------------------------------------- /tests/test_compiler_send.py: -------------------------------------------------------------------------------- 1 | from Qcover.core import Qcover 2 | from Qcover.backends import CircuitByQulacs 3 | from Qcover.optimizers import COBYLA 4 | from Qcover.compiler import CompilerForQAOA 5 | import networkx as nx 6 | import matplotlib.pyplot as plt 7 | 8 | # Qcover supports real quantum computers to solve combinatorial optimization problems. 9 | # You only need to transform the combinatorial optimization problem into a weight graph, 10 | # and you can use the quafu quantum computing cloud platform (http://quafu.baqis.ac.cn/) 11 | # to solve the corresponding problem. The following is an example of a max-cut problem. 12 | 13 | # The weight graph corresponding to the combinatorial optimization problem and transformed it to networkx format. 14 | nodes = [(0, 1), (1, 3), (2, 2), (3, 1), (4, 0), (5, 3)] 15 | edges = [(0, 1, -1), (1, 2, -4), (2, 3, 2), (3, 4, -2), (4, 5, -1), (1, 3, 0), (2, 4, 3)] 16 | graph = nx.Graph() 17 | for nd in nodes: 18 | u, w = nd[0], nd[1] 19 | graph.add_node(int(u), weight=int(w)) 20 | for ed in edges: 21 | u, v, w = ed[0], ed[1], ed[2] 22 | graph.add_edge(int(u), int(v), weight=int(w)) 23 | 24 | 25 | # draw weighted graph to be calculated 26 | new_labels = dict(map(lambda x: ((x[0], x[1]), str(x[2]['weight'])), graph.edges(data=True))) 27 | pos = nx.spring_layout(graph) 28 | # pos = nx.circular_layout(g) 29 | nx.draw_networkx(graph, pos=pos, node_size=400, font_size=13, node_color='y') 30 | # nx.draw_networkx_edge_labels(g, pos=pos, edge_labels=new_labels, font_size=15) 31 | nx.draw_networkx_edges(graph, pos, width=2, edge_color='g', arrows=False) 32 | plt.show() 33 | 34 | # Using Qcover to calculate the optimal parameters of QAOA circuit. 35 | p = 1 36 | bc = CircuitByQulacs() 37 | optc = COBYLA(options={'tol': 1e-3, 'disp': True}) 38 | qc = Qcover(graph, p=p, optimizer=optc, backend=bc) 39 | res = qc.run() 40 | optimal_params = res['Optimal parameter value'] 41 | 42 | # Compile and send the QAOA circuit to the quafu cloud. 43 | # Token parameter should be set according to your own account 44 | # For more introduction see https://github.com/ScQ-Cloud/pyquafu 45 | token = "IB2Vz-3bqNNRHPCIw1RcdBMgPq8LNeGZe4KbBYDE_0A.9BzN5MjMwkjN2EjOiAHelJCLzITM6ICZpJye.9JiN1IzUIJiOicGbhJCLiQ1VKJiOiAXe0Jye" 46 | cloud_backend = 'ScQ-P20' 47 | qcover_compiler = CompilerForQAOA(graph, p=p, optimal_params=optimal_params, apitoken=token, cloud_backend=cloud_backend) 48 | task_id = qcover_compiler.send(wait=True, shots=5000, task_name='MaxCut') 49 | # If you choose wait=True, you have to wait for the result to return. 50 | # If you choose wait=False, you can execute the following command to query the result status at any time, 51 | # and the result will be returned when the task is completed. 52 | quafu_solver = qcover_compiler.task_status_query(task_id) 53 | if quafu_solver: 54 | counts_energy = qcover_compiler.results_processing(quafu_solver) 55 | qcover_compiler.visualization(counts_energy) 56 | 57 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import platform 2 | from codecs import open 3 | from os import path 4 | 5 | #from Cython.Build import cythonize 6 | from setuptools import Extension, find_packages, setup 7 | 8 | here = path.abspath(path.dirname(__file__)) 9 | 10 | # Get the long description from the README file 11 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 12 | long_description = f.read() 13 | 14 | # def _process_requirements(): 15 | # packages = open('requirements.txt', "r", encoding="utf-16").read().strip().split('\n') 16 | # requires = [] 17 | # for pkg in packages: 18 | # if pkg.startswith('git+ssh'): 19 | # return_code = os.system('pip install {}'.format(pkg)) 20 | # assert return_code == 0, 'error, status_code is: {}, exit!'.format(return_code) 21 | # else: 22 | # requires.append("\"" + str(pkg) + "\"") 23 | # return requires 24 | 25 | # This reads the __version__ variable from Qcover/version.py 26 | __version__ = "" 27 | exec(open('Qcover/version.py').read()) 28 | 29 | requirements = [ 30 | "qiskit>=0.33.0", 31 | "projectq>=0.6.1.post0", 32 | "cirq>=0.13.0", 33 | "quimb>=1.3.0", 34 | "qulacs>=0.3.0", 35 | "pyquafu>=0.3.6" 36 | ] 37 | 38 | setup( 39 | name="Qcover", 40 | version=__version__, 41 | author="ntyz&finleyzhuang", 42 | author_email="puyn@baqis.ac.cn", 43 | description="Quantum computing solver", 44 | long_description=long_description, 45 | long_description_content_type='text/markdown', 46 | license="Apache-2.0 License", 47 | url="https://github.com/BAQIS-Quantum/Qcover", 48 | keywords="QAOA based combinational optimization solver QUBO quantum", 49 | 50 | packages=find_packages(), 51 | #ext_modules = cythonize("waveforms/math/prime.pyx"), 52 | include_package_data=True, 53 | #data_files=[('waveforms/Data', waveData)], 54 | install_requires=requirements, 55 | extras_require={ 56 | 'test': [ 57 | 'pytest>=4.4.0', 58 | ], 59 | 'docs': [ 60 | 'Sphinx', 61 | 'sphinxcontrib-napoleon', 62 | 'sphinxcontrib-zopeext', 63 | ], 64 | }, 65 | python_requires='>=3.8', 66 | classifiers=[ 67 | 'Development Status :: 3 - Alpha', 68 | 'Intended Audience :: Developers', 69 | 'Intended Audience :: Science/Research', 70 | 'Operating System :: Microsoft :: Windows', 71 | 'Operating System :: POSIX :: Linux', 72 | 'Operating System :: MacOS :: MacOS X', 73 | 'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator', 74 | 'Programming Language :: Python', 75 | 'Programming Language :: Python :: 3', 76 | 'Programming Language :: Python :: 3.8', 77 | 'Programming Language :: Python :: 3.9', 78 | ], 79 | project_urls={ # Optional 80 | 'Bug Reports': 'https://github.com/BAQIS-Quantum/Qcover/issues', 81 | 'Source': 'https://github.com/BAQIS-Quantum/Qcover', 82 | }, 83 | ) 84 | -------------------------------------------------------------------------------- /Qcover/optimizers/SHGO.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Optional 3 | import logging 4 | import numpy as np 5 | from scipy import optimize as opt 6 | from scipy.optimize import shgo 7 | from Qcover.optimizers import Optimizer 8 | from Qcover.exceptions import ArrayShapeError 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | class SHGO(Optimizer): 14 | """ 15 | SLSQP: a numerical optimization method for constrained problems 16 | 17 | based on scipy.optimize.minimize SLSQP. 18 | For further detail, please refer to 19 | https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html 20 | """ 21 | 22 | # pylint: disable=unused-argument 23 | def __init__(self, 24 | options: dict = None, 25 | initial_point: Optional[np.ndarray] = None) -> None: 26 | """ 27 | Args: 28 | options: some optional setting parameters such as: 29 | maxiter: Maximum number of function evaluations. 30 | disp: Set to True to print convergence messages. 31 | rhobeg: Reasonable initial changes to the variables. 32 | tol: Final accuracy in the optimization (not precisely guaranteed). 33 | This is a lower bound on the size of the trust region. 34 | """ 35 | super().__init__() 36 | self._p = None 37 | self._options = options 38 | self._initial_point = initial_point 39 | 40 | def optimize(self, objective_function): 41 | if self._initial_point is None: 42 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 43 | else: 44 | try: 45 | if len(self._initial_point) != 2 * self._p: 46 | raise ArrayShapeError("The shape of initial parameters is not match with p") 47 | except ArrayShapeError as e: 48 | print(e) 49 | sys.exit() 50 | 51 | # method = self._options['method'] if 'method' in self._options['method'] else 'SLSQP' 52 | sampling_method = self._options['sampling_method'] if 'sampling_method' in self._options else 'simplicial' 53 | minimizer_kwargs = self._options['minimizer_kwargs'] if 'minimizer_kwargs' in self._options else None 54 | bounds = [(None, None), ] * len(self._initial_point) 55 | res = shgo(objective_function, bounds=bounds, sampling_method=sampling_method, minimizer_kwargs=minimizer_kwargs) 56 | # res = opt.minimize(objective_function, 57 | # x0=np.array(self._initial_point), 58 | # args=self._p, 59 | # method='SLSQP', 60 | # jac=None, 61 | # options=self._options 62 | # ) 63 | 64 | return res.x, res.fun, res.nfev 65 | 66 | # opts = SLSQP(options={'maxiter': 100, 67 | # 'ftol': 1e-06, 68 | # 'iprint': 1, 69 | # 'disp': False, 70 | # 'eps': 1.4901161193847656e-08, 71 | # 'finite_diff_rel_step': None}) -------------------------------------------------------------------------------- /paper data/sk_problem/wholetensor.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from Qcover.applications.sherrington_kirkpatrick import SherringtonKirkpatrick 4 | 5 | 6 | from time import time 7 | import numpy as np 8 | import h5py 9 | 10 | import quimb as qu 11 | import quimb.tensor as qtn 12 | from scipy.optimize import minimize, rosen_der 13 | 14 | 15 | def qaoa_tensor(graph, p, params): 16 | N = len(graph.nodes) 17 | circ = qu.tensor.Circuit(N) 18 | 19 | for i in graph.nodes(): 20 | circ.apply_gate('H', i) 21 | 22 | for k in range(p): 23 | for i in graph.nodes: 24 | node_weight = graph.nodes[i]['weight'] 25 | 26 | circ.apply_gate('rz', 2 * params[2 * k] * node_weight, i) 27 | 28 | for edge in graph.edges: 29 | edge_weight = graph.get_edge_data(edge[0], edge[1])['weight'] 30 | 31 | gamma = -params[2 * k] * edge_weight 32 | circ.apply_gate('RZZ', gamma, edge[0], edge[1]) 33 | 34 | for i in graph.nodes: 35 | circ.apply_gate('rx', 2 * params[2 * k + 1], i) 36 | return circ 37 | 38 | 39 | def expectation(mx_g, circ, opt): 40 | expectation = 0 41 | ZZ = qu.pauli('Z') & qu.pauli('Z') 42 | for node in mx_g.nodes: 43 | w = mx_g.nodes[node]['weight'] 44 | expectation = w * circ.local_expectation(qu.pauli('Z'), node, optimize=opt) + expectation 45 | 46 | for edge in mx_g.edges: 47 | w = mx_g.get_edge_data(edge[0], edge[1])['weight'] 48 | expectation = w * circ.local_expectation(ZZ, edge, optimize=opt) + expectation 49 | return expectation.real 50 | 51 | 52 | def energy(params, mx_g, p,opt): 53 | circ = qaoa_tensor(mx_g, p, params) 54 | expec = expectation(mx_g, circ,opt) 55 | return expec 56 | 57 | 58 | 59 | p = 1 60 | opt = 'greedy' 61 | #num_nodes_list = np.arange(4,50,4) 62 | num_nodes_list = np.array([10,12]) 63 | time_tensor = np.zeros(len(num_nodes_list), dtype=float) 64 | 65 | 66 | cy_ind = 0 67 | max_step = 1 68 | for num_nodes in num_nodes_list: 69 | sk = SherringtonKirkpatrick(num_nodes) 70 | sk_graph = sk.run() 71 | 72 | gamma = np.random.rand(p) 73 | beta = np.random.rand(p) 74 | 75 | st = time() 76 | qser_whole_tensor = minimize(energy, np.asarray([gamma, beta]), args=(sk_graph, p,opt), 77 | method='COBYLA', 78 | tol=1e-14, 79 | jac=rosen_der, 80 | options={'gtol': 1e-8, 'maxiter': max_step, 'disp': True}) 81 | time_tensor[cy_ind] = time() - st 82 | cy_ind += 1 83 | 84 | dirs = '../data' 85 | if not os.path.exists(dirs): 86 | os.makedirs(dirs) 87 | 88 | if len(num_nodes_list) == 1: 89 | filename = '../data/sk_wtensor_p%i_nodesnum%i.h5'%(p, num_nodes_list[0]) 90 | else: 91 | filename = '../data/sk_wtensor_p%i.h5'%(p) 92 | data = h5py.File(filename, 'w') 93 | data['time_tensor'] = time_tensor 94 | data['num_nodes_list'] = num_nodes_list 95 | data['maxiter'] = max_step 96 | data['p'] = p 97 | data.close() 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /paper data/graph_coloring/decomp_tensor.py: -------------------------------------------------------------------------------- 1 | from tests.core_without_RQAOA import * 2 | import os 3 | 4 | from Qcover.backends import CircuitByTensor 5 | from Qcover.applications.graph_color import GraphColoring 6 | 7 | from time import time 8 | import numpy as np 9 | import h5py 10 | 11 | import quimb as qu 12 | import quimb.tensor as qtn 13 | 14 | 15 | def qaoa_tensor(graph, p, params): 16 | 17 | 18 | N = len(graph.nodes) 19 | circ = qu.tensor.Circuit(N) 20 | 21 | for i in graph.nodes(): 22 | circ.apply_gate('H', i) 23 | 24 | for k in range(p): 25 | for i in graph.nodes: 26 | node_weight = graph.nodes[i]['weight'] 27 | # print('ndw_%i' % node_weight) 28 | 29 | circ.apply_gate('rz', 2 * params[2 * k] * node_weight, i) 30 | 31 | for edge in graph.edges: 32 | edge_weight = graph.get_edge_data(edge[0], edge[1])['weight'] 33 | 34 | gamma = -params[2 * k] * edge_weight 35 | circ.apply_gate('RZZ', gamma, edge[0], edge[1]) 36 | 37 | for i in graph.nodes: 38 | circ.apply_gate('rx', 2 * params[2 * k + 1], i) 39 | return circ 40 | 41 | 42 | 43 | def expectation(mx_g, circ, opt): 44 | expectation = 0 45 | ZZ = qu.pauli('Z') & qu.pauli('Z') 46 | for node in mx_g.nodes: 47 | w = mx_g.nodes[node]['weight'] 48 | expectation = w * circ.local_expectation(qu.pauli('Z'), node, optimize=opt) + expectation 49 | 50 | for edge in mx_g.edges: 51 | w = mx_g.get_edge_data(edge[0], edge[1])['weight'] 52 | expectation = w * circ.local_expectation(ZZ, edge, optimize=opt) + expectation 53 | return expectation.real 54 | 55 | 56 | def energy(params, mx_g, p,opt): 57 | circ = qaoa_tensor(mx_g, p, params) 58 | expec = expectation(mx_g, circ,opt) 59 | return expec 60 | 61 | 62 | # num_nodes_list = np.arange(10,500,40) 63 | num_nodes_list = np.array([4,6]) 64 | p = 1 65 | cln = 3 66 | nd = 3 67 | opt = 'greedy' 68 | max_step = 1 69 | time_qcover_tensor = np.zeros(len(num_nodes_list), dtype=float) 70 | 71 | cy_ind = 0 72 | for num_nodes in num_nodes_list: 73 | 74 | gct = GraphColoring(node_num=num_nodes, color_num=cln, node_degree=nd) 75 | g = gct.run() 76 | 77 | quimb_bc = CircuitByTensor(contract_opt='greedy') 78 | optc = COBYLA(maxiter=1, tol=1e-6, disp=True) 79 | qc = Qcover(g, p=p, optimizer=optc, backend=quimb_bc) 80 | st = time() 81 | res = qc.run() 82 | time_qcover_tensor[cy_ind] = time() - st 83 | 84 | cy_ind += 1 85 | 86 | dirs = '../data' 87 | 88 | if not os.path.exists(dirs): 89 | os.makedirs(dirs) 90 | 91 | if len(num_nodes_list) == 1: 92 | filename = '../data/graphcolor_decomp_tensor_p%i_nodesnum%i_nd%i_cln%i.h5'%(p, num_nodes_list[0],nd,cln) 93 | else: 94 | filename = '../data/graphcolor_decomp_tensor_p%i_nd%i_cln%i.h5'%(p,nd,cln) 95 | data = h5py.File(filename, 'w') 96 | data['time_qcover_tensor'] = time_qcover_tensor 97 | data['num_nodes_list'] = num_nodes_list 98 | data['maxiter'] = max_step 99 | data['p'] = p 100 | data['nd'] = nd 101 | data['cln'] = cln 102 | data.close() 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /Qcover/utils.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import numpy as np 3 | 4 | def get_graph_weights(graph): 5 | """ 6 | get the weights of nodes and edges in graph 7 | Args: 8 | graph (nx.Graph): graph to get weight of nodes and edges 9 | Return: 10 | node weights form is dict{nid1: node_weight}, edges weights form is dict{(nid1, nid2): edge_weight} 11 | """ 12 | nodew = nx.get_node_attributes(graph, 'weight') 13 | edw = nx.get_edge_attributes(graph, 'weight') 14 | edgew = edw.copy() 15 | for key, val in edw.items(): 16 | edgew[(key[1], key[0])] = val 17 | 18 | return nodew, edgew 19 | 20 | 21 | def generate_graph_data(node_num, edge_num, weight_range=10): 22 | """ 23 | generate a simple graph‘s weights of nodes and edges with 24 | node number is node_num, edge number is edge_num 25 | Args: 26 | node_num (int): node number in graph 27 | edge_num (int): edge number in graph 28 | weight_range (int): weight range of nodes and edges 29 | Return: 30 | nodes(set of tuple(nid, node_weight)), edges(set of tuple(nid1, nid2, edge_weight)) 31 | """ 32 | if weight_range is None: 33 | weight_range = 10 34 | 35 | nodes = set() 36 | for i in range(node_num): 37 | ndw = np.random.choice(range(weight_range)) 38 | nodes |= {(i, ndw)} 39 | 40 | edges = set() 41 | cnt = edge_num 42 | max_edges = node_num * (node_num - 1) / 2 43 | if cnt > max_edges: 44 | cnt = max_edges 45 | while cnt > 0: 46 | u = np.random.randint(node_num) 47 | v = np.random.randint(node_num) 48 | if u == v: # without self loop 49 | continue 50 | flg = 0 51 | for e in edges: # without duplicated edges 52 | if set(e[:2]) == set([v, u]): 53 | flg = 1 54 | break 55 | if flg == 1: 56 | continue 57 | edw = np.random.choice(range(weight_range)) 58 | edges |= {(u, v, edw)} 59 | cnt -= 1 60 | return nodes, edges 61 | 62 | 63 | def generate_weighted_graph(nodes, edges, weight_range=10): 64 | """ 65 | generate graph from nodes list and edges list which identify the nodes and edges 66 | that should be add in graph, and the random weight range of every node and edge. 67 | Args: 68 | nodes (list/set): list of node idex / node-weight map, element form is tuple(nid, weight) 69 | edges (list/set): list of edge: (e_idex1, e_idex2) / edge-weight map, element form is tuple(nid1, nid2, edge_weight) 70 | weight_range (int): random weight range of every node and edge 71 | Returns: 72 | g (nx.Graph): graph generated by args 73 | """ 74 | g = nx.Graph() 75 | if isinstance(nodes, list) and isinstance(edges, list): 76 | for v in nodes: 77 | w = np.random.choice(range(weight_range)) 78 | g.add_node(v, weight=w) 79 | 80 | for e in edges: 81 | w = np.random.choice(range(weight_range)) 82 | g.add_edge(e[0], e[1], weight=w) 83 | else: 84 | for item in nodes: 85 | g.add_node(item[0], weight=item[1]) 86 | 87 | for item in edges: 88 | g.add_edge(item[0], item[1], weight=item[2]) 89 | return g -------------------------------------------------------------------------------- /Qcover/examples/g000036.txt: -------------------------------------------------------------------------------- 1 | # NAME: i167.txt 2 | # COMMENT: \f0\fs26 \cf0 #http://www.maths.gla.ac.uk/~es/srgraphs.html\ 3 | 40 240 4 | 1 2 1 5 | 1 3 1 6 | 1 4 1 7 | 1 5 1 8 | 1 6 1 9 | 1 7 1 10 | 1 8 1 11 | 1 9 1 12 | 1 10 1 13 | 1 11 1 14 | 1 12 1 15 | 1 13 1 16 | 2 3 1 17 | 2 4 1 18 | 2 14 1 19 | 2 15 1 20 | 2 16 1 21 | 2 17 1 22 | 2 18 1 23 | 2 19 1 24 | 2 20 1 25 | 2 21 1 26 | 2 22 1 27 | 3 4 1 28 | 3 23 1 29 | 3 24 1 30 | 3 25 1 31 | 3 26 1 32 | 3 27 1 33 | 3 28 1 34 | 3 29 1 35 | 3 30 1 36 | 3 31 1 37 | 4 32 1 38 | 4 33 1 39 | 4 34 1 40 | 4 35 1 41 | 4 36 1 42 | 4 37 1 43 | 4 38 1 44 | 4 39 1 45 | 4 40 1 46 | 5 6 1 47 | 5 7 1 48 | 5 14 1 49 | 5 15 1 50 | 5 16 1 51 | 5 23 1 52 | 5 24 1 53 | 5 25 1 54 | 5 32 1 55 | 5 33 1 56 | 5 34 1 57 | 6 7 1 58 | 6 17 1 59 | 6 18 1 60 | 6 19 1 61 | 6 26 1 62 | 6 27 1 63 | 6 28 1 64 | 6 35 1 65 | 6 36 1 66 | 6 37 1 67 | 7 20 1 68 | 7 21 1 69 | 7 22 1 70 | 7 29 1 71 | 7 30 1 72 | 7 31 1 73 | 7 38 1 74 | 7 39 1 75 | 7 40 1 76 | 8 9 1 77 | 8 10 1 78 | 8 14 1 79 | 8 17 1 80 | 8 20 1 81 | 8 23 1 82 | 8 26 1 83 | 8 29 1 84 | 8 32 1 85 | 8 35 1 86 | 8 38 1 87 | 9 10 1 88 | 9 15 1 89 | 9 18 1 90 | 9 21 1 91 | 9 24 1 92 | 9 27 1 93 | 9 30 1 94 | 9 33 1 95 | 9 36 1 96 | 9 39 1 97 | 10 16 1 98 | 10 19 1 99 | 10 22 1 100 | 10 25 1 101 | 10 28 1 102 | 10 31 1 103 | 10 34 1 104 | 10 37 1 105 | 10 40 1 106 | 11 12 1 107 | 11 13 1 108 | 11 14 1 109 | 11 18 1 110 | 11 22 1 111 | 11 24 1 112 | 11 28 1 113 | 11 29 1 114 | 11 34 1 115 | 11 35 1 116 | 11 39 1 117 | 12 13 1 118 | 12 15 1 119 | 12 19 1 120 | 12 20 1 121 | 12 25 1 122 | 12 26 1 123 | 12 30 1 124 | 12 32 1 125 | 12 36 1 126 | 12 40 1 127 | 13 16 1 128 | 13 17 1 129 | 13 21 1 130 | 13 23 1 131 | 13 27 1 132 | 13 31 1 133 | 13 33 1 134 | 13 37 1 135 | 13 38 1 136 | 14 15 1 137 | 14 19 1 138 | 14 26 1 139 | 14 28 1 140 | 14 31 1 141 | 14 33 1 142 | 14 38 1 143 | 14 39 1 144 | 15 22 1 145 | 15 23 1 146 | 15 27 1 147 | 15 29 1 148 | 15 36 1 149 | 15 37 1 150 | 15 40 1 151 | 16 17 1 152 | 16 20 1 153 | 16 24 1 154 | 16 27 1 155 | 16 28 1 156 | 16 32 1 157 | 16 39 1 158 | 16 40 1 159 | 17 22 1 160 | 17 26 1 161 | 17 29 1 162 | 17 30 1 163 | 17 33 1 164 | 17 34 1 165 | 17 36 1 166 | 18 20 1 167 | 18 21 1 168 | 18 25 1 169 | 18 28 1 170 | 18 29 1 171 | 18 32 1 172 | 18 33 1 173 | 18 37 1 174 | 19 21 1 175 | 19 27 1 176 | 19 30 1 177 | 19 31 1 178 | 19 32 1 179 | 19 34 1 180 | 19 35 1 181 | 20 23 1 182 | 20 25 1 183 | 20 31 1 184 | 20 35 1 185 | 20 36 1 186 | 20 39 1 187 | 21 23 1 188 | 21 24 1 189 | 21 26 1 190 | 21 34 1 191 | 21 38 1 192 | 21 40 1 193 | 22 24 1 194 | 22 25 1 195 | 22 30 1 196 | 22 35 1 197 | 22 37 1 198 | 22 38 1 199 | 23 28 1 200 | 23 30 1 201 | 23 34 1 202 | 23 35 1 203 | 23 37 1 204 | 24 26 1 205 | 24 31 1 206 | 24 32 1 207 | 24 35 1 208 | 24 36 1 209 | 25 26 1 210 | 25 27 1 211 | 25 33 1 212 | 25 34 1 213 | 25 38 1 214 | 26 37 1 215 | 26 39 1 216 | 26 40 1 217 | 27 29 1 218 | 27 35 1 219 | 27 38 1 220 | 27 39 1 221 | 28 30 1 222 | 28 36 1 223 | 28 38 1 224 | 28 40 1 225 | 29 31 1 226 | 29 32 1 227 | 29 34 1 228 | 29 40 1 229 | 30 32 1 230 | 30 33 1 231 | 30 39 1 232 | 31 33 1 233 | 31 36 1 234 | 31 37 1 235 | 32 37 1 236 | 32 38 1 237 | 33 35 1 238 | 33 40 1 239 | 34 36 1 240 | 34 39 1 241 | 35 40 1 242 | 36 38 1 243 | 37 39 1 244 | -------------------------------------------------------------------------------- /tests/draw_figure.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from matplotlib.ticker import MultipleLocator, FormatStrFormatter 3 | node_d, p = [2], [3, 4, 5, 6, 7] 4 | Fourier_tm = [39.82834434509277, 70.68283319473267, 221.981107711792, 219.1239206790924, 961.8482122421265] 5 | COBYLA_tm = [12.406248569488525, 46.92031240463257, 112.66444778442383, 414.25853872299194, 3002.007876396179] 6 | 7 | Fourier_ep = [-17.52356047443737, -19.95655456409755, -20.121290948456906, -83.90646488094582, -17.878072229328218] 8 | COBYLA_ep = [-75.40026093126744, -32.05658765533073, -28.55836130045919, -29.414369385675837, -46.222880091593204] 9 | 10 | plt.figure(1) 11 | plt.plot(range(3, 8), Fourier_tm, "ob-", label="Fourier") 12 | plt.plot(range(3, 8), COBYLA_tm, "^r-", label="COBYLA") 13 | plt.ylabel('Times cost') 14 | plt.xlabel('P value') 15 | plt.title("node = 23, degree = 2") 16 | plt.legend() 17 | plt.show() 18 | 19 | plt.figure(2) 20 | plt.plot(range(3, 8), Fourier_ep, "ob-", label="Fourier") 21 | plt.plot(range(3, 8), COBYLA_ep, "^r-", label="COBYLA") 22 | plt.ylabel('Expectation value') 23 | plt.xlabel('P value') 24 | plt.title("node = 23, degree = 2") 25 | plt.legend() 26 | plt.show() 27 | 28 | 29 | 30 | # plt.figure(1) 31 | # 手动设置标度 32 | # x = [1, 4, 8, 12, 16, 25, 40, 60] #虚假的x值,用来等间距分割 33 | # x_index = [str(e) for e in num_nodes] # ['4', '6', '8', '10', '12', '14', '100', 1000, 10000] x 轴显示的刻度 34 | # plt.plot(x[:len(qiskit_tm)], qiskit_tm, "ob-", label="Qiskit", marker='d') 35 | # plt.plot(x, qcover_tm, "^r-", label="QCover", marker='d') 36 | # plt.xticks(x, x_index) # range(1, len(num_nodes) + 1) 37 | 38 | # plt.plot(num_nodes[:len(qiskit_tm)], qiskit_tm, "ob-", label="Qiskit", marker='d') 39 | # plt.plot(num_nodes, qcover_tm, "*r-", label="QCover", marker='d') 40 | #设置指数标度 41 | # plt.xscale("symlog") 42 | # plt.yscale("symlog") 43 | # plt.xticks(x, x_index) # range(1, len(num_nodes) + 1) 44 | # plt.ylabel('single query times/(log10 s)') 45 | # plt.xlabel('node numbers') 46 | 47 | # plt.legend() 48 | # plt.savefig('/home/wfzhuang/data/Qcover/result_log/backends_compare/res_serial_%s.png' % str(i)) 49 | # plt.savefig('/public/home/humengjun/QCover/result_log/maxcut_time_large.png') 50 | # plt.savefig('/home/puyanan/QCover/result_log/backends_compare/tm_serial_%s.png' % str(i)) 51 | # plt.savefig('E:/Working_projects/QAOA/QCover/result_log/maxcut_time_large.png') # maxcut_serial 52 | # plt.show() 53 | # plt.cla() 54 | 55 | 56 | # 不等距坐标轴 57 | # import matplotlib.pyplot as plt 58 | # import numpy as np 59 | # import pandas as pd 60 | # plt.rcParams['font.sans-serif']=['SimHei'] # 处理中文无法正常显示的问题 成功 61 | # plt.rcParams['axes.unicode_minus'] = False #负号显示 62 | # plt.xlabel("x轴") # 设置x轴名称 63 | # plt.ylabel("y轴") # 设置y轴名称 64 | # plt.title("标题") # 设置标题 65 | # 66 | # x=[1,2,3,4,5,6] #虚假的x值,用来等间距分割 67 | # x_index=['1','10','100','1000','10000','100000'] # x 轴显示的刻度 68 | # y=[0.1,0.15,0.2,0.3,0.35,0.5] #y值 69 | # plt.plot(x,y,marker='d') 70 | # _ = plt.xticks(x,x_index) # 显示坐标字 71 | # plt.show() 72 | # 73 | # # 纵坐标刻度为10^n 74 | # import matplotlib.pyplot as plt 75 | # y=[pow(10,i) for i in range(0,10)] 76 | # x=range(0,len(y)) 77 | # plt.plot(x, y, 'r') 78 | # plt.yscale('log')#设置纵坐标的缩放,写成m³格式 79 | # plt.show() -------------------------------------------------------------------------------- /Qcover/optimizers/SPSA.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Optional 3 | import logging 4 | import numpy as np 5 | from scipy import optimize as opt 6 | from Qcover.optimizers import Optimizer 7 | from Qcover.exceptions import ArrayShapeError 8 | import warnings 9 | warnings.filterwarnings("ignore") 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | class SPSA(Optimizer): 14 | # pylint: disable=unused-argument 15 | def __init__(self, 16 | options: dict = None, # {'maxiter':300, 'disp':True, 'rhobeg': 1.0, 'tol':1e-6}, 17 | initial_point: Optional[np.ndarray] = None) -> None: 18 | """ 19 | Args: 20 | options: some optional setting parameters such as: 21 | maxiter: Maximum number of function evaluations. 22 | disp: Set to True to print convergence messages. 23 | rhobeg: Reasonable initial changes to the variables. 24 | tol: Final accuracy in the optimization (not precisely guaranteed). 25 | This is a lower bound on the size of the trust region. 26 | """ 27 | super().__init__() 28 | self._p = None 29 | self._options = options 30 | self._initial_point = initial_point 31 | 32 | def _minimize(self, loss): 33 | if 'A' in self._options: 34 | A = self._options["A"] 35 | else: 36 | A = 20 37 | 38 | if 'R' in self._options: 39 | R = self._options['R'] 40 | else: 41 | R = 0.2 42 | 43 | if 'a0' in self._options: 44 | a0 = self._options["a0"] 45 | else: 46 | a0 = 1.3 47 | 48 | if 'c0' in self._options: 49 | c0 = self._options['c0'] 50 | else: 51 | c0 = 0.2 52 | 53 | if 'tol' in self._options: 54 | tol = self._options['tol'] 55 | else: 56 | tol = 1e-6 57 | 58 | if 'maxiter' in self._options: 59 | maxiter = self._options['maxiter'] 60 | else: 61 | maxiter = 500 62 | 63 | # a0 = 1.2 64 | # c0 = 1.0 65 | # prepare some initials 66 | x = np.asarray(self._initial_point) 67 | nfevs = 0 68 | 69 | for i in range(1, maxiter + 1): 70 | a = a0 / (i + 1) ** A 71 | c = c0 / (i + 1) ** R 72 | # delta = np.random.random() - 0.5 73 | delta = (2 * np.random.randint(0, 2, size=len(self._initial_point)) - 1) * c 74 | diff = loss(x + delta) - loss(x - delta) # 75 | grad = 0.5 * diff / delta # 76 | 77 | x_next = x - a * grad 78 | x = x_next 79 | stepsize = np.linalg.norm(grad) # the distance between grad to zero 80 | nfevs += 1 81 | # check termination 82 | if stepsize < tol: 83 | break 84 | 85 | return x, loss(x), nfevs 86 | 87 | def optimize(self, objective_function): 88 | if self._initial_point is None: 89 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 90 | else: 91 | try: 92 | if len(self._initial_point) != 2 * self._p: 93 | raise ArrayShapeError("The shape of initial parameters is not match with p") 94 | except ArrayShapeError as e: 95 | print(e) 96 | sys.exit() 97 | 98 | return self._minimize(objective_function) -------------------------------------------------------------------------------- /paper data/3-regular_graph/wholetensor.py: -------------------------------------------------------------------------------- 1 | import os 2 | from Qcover.applications.max_cut import MaxCut 3 | from time import time 4 | import numpy as np 5 | import h5py 6 | # import ast 7 | 8 | 9 | import quimb as qu 10 | import quimb.tensor as qtn 11 | import networkx as nx 12 | from scipy.optimize import minimize, rosen_der 13 | 14 | 15 | def qaoa_tensor(graph, p, params): 16 | 17 | 18 | N = len(graph.nodes) 19 | circ = qu.tensor.Circuit(N) 20 | 21 | for i in graph.nodes(): 22 | circ.apply_gate('H', i) 23 | 24 | for k in range(p): 25 | for i in graph.nodes: 26 | node_weight = graph.nodes[i]['weight'] 27 | 28 | circ.apply_gate('rz', 2 * params[2 * k] * node_weight, i) 29 | 30 | for edge in graph.edges: 31 | edge_weight = graph.get_edge_data(edge[0], edge[1])['weight'] 32 | 33 | gamma = -params[2 * k] * edge_weight 34 | circ.apply_gate('RZZ', gamma, edge[0], edge[1]) 35 | 36 | for i in graph.nodes: 37 | circ.apply_gate('rx', 2 * params[2 * k + 1], i) 38 | return circ 39 | 40 | 41 | def expectation(mx_g, circ,opt): 42 | expectation = 0 43 | ZZ = qu.pauli('Z') & qu.pauli('Z') 44 | for node in mx_g.nodes: 45 | w = mx_g.nodes[node]['weight'] 46 | expectation = w * circ.local_expectation(qu.pauli('Z'), node, optimize=opt) + expectation 47 | 48 | for edge in mx_g.edges: 49 | w = mx_g.get_edge_data(edge[0], edge[1])['weight'] 50 | expectation = w * circ.local_expectation(ZZ, edge, optimize=opt) + expectation 51 | return expectation.real 52 | 53 | 54 | def energy(params, mx_g, p ,opt): 55 | circ = qaoa_tensor(mx_g, p, params) 56 | expec = expectation(mx_g, circ, opt) 57 | return expec 58 | 59 | nd = 3 60 | p = 1 61 | num_nodes_list = np.array([10,14]) 62 | time_tensor = np.zeros(len(num_nodes_list), dtype=float) 63 | exp_tensor = np.zeros_like(time_tensor) #expectation value 64 | parametr_f_tensor = np.zeros([len(num_nodes_list),2, p], dtype=float) 65 | 66 | cy_ind = 0 67 | max_step = 1 68 | opt = 'greedy' #for p=4, we chose opt = 'greedy-rf' 69 | for num_nodes in num_nodes_list: 70 | 71 | mxt = MaxCut(node_num = num_nodes, node_degree=nd) 72 | mc_mat = nx.adj_matrix(mxt.graph).A 73 | g = mxt.run() 74 | 75 | gamma = np.random.rand(p) 76 | beta = np.random.rand(p) 77 | 78 | st = time() 79 | qser_whole_tensor = minimize(energy, np.asarray([gamma, beta]), args=(g, p, opt), 80 | method='COBYLA', 81 | tol=1e-14, 82 | jac=rosen_der, 83 | options={'gtol': 1e-8, 'maxiter': max_step, 'disp': True}) 84 | time_tensor[cy_ind] = time() - st 85 | exp_tensor[cy_ind] = qser_whole_tensor.fun 86 | parametr_f_tensor[cy_ind,0,:] = qser_whole_tensor.x[0,:] 87 | parametr_f_tensor[cy_ind,1,:] = qser_whole_tensor.x[1,:] 88 | 89 | cy_ind += 1 90 | 91 | dirs = '../data' 92 | if not os.path.exists(dirs): 93 | os.makedirs(dirs) 94 | 95 | if len(num_nodes_list) == 1: 96 | filename = '../data/Qcover_wtensor_p%i_nd%i_nodesnum%i.h5'%(p, nd, num_nodes_list[0]) 97 | else: 98 | filename = '../data/Qcover_wtensor_p%i_nd%i.h5'%(p, nd) 99 | data = h5py.File(filename, 'w') 100 | data['time_tensor'] = time_tensor 101 | data['exp_tensor'] = exp_tensor 102 | data['parametr_f_tensor'] = parametr_f_tensor 103 | data['num_nodes_list'] = num_nodes_list 104 | data['maxiter'] = max_step 105 | data['nd'] = nd 106 | data['p'] = p 107 | data.close() 108 | 109 | 110 | -------------------------------------------------------------------------------- /paper data/graph_coloring/wholetensor.py: -------------------------------------------------------------------------------- 1 | import os 2 | from Qcover.applications.graph_color import GraphColoring 3 | from time import time 4 | import numpy as np 5 | import h5py 6 | import quimb as qu 7 | import quimb.tensor as qtn 8 | from scipy.optimize import minimize, rosen_der 9 | 10 | 11 | def qaoa_tensor(graph, p, params): 12 | 13 | N = len(graph.nodes) 14 | circ = qu.tensor.Circuit(N) 15 | 16 | for i in graph.nodes(): 17 | circ.apply_gate('H', i) 18 | 19 | for k in range(p): 20 | for i in graph.nodes: 21 | node_weight = graph.nodes[i]['weight'] 22 | circ.apply_gate('rz', 2 * params[2 * k] * node_weight, i) 23 | 24 | for edge in graph.edges: 25 | edge_weight = graph.get_edge_data(edge[0], edge[1])['weight'] 26 | 27 | gamma = -params[2 * k] * edge_weight 28 | circ.apply_gate('RZZ', gamma, edge[0], edge[1]) 29 | 30 | for i in graph.nodes: 31 | circ.apply_gate('rx', 2 * params[2 * k + 1], i) 32 | return circ 33 | 34 | def expectation(mx_g, circ, opt): 35 | expectation = 0 36 | ZZ = qu.pauli('Z') & qu.pauli('Z') 37 | for node in mx_g.nodes: 38 | # warning 39 | w = mx_g.nodes[node]['weight'] 40 | expectation = w * circ.local_expectation(qu.pauli('Z'), node, optimize=opt) + expectation 41 | 42 | for edge in mx_g.edges: 43 | # warning 44 | w = mx_g.get_edge_data(edge[0], edge[1])['weight'] 45 | expectation = w * circ.local_expectation(ZZ, edge, optimize=opt) + expectation 46 | return expectation.real 47 | 48 | def energy(params, mx_g, p,opt): 49 | circ = qaoa_tensor(mx_g, p, params) 50 | expec = expectation(mx_g, circ,opt) 51 | return expec 52 | 53 | p = 1 54 | opt = 'greedy' 55 | # num_nodes_list = np.arange(10,500,40) 56 | num_nodes_list = np.array([4,6]) 57 | cln = 3 58 | nd = 3 59 | time_tensor = np.zeros(len(num_nodes_list), dtype=float) 60 | exp_tensor = np.zeros_like(time_tensor) #expectation value 61 | parametr_f_tensor = np.zeros([len(num_nodes_list),2, p], dtype=float) 62 | cy_ind = 0 63 | max_step = 1 64 | for num_nodes in num_nodes_list: 65 | gct = GraphColoring(node_num=num_nodes, color_num=cln, node_degree=nd) 66 | ising_g = gct.run() 67 | gamma = np.random.rand(p) 68 | beta = np.random.rand(p) 69 | 70 | st = time() 71 | qser_whole_tensor = minimize(energy, np.asarray([gamma, beta]), args=(ising_g, p,opt), 72 | method='COBYLA', 73 | tol=1e-14, 74 | jac=rosen_der, 75 | options={'gtol': 1e-8, 'maxiter': max_step, 'disp': True}) 76 | time_tensor[cy_ind] = time() - st 77 | exp_tensor[cy_ind] = qser_whole_tensor.fun 78 | parametr_f_tensor[cy_ind,0,:] = qser_whole_tensor.x[0,:] 79 | parametr_f_tensor[cy_ind,1,:] = qser_whole_tensor.x[1,:] 80 | 81 | cy_ind += 1 82 | 83 | dirs = '../data' 84 | if not os.path.exists(dirs): 85 | os.makedirs(dirs) 86 | 87 | if len(num_nodes_list) == 1: 88 | filename = '../data/graphcolor_wtensor_p%i_nodesnum%i_nd%i_cln%i.h5'%(p, num_nodes_list[0],nd,cln) 89 | else: 90 | filename = '../data/graphcolor_wtensor_p%i_nd%i_cln%i.h5'%(p,nd,cln) 91 | data = h5py.File(filename, 'w') 92 | data['time_tensor'] = time_tensor 93 | data['exp_tensor'] = exp_tensor 94 | data['parametr_f_tensor'] = parametr_f_tensor 95 | data['num_nodes_list'] = num_nodes_list 96 | data['maxiter'] = max_step 97 | data['p'] = p 98 | data['nd'] = nd 99 | data['cln'] = cln 100 | data.close() 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /tests/pros_tests.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | 5 | #test codes used in core 6 | 7 | #General01Programming 8 | # element_list = ['a','b','c','d','e'] 9 | # element_list_len = len(element_list) 10 | # element_weight = [6,4,8,5,5] 11 | # coefficients = [[2,2,4,3,2],[1,2,2,1,2],[3,3,2,4,4]] 12 | # signs = ['<=', '=', '>='] 13 | # constants = [7,4,5] 14 | # penalty = 10 15 | # slack_1 = 3 16 | 17 | # from Qcover.applications import General01Programming 18 | # gp = General01Programming(element_list=element_list, 19 | # signs=signs, 20 | # b=constants, 21 | # length=element_list_len, 22 | # weight=element_weight, 23 | # element_set=coefficients,P=penalty,slack_1=slack_1) 24 | # ising_g, shift = gp.run() 25 | 26 | #Max2Sat 27 | # clauses_matrix = [[1,1,0,0],[1,-1,0,0],[-1,1,0,0],[-1,-1,0,0],[-1,0,1,0],[-1,0,-1,0], 28 | # [0,1,-1,0],[0,1,0,1],[0,-1,1,0],[0,-1,-1,0],[0,0,1,1],[0,0,-1,-1]] 29 | # variable_number = len(clauses_matrix[0]) 30 | 31 | # from Qcover.applications import Max2Sat 32 | # m2s = Max2Sat(clauses=clauses_matrix,variable_no=variable_number) 33 | # ising_g, shift = m2s.run() 34 | 35 | #MinimumVertexCover 36 | 37 | # adjacency_matrix = np.array([[0, 1, 1, 0, 0], 38 | # [1, 0, 0, 1, 0], 39 | # [1, 0, 0, 1, 1], 40 | # [0, 1, 1, 0, 1], 41 | # [0, 0, 1, 1, 0]]) 42 | # penalty = 8 43 | # from Qcover.applications import MinimumVertexCover 44 | # mvc = MinimumVertexCover(graph=g, P=penalty) 45 | # ising_g, shift = mvc.run() 46 | 47 | #NumberPartition 48 | # number_list_len = 5 49 | 50 | # from Qcover.applications import NumberPartition 51 | # np = NumberPartition(length=number_list_len) 52 | # ising_g, shift = np.run() 53 | 54 | #QadraticKnapsack 55 | # v_list = [[2,4,3,5],[4,5,1,3],[3,1,2,2],[5,3,2,4]] 56 | # length = len(v_list) 57 | # subset = [8,6,5,3] 58 | # constant = [16] 59 | # penalty = 10 60 | 61 | # from Qcover.applications import QadraticKnapsack 62 | # qk = QadraticKnapsack(v=v_list, length=length, element_set=subset, b=constant, P=penalty) 63 | # ising_g, shift = qk.run() 64 | 65 | #QadraticAssignment 66 | # flow_matrix = [[0,5,2],[5,0,3],[2,3,0]] 67 | # distance_matrix = [[0,8,15],[8,0,13],[15,13,0]] 68 | # n = len(flow_matrix) 69 | # subsets = [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9]] 70 | # penalty = 200 71 | 72 | # from Qcover.applications import QadraticAssignment 73 | # qa = QadraticAssignment(flow=flow_matrix, distance=distance_matrix, element_set=subsets, n=n, P=penalty) 74 | # ising_g, shift = qa.run() 75 | 76 | #SetPacking 77 | # element_list = ['a','b','c','d'] 78 | # element_list_len = len(element_list) 79 | # element_weight = [10,8,10,12] 80 | # # element_weight = np.ones((element_list_len,), dtype=int) 81 | # subsets = [[1,2],[1,3,4]] 82 | # penalty = 6 83 | 84 | # from Qcover.applications import SetPacking 85 | # sp = SetPacking(element_list=element_list,length=element_list_len, weight=element_weight, element_set=subsets, P=penalty) 86 | # ising_g, shift = sp.run() 87 | 88 | # SetPartitioning 89 | # element_list = ['a','b','c','d','e','f'] 90 | # element_list_len = len(element_list) 91 | # element_weight = [3,2,1,1,3,2] 92 | # subsets = [[1,3,6],[2,3,5,6],[3,4,5],[1,2,4,6]] 93 | # penalty = 10 94 | 95 | 96 | # from Qcover.applications import SetPartitioning 97 | # sp = SetPartitioning(element_list=element_list,length=element_list_len, weight=element_weight, element_set =subsets, P=penalty) 98 | # ising_g, shift = sp.run() -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # file 不需要提交的文件 2 | config.ini 3 | ​ 4 | # log 不需要提交的任意包含后缀名为log的文件 5 | *.log 6 | ​ 7 | # Package Files 不需要提交的任意包含后缀名为jar的文件 8 | *.jar 9 | 10 | 11 | # Byte-compiled / optimized / DLL files 12 | __pycache__/ 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | share/python-wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .nox/ 53 | .coverage 54 | .coverage.* 55 | .cache 56 | nosetests.xml 57 | coverage.xml 58 | *.cover 59 | *.py,cover 60 | .hypothesis/ 61 | .pytest_cache/ 62 | cover/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | db.sqlite3 72 | db.sqlite3-journal 73 | 74 | # Flask stuff: 75 | instance/ 76 | .webassets-cache 77 | 78 | # Scrapy stuff: 79 | .scrapy 80 | 81 | # Sphinx documentation 82 | docs/_build/ 83 | 84 | # PyBuilder 85 | .pybuilder/ 86 | target/ 87 | 88 | # Jupyter Notebook 89 | .ipynb_checkpoints 90 | 91 | # IPython 92 | profile_default/ 93 | ipython_config.py 94 | 95 | # pyenv 96 | # For a library or package, you might want to ignore these files since the code is 97 | # intended to run in multiple environments; otherwise, check them in: 98 | .python-version 99 | 100 | # pipenv 101 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 102 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 103 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 104 | # install all needed dependencies. 105 | #Pipfile.lock 106 | 107 | # poetry 108 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 109 | # This is especially recommended for binary packages to ensure reproducibility, and is more 110 | # commonly ignored for libraries. 111 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 112 | #poetry.lock 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | .idea/ 163 | 164 | .ipynb_checkpoints/ 165 | 166 | # VSCode 167 | .vscode/ 168 | 169 | ### Files not published temporarily ### 170 | solver/ 171 | Qcover/backends/circuitbyquafu.py -------------------------------------------------------------------------------- /tests/spinmodel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Mar 16 14:30:29 2022 4 | 5 | @author: humengjun 6 | """ 7 | import random 8 | 9 | import qiskit 10 | 11 | from qiskit import * 12 | 13 | from qiskit import Aer 14 | 15 | from qiskit.circuit import Parameter 16 | 17 | from qiskit.visualization import plot_state_city 18 | 19 | from qiskit.visualization import plot_histogram 20 | 21 | import numpy as np 22 | 23 | import networkx as nx 24 | import matplotlib.pyplot as plt 25 | from scipy.optimize import minimize 26 | 27 | backend = Aer.get_backend('qasm_simulator') 28 | backend.shots = 1024*500 29 | 30 | 31 | #set initial measured spin state 32 | phi = np.pi/2 33 | 34 | 35 | def create_qaoa_cir(G, theta): 36 | N_qubits = len(G.nodes()) 37 | p = len(theta)//2 38 | qc = QuantumCircuit(N_qubits) 39 | 40 | gamma = theta[:p] 41 | beta = theta[p:] 42 | 43 | for i in range((N_qubits-1)//2): 44 | qc.h(i) 45 | 46 | qc.ry(phi, (N_qubits-1)//2) 47 | 48 | for i in range((N_qubits+1)//2, N_qubits): 49 | qc.h(i) 50 | 51 | for irep in range(p): 52 | for pair in list(G.edges()): 53 | qc.rzz(-2*gamma[irep], pair[0], pair[1]) 54 | 55 | #for i in range(N_qubits): 56 | # qc.rx(2*beta[irep], i) 57 | 58 | for i in range((N_qubits-1)//2): 59 | qc.rx(2*beta[irep], i) 60 | for i in range((N_qubits+1)//2, N_qubits): 61 | qc.rx(2*beta[irep], i) 62 | 63 | qc.measure_all() 64 | 65 | return qc 66 | 67 | 68 | def Ising_obj(x, G): 69 | obj = 0 70 | for i, j in G.edges(): 71 | if x[i] == x[j]: 72 | obj -= 1 73 | else: 74 | obj +=1 75 | return obj 76 | 77 | 78 | 79 | def compute_expectation(counts, G): 80 | avg = 0 81 | sum_count = 0 82 | for bitstring, count in counts.items(): 83 | obj = Ising_obj(bitstring, G) 84 | avg += obj * count 85 | sum_count += count 86 | return avg/sum_count 87 | 88 | 89 | def get_expectation(G, theta, shots=1024): 90 | backend = Aer.get_backend('qasm_simulator') 91 | backend.shots = shots 92 | 93 | def execute_circ(theta): 94 | 95 | qc = create_qaoa_cir(G, theta) 96 | counts = backend.run(qc, seed_simulator=10, nshots=1024*100).result().get_counts() 97 | 98 | return compute_expectation(counts, G) 99 | 100 | return execute_circ 101 | 102 | 103 | def plot_counts(x): 104 | qc_res = create_qaoa_cir(G, x) 105 | counts = backend.run(qc_res, seed_simulator=10).result().get_counts() 106 | qc_res.draw('mpl') 107 | plot_histogram(counts) 108 | return counts 109 | 110 | 111 | G = nx.Graph() 112 | N=7 113 | #nearest interaction 114 | for i in range(N-1): 115 | G.add_edge(i, i+1, weight=-1) 116 | for i in range(N): 117 | G.add_node(i, weight=0) 118 | 119 | #all connection 120 | #for i in range(N-1): 121 | # for j in range(i+1, N): 122 | # G.add_edge(i, j, weight=-1) 123 | #for i in range(N): 124 | #G.add_node(i, weight=0) 125 | 126 | # theta = [1, 1, 1, 1, 1, 1, 1, 1] # 127 | p = 20 128 | theta = [random.random() for i in range(2 * p)] 129 | exp = get_expectation(G, theta) 130 | res = minimize(exp, theta, method="COBYLA") 131 | print(res) 132 | 133 | qc_res = create_qaoa_cir(G, res.x) 134 | counts = backend.run(qc_res, seed_simulator=10).result().get_counts() 135 | qc_res.draw('mpl') 136 | plot_histogram(counts) 137 | plt.show() 138 | # P = plot_counts(res.x) 139 | 140 | 141 | # backend1 = Aer.get_backend('statevector_simulator') 142 | # 143 | # job1 = backend1.run(qc) 144 | # 145 | # result1 = job1.result() 146 | # 147 | # outputstate = result1.get_statevector(qc) 148 | 149 | #backend2 = Aer.get_backend('qasm_simulator') 150 | 151 | #job2 = backend2.run(qc, shots=1024*500) 152 | 153 | #result2 = job2.result() 154 | 155 | #counts = result2.get_counts(qc) 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /Qcover/applications/max_cut.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import logging 4 | import time 5 | import numpy as np 6 | import networkx as nx 7 | import random 8 | import matplotlib.pyplot as plt 9 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_regular_graph 10 | 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | 15 | class MaxCut: 16 | """ 17 | max cut problem: 18 | Divide the vertex set V of the graph into two sets so that the number of edges connecting 19 | the two vertex sets is as large as possible. 20 | For the weight graph, the weight sum of the edges connecting the two sets is required to be the largest. 21 | """ 22 | def __init__(self, 23 | graph: nx.Graph = None, 24 | node_num: int = None, 25 | node_degree: int = 3, 26 | weight_range: int = 10, 27 | seed: int = None) -> None: 28 | 29 | if graph is None: 30 | # generate random graph according to node_num and node_degree and weight_range 31 | self._node_num = node_num 32 | self._graph = random_regular_graph(node_num=self._node_num, 33 | degree=node_degree, 34 | weight_range=weight_range, 35 | seed=seed) 36 | else: 37 | self._node_num = len(graph.nodes) 38 | self._graph = graph 39 | 40 | self._qmatrix = None 41 | self._shift = None 42 | 43 | @property 44 | def node_num(self): 45 | return self._node_num 46 | 47 | @property 48 | def graph(self): 49 | return self._graph 50 | 51 | def update_random_graph(self, node_num, node_degree, weight_range, seed): 52 | self._node_num = node_num 53 | self._graph = random_regular_graph(node_num=self._node_num, 54 | degree=node_degree, 55 | weight_range=weight_range, 56 | seed=seed) 57 | 58 | def get_Qmatrix(self): 59 | """ 60 | get the Q matrix in QUBO model of max cut problem 61 | Args: 62 | adjacent_mat (np.array): the adjacent matrix of graph G of the problem 63 | 64 | Returns: 65 | q_mat (np.array): the the Q matrix of QUBO model. 66 | """ 67 | adj_mat = nx.adjacency_matrix(self._graph).A 68 | qubo_mat = np.array(adj_mat, dtype='float64') 69 | # qubo_mat = adj_mat.astype('float64') 70 | for i in range(self._node_num): 71 | qubo_mat[i][i] = -(np.sum(adj_mat[i]) - adj_mat[i][i]) 72 | for j in range(self._node_num): 73 | if i == j: 74 | continue 75 | qubo_mat[i][j] = qubo_mat[i][j] 76 | 77 | shift = 0.0 78 | for i, j in self._graph.edges: 79 | try: 80 | ed_w = self._graph.adj[i][j]["weight"] 81 | except KeyError: 82 | ed_w = 1.0 83 | shift += 1.0/4.0 * ed_w 84 | 85 | for i in self._graph.nodes: 86 | try: 87 | nd_w = self._graph.nodes[i]["weight"] 88 | except KeyError: 89 | nd_w = 0 90 | shift += 1.0/2.0 * nd_w 91 | 92 | # self._shift = shift 93 | return qubo_mat, shift 94 | 95 | def max_cut_value(self, x, w): 96 | """Compute the value of a cut. 97 | 98 | Args: 99 | x (numpy.ndarray): binary string as numpy array. 100 | w (numpy.ndarray): adjacency matrix. 101 | 102 | Returns: 103 | float: value of the cut. 104 | """ 105 | X = np.outer(x, (1 - x)) 106 | return np.sum(w * X) 107 | 108 | def run(self): 109 | if self._qmatrix is None: 110 | self._qmatrix, self._shift = self.get_Qmatrix() 111 | 112 | qubo_mat = self._qmatrix 113 | ising_mat = get_ising_matrix(qubo_mat) 114 | mc_graph = get_weights_graph(ising_mat) 115 | return mc_graph, self._shift 116 | 117 | -------------------------------------------------------------------------------- /Qcover/applications/number_partition.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import os 14 | import sys 15 | import logging 16 | import time 17 | import numpy as np 18 | import networkx as nx 19 | import random 20 | import matplotlib.pyplot as plt 21 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_number_list #Qcover.applications. 22 | 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | 27 | class NumberPartition: 28 | """ 29 | number partition problem: 30 | given the set of numbers, divided it into two parties 31 | so the sum of numbers in two parties are as close as possible 32 | """ 33 | def __init__(self, 34 | number_list: np.array = None, 35 | length: int = None, 36 | weight_range: tuple = (1, 100), 37 | seed: int = None) -> None: 38 | 39 | if number_list is None: 40 | assert length is not None 41 | 42 | self._length = length 43 | self._weight_range = weight_range 44 | self._seed = seed 45 | self._number_list = random_number_list(n=self._length, 46 | weight_range=self._weight_range, 47 | seed=self._seed) 48 | else: 49 | self._number_list = number_list 50 | self._length = len(number_list) 51 | self._weight_range = (np.abs(number_list).min(), np.abs(number_list).max()) 52 | self._seed = seed 53 | 54 | self._qmatrix = None 55 | self._shift = None 56 | 57 | @property 58 | def length(self): 59 | return self._length 60 | 61 | @property 62 | def weight_range(self): 63 | return self._weight_range 64 | 65 | @property 66 | def number_list(self): 67 | return self._number_list 68 | 69 | def update_args(self, length, weight_range): 70 | self._length = length 71 | self._weight_range = weight_range 72 | 73 | self._number_list = random_number_list(n=self._length, weight_range=self._weight_range, seed=self._seed) # self.get_random_number_list() 74 | 75 | def get_Qmatrix(self): 76 | """ 77 | get the Q matrix in QUBO model of number partition problem 78 | 79 | Args: 80 | numbers (np.array): the number set that to be divided 81 | 82 | Returns: 83 | q_mat (np.array): the the Q matrix of QUBO model. 84 | """ 85 | 86 | all_sum = np.sum(self._number_list) 87 | q_mat = np.eye(self._length) 88 | 89 | for i in range(self._length): 90 | q_mat[i][i] = self._number_list[i] * (self._number_list[i] - all_sum) 91 | for j in range(self._length): 92 | if i == j: 93 | continue 94 | q_mat[i][j] = self._number_list[i] * self._number_list[j] 95 | 96 | shift = 0.0 97 | 98 | return q_mat,shift 99 | 100 | def partition_value(self, x, number_list): # 101 | """Compute the value of a partition. 102 | 103 | Args: 104 | x (numpy.ndarray): binary string as numpy array. 105 | number_list (numpy.ndarray): list of numbers in the instance. 106 | 107 | Returns: 108 | float: difference squared between the two sides of the number 109 | partition. 110 | """ 111 | diff = np.sum(number_list[x == 0]) - np.sum(number_list[x == 1]) 112 | return diff * diff 113 | 114 | def run(self): 115 | if self._qmatrix is None: 116 | self._qmatrix, self._shift = self.get_Qmatrix() 117 | 118 | qubo_mat = self._qmatrix 119 | ising_mat = get_ising_matrix(qubo_mat) 120 | np_graph = get_weights_graph(ising_mat) 121 | return np_graph, self._shift -------------------------------------------------------------------------------- /tests/fourier_test.py: -------------------------------------------------------------------------------- 1 | # import sys 2 | # sys.path.append('/public/home/humengjun/Qcover/') 3 | import time 4 | import matplotlib 5 | matplotlib.use('Agg') 6 | import matplotlib.pyplot as plt 7 | from tests.core_without_RQAOA import Qcover 8 | from Qcover.applications.max_cut import MaxCut 9 | from Qcover.optimizers import COBYLA, Fourier 10 | from Qcover.backends import CircuitByQulacs, CircuitByTensor 11 | 12 | node_num = 23 # even 13 | node_degree = [2, 4, 6, 8] 14 | plist = [4, 8, 12, 16] 15 | 16 | 17 | ts_bc = CircuitByTensor() 18 | qulacs_bc = CircuitByQulacs() 19 | 20 | for d in node_degree: 21 | ct, gt, it, ft = [], [], [], [] 22 | ce, ge, ie, fe = [], [], [], [] 23 | mxt = MaxCut(node_num=node_num, node_degree=d) 24 | ising_g = mxt.run() 25 | for p in plist: 26 | print("node degree is %s, p value is %s" %(str(d), str(p))) 27 | print("runing Fourier") 28 | optf = Fourier(p=p, q=4, r=1, alpha=0.6, optimize_method="COBYLA", options={'tol': 1e-3, 'disp':False}) 29 | qf = Qcover(ising_g, p=p, 30 | optimizer=optf, 31 | backend=CircuitByQulacs()) #qulacs_bc ts_bc 32 | 33 | time_start = time.time() 34 | res = qf.run(is_parallel=False) # True 35 | time_end = time.time() 36 | ft.append(time_end - time_start) 37 | fe.append(res["Expectation of Hamiltonian"]) 38 | nfev = res["Total iterations"] 39 | print("time cost by Fourier is: ", time_end - time_start) 40 | 41 | print("--------------------------------------------") 42 | print("runing COBYLA") 43 | optc = COBYLA(options={'maxiter': nfev, 'tol': 1e-3, 'disp': True}) 44 | qc = Qcover(ising_g, p=p, 45 | optimizer=optc, 46 | backend=CircuitByQulacs()) # ts_bc 47 | 48 | time_start = time.time() 49 | res = qc.run(is_parallel=False) # True 50 | time_end = time.time() 51 | ct.append(time_end - time_start) 52 | ce.append(res["Expectation of Hamiltonian"]) 53 | print("time cost by COBYLA is: ", time_end - time_start) 54 | 55 | # print("--------------------------------------------") 56 | # print("runing GradientDescent") 57 | # optg = GradientDescent(maxiter=nfev, tol=1e-6) 58 | # qg = Qcover(ising_g, p=p, 59 | # optimizer=optg, 60 | # backend=qulacs_bc) # ts_bc 61 | # 62 | # time_start = time.time() 63 | # res = qg.run(is_parallel=False) # True 64 | # time_end = time.time() 65 | # gt.append(time_end - time_start) 66 | # print("time cost by GradientDescent is: ", time_end - time_start) 67 | # 68 | # print("--------------------------------------------") 69 | # print("runing Intep") 70 | # opti = Interp(optimize_method="COBYLA") # 71 | # qi = Qcover(ising_g, p=p, 72 | # optimizer=opti, 73 | # backend=qulacs_bc) # ts_bc 74 | # 75 | # time_start = time.time() 76 | # res = qi.run(is_parallel=False) # True 77 | # time_end = time.time() 78 | # it.append(time_end - time_start) 79 | # print("time cost by Interp is: ", time_end - time_start) 80 | 81 | plt.figure(1) 82 | plt.subplot(121) 83 | plt.plot(plist[:len(ft)], ft, "ob-", label="Fourier degree=%s" % str(d)) 84 | plt.plot(plist[:len(ct)], ct, "^r-", label="COBYLA degree=%s" % str(d)) 85 | # plt.plot(plist[:len(gt)], gt, "*g-", label="GradientDescent %s" % str(d)) 86 | # plt.plot(plist[:len(it)], it, "xy-", label="Interp %s" % str(d)) 87 | plt.ylabel('time cost(s)') 88 | plt.xlabel('P value') 89 | plt.legend() 90 | plt.subplot(122) 91 | plt.plot(plist[:len(fe)], fe, "ob-", label="Fourier degree=%s" % str(d)) 92 | plt.plot(plist[:len(ce)], ce, "^r-", label="COBYLA degree=%s" % str(d)) 93 | plt.ylabel('expectation') 94 | plt.xlabel('P value') 95 | # plt.savefig('/home/wfzhuang/data/Qcover/result_log/backends_compare/res_serial_%s.png' % str(d)) 96 | plt.savefig('/public/home/humengjun/Qcover/result_log/fourier_node23/ptm_r1_%s.png' % str(d)) #fourier_test 97 | # plt.savefig('/home/puyanan/QCover/result_log/backends_compare/tm_serial_%s.png' % str(d)) 98 | # plt.savefig('E:/Working_projects/QAOA/QCover/result_log/backends_compare/res_serial_%s.png' % str(d)) # maxcut_serial 99 | # plt.show() 100 | plt.cla() 101 | -------------------------------------------------------------------------------- /Qcover/applications/graph_color.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import time 14 | import logging 15 | import random 16 | import numpy as np 17 | import networkx as nx 18 | import matplotlib.pyplot as plt 19 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_regular_graph 20 | 21 | 22 | logger = logging.getLogger(__name__) 23 | 24 | 25 | class GraphColoring: 26 | """ 27 | graph coloring problem: 28 | Given a graph and K colors, each vertex is required to have only one color, 29 | and the colors of the connected vertices are different 30 | """ 31 | def __init__(self, 32 | graph: nx.Graph = None, 33 | node_num: int = None, 34 | color_num: int = 4, 35 | node_degree: int = 3, 36 | weight_range: int = 1, 37 | seed: int = None, 38 | penalty: int = 99998 39 | ): 40 | 41 | self._color_num = color_num 42 | self._P = penalty 43 | if graph is None: 44 | self._node_num = node_num 45 | # self._node_degree = node_degree 46 | # self._weight_range = weight_range 47 | # self._seed = seed 48 | self._graph = random_regular_graph(node_num=self._node_num, 49 | degree=node_degree, 50 | weight_range=weight_range, 51 | seed=seed) # self.get_random_regular_graph() 52 | else: 53 | self._node_num = len(graph.nodes) 54 | self._graph = graph 55 | 56 | self._qmatrix = None 57 | self._shift = None 58 | 59 | @property 60 | def node_num(self): 61 | return self._node_num 62 | 63 | @property 64 | def color_num(self): 65 | return self._color_num 66 | 67 | @property 68 | def graph(self): 69 | return self._graph 70 | 71 | def update_random_graph(self, node_num, color_num, node_degree, weight_range, seed): 72 | self._node_num = node_num 73 | self._color_num = color_num 74 | 75 | self._graph = random_regular_graph(node_num=self._node_num, 76 | degree=node_degree, 77 | weight_range=weight_range, 78 | seed=seed) 79 | 80 | def get_Qmatrix(self): 81 | """ 82 | get the Q matrix in QUBO model of max cut problem 83 | Args: 84 | adjacent_mat (np.array): the adjacent matrix of graph G of the problem 85 | cnt (int): number of vertices in the graph of the problem 86 | k: the number of colors that can be used 87 | P: an arbitrarily large number used as constant parameter 88 | 89 | Returns: 90 | q_mat (np.array): the the Q matrix of QUBO model. 91 | """ 92 | 93 | Qmat_length = self._node_num * self._color_num 94 | qubo_mat = -self._P * np.eye(Qmat_length) 95 | adj_mat = nx.adjacency_matrix(self._graph).A 96 | for i in range(Qmat_length): 97 | for j in range(Qmat_length): 98 | x, y = i // self._color_num, j // self._color_num 99 | idl, idr = x * self._color_num, (x + 1) * self._color_num - 1 100 | if (i >= idl and i <= idr) and (j >= idl and j <= idr): 101 | if i == j: 102 | qubo_mat[i][i] = -self._P 103 | else: 104 | qubo_mat[i][j] = self._P 105 | else: 106 | if adj_mat[x][y] != 0: 107 | for l in range(self._color_num): 108 | qubo_mat[x*self._color_num + l][y*self._color_num + l] = self._P/2 109 | 110 | shift = self._P 111 | return qubo_mat, shift 112 | 113 | def run(self): 114 | if self._qmatrix is None: 115 | self._qmatrix, self._shift = self.get_Qmatrix() 116 | 117 | qubo_mat = self._qmatrix 118 | ising_mat = get_ising_matrix(qubo_mat) 119 | gc_graph = get_weights_graph(ising_mat) 120 | return gc_graph, self._shift 121 | -------------------------------------------------------------------------------- /Qcover/optimizers/Interp.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | from typing import Optional 4 | from scipy import optimize as opt 5 | from Qcover.optimizers import Optimizer 6 | from Qcover.exceptions import ArrayShapeError, OptimizerConfigError 7 | 8 | 9 | class Interp(Optimizer): 10 | """ 11 | Interp optimizer: a heuristic optimization method for QAOA, 12 | implemented according to the paper 13 | "Quantum Approximate Optimization Algorithm: Performance, Mechanism, and Implementation on Near-Term Devices" 14 | """ 15 | def __init__(self, 16 | optimize_method='COBYLA', 17 | options: dict = None, #{'maxiter':300, 'disp':True, 'rhobeg': 1.0, 'tol':1e-6}, 18 | initial_point: Optional[np.ndarray] = None): 19 | super().__init__() 20 | self._p = None 21 | self._optimize_method = optimize_method 22 | self._options = options 23 | self._initial_point = initial_point 24 | 25 | def _minimize(self, objective_function): 26 | """ 27 | minimize the loss function 28 | Args: 29 | loss: the loss function 30 | initial_point: the init parameters of gamma and beta 31 | 32 | Returns: 33 | x: the optimized gamma and beta 34 | value: the optimized value of loss function 35 | nfev: is the number of objective function calls 36 | """ 37 | nfev = 0 38 | for k in range(1, self._p + 1): 39 | if k == 1: 40 | # though only used in p=1, but to be consistent with other optimizers, 41 | # self._initial_point should be defined according to p 42 | gamma_list, beta_list = self._initial_point[: k], self._initial_point[self._p: self._p + k] 43 | gamma_list = np.insert(gamma_list, 0, 0) 44 | beta_list = np.insert(beta_list, 0, 0) 45 | else: 46 | gamma_list = np.insert(gamma_list, 0, 0) 47 | beta_list = np.insert(beta_list, 0, 0) 48 | gamma_list = np.append(gamma_list, 0) 49 | beta_list = np.append(beta_list, 0) 50 | gamma_list_new, beta_list_new = gamma_list, beta_list 51 | for i in range(1, k + 1): 52 | gamma_list_new[i] = (i - 1) / k * gamma_list[i - 1] + (k - i + 1) / k * gamma_list[i] 53 | beta_list_new[i] = (i - 1) / k * beta_list[i - 1] + (k - i + 1) / k * beta_list[i] 54 | 55 | if gamma_list_new[i] < -np.pi / 2: 56 | gamma_list_new[i] = -np.pi / 2 + 0.01 57 | if beta_list_new[i] < -np.pi / 4: 58 | beta_list_new[i] = -np.pi / 4 + 0.01 59 | if gamma_list_new[i] > np.pi / 2: 60 | gamma_list_new[i] = np.pi / 2 - 0.01 61 | if beta_list_new[i] > np.pi / 4: 62 | beta_list_new[i] = np.pi / 4 - 0.01 63 | 64 | gamma_list, beta_list = gamma_list_new, beta_list_new 65 | 66 | res = opt.minimize(objective_function, 67 | x0=np.append(gamma_list[1:k+1], beta_list[1:k+1]), 68 | args=k, 69 | method=self._optimize_method, 70 | jac=opt.rosen_der, 71 | options=self._options) 72 | 73 | gamma_list, beta_list = res["x"][:k], res["x"][k:] 74 | value = res["fun"] 75 | nfev += res["nfev"] 76 | 77 | try: 78 | optv_f, aprv_f = "optimal_value" in self._options, "approximate_ratio" in self._options 79 | if optv_f ^ aprv_f: 80 | raise OptimizerConfigError("optimal value and approximate ratio option should be set simultaneously to the optimizer") 81 | if optv_f & aprv_f: 82 | optv, aprv = self._options["optimal_value"], self._options["approximate_ratio"] 83 | if optv * aprv >= value: 84 | print("The target approximation relative to the optimal value is reached. " 85 | "The value of P is", k) 86 | break 87 | except OptimizerConfigError as e: 88 | print(e) 89 | sys.exit() 90 | 91 | return np.append(gamma_list, beta_list), value, nfev 92 | # return {"gamma": gamma_list, "beta": beta_list, "optimal value": value, "nfev": nfev} 93 | 94 | def optimize(self, objective_function): 95 | if self._initial_point is None: 96 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 97 | else: 98 | try: 99 | if len(self._initial_point) != 2 * self._p: 100 | raise ArrayShapeError("The shape of initial parameters is not match with p") 101 | except ArrayShapeError as e: 102 | print(e) 103 | sys.exit() 104 | 105 | return self._minimize(objective_function) -------------------------------------------------------------------------------- /tests/problems_tests.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | from Qcover.applications.set_partitioning import SetPartitioning 5 | from Qcover.applications.set_packing import SetPacking 6 | from Qcover.applications.general_01_programming import General01Programming 7 | from Qcover.applications.max_2_sat import Max2Sat 8 | from Qcover.applications.minimum_vertex_cover import MinimumVertexCover 9 | from Qcover.applications.qadratic_knapsack import QadraticKnapsack 10 | from Qcover.applications.quadratic_assignment import QadraticAssignment 11 | #SetPartitioning 12 | element_list = ['a','b','c','d','e','f'] 13 | element_list_len = len(element_list) 14 | element_weight = [3,2,1,1,3,2] 15 | subsets = [[1,3,6],[2,3,5,6],[3,4,5],[1,2,4,6]] 16 | penalty = 10 17 | 18 | g = SetPartitioning(element_list=element_list,length=element_list_len, weight=element_weight, element_set =subsets, P=penalty).run() 19 | nx.draw_networkx(g) # should be a completed graph 20 | plt.show() 21 | sp = SetPartitioning(element_list=element_list,length=element_list_len, weight=element_weight, element_set =subsets, P=penalty) 22 | print(sp.get_Qmatrix()) 23 | print(sp.constraints) 24 | 25 | #SetPacking 26 | # element_list = ['a','b','c','d'] 27 | # element_list_len = len(element_list) 28 | # element_weight = [10,8,10,12] 29 | # # element_weight = np.ones((element_list_len,), dtype=int) 30 | # subsets = [[1,2],[1,3,4]] 31 | # penalty = 6 32 | # 33 | # g = SetPacking(element_list=element_list,length=element_list_len, weight=element_weight, element_set=subsets, P=penalty).run() 34 | # nx.draw_networkx(g) # should be a completed graph 35 | # plt.show() 36 | # sp = SetPacking(element_list=element_list,length=element_list_len, weight=element_weight, element_set=subsets, P=penalty) 37 | # print(sp.get_Qmatrix()) 38 | # print(sp.constraints()) 39 | 40 | #General01Programming 41 | # element_list = ['a', 'b', 'c', 'd', 'e'] 42 | # element_list_len = len(element_list) 43 | # element_weight = [6, 4, 8, 5, 5] 44 | # coefficients = [[2, 2, 4, 3, 2], [1, 2, 2, 1, 2], [3, 3, 2, 4, 4]] 45 | # signs = ['<=', '=', '>='] 46 | # constants = [7, 4, 5] 47 | # penalty = 10 48 | # slack_1 = 3 49 | # 50 | # g = General01Programming(element_list=element_list, 51 | # signs=signs, 52 | # b=constants, 53 | # length=element_list_len, 54 | # weight=element_weight, 55 | # element_set=coefficients, P=penalty, slack_1=slack_1).run() 56 | # nx.draw_networkx(g) # should be a completed graph 57 | # plt.show() 58 | # 59 | # gp = General01Programming(element_list=element_list, 60 | # signs=signs, 61 | # b=constants, 62 | # length=element_list_len, 63 | # weight=element_weight, 64 | # element_set=coefficients, P=penalty, slack_1=slack_1) 65 | # print(gp.get_Qmatrix()) 66 | # print(gp._constraints) 67 | 68 | #Max2Sat 69 | # clauses_matrix = [[1,1,0,0],[1,-1,0,0],[-1,1,0,0],[-1,-1,0,0],[-1,0,1,0],[-1,0,-1,0], 70 | # [0,1,-1,0],[0,1,0,1],[0,-1,1,0],[0,-1,-1,0],[0,0,1,1],[0,0,-1,-1]] 71 | # variable_number = len(clauses_matrix[0]) 72 | # g = Max2Sat(clauses=clauses_matrix,variable_no=variable_number).run() 73 | # nx.draw_networkx(g) # should be a completed graph 74 | # plt.show() 75 | # 76 | # m2s = Max2Sat(clauses=clauses_matrix,variable_no=variable_number) 77 | # print(m2s.get_Qmatrix()) 78 | 79 | #MinimumVertexCover 80 | # adjacency_matrix = np.array([[0, 1, 1, 0, 0], 81 | # [1, 0, 0, 1, 0], 82 | # [1, 0, 0, 1, 1], 83 | # [0, 1, 1, 0, 1], 84 | # [0, 0, 1, 1, 0]]) 85 | # g = nx.from_numpy_matrix(adjacency_matrix) 86 | # penalty = 8 87 | # 88 | # mvc = MinimumVertexCover(graph=g, P=penalty) 89 | # nx.draw_networkx(mvc.graph) 90 | # plt.show() # shouldn't be a completed graph 91 | # print(mvc.get_Qmatrix()) 92 | 93 | #QadraticKnapsack 94 | # v_list = [[2,4,3,5],[4,5,1,3],[3,1,2,2],[5,3,2,4]] 95 | # length = len(v_list) 96 | # subset = [8,6,5,3] 97 | # constant = [16] 98 | # penalty = 10 99 | # 100 | # g = QadraticKnapsack(v=v_list, length=length, element_set=subset, b=constant, P=penalty).run() 101 | # nx.draw_networkx(g) # should be a completed graph 102 | # plt.show() 103 | # qk = QadraticKnapsack(v=v_list, length=length, element_set=subset, b=constant, P=penalty) 104 | # print(qk._constraints) 105 | # print(qk.get_Qmatrix()) 106 | 107 | #QadraticAssignment 108 | # flow_matrix = [[0,5,2],[5,0,3],[2,3,0]] 109 | # distance_matrix = [[0,8,15],[8,0,13],[15,13,0]] 110 | # n = len(flow_matrix) 111 | # subsets = [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9]] 112 | # penalty = 200 113 | # 114 | # g = QadraticAssignment(flow=flow_matrix, distance=distance_matrix, element_set=subsets, n=n, P=penalty).run() 115 | # nx.draw_networkx(g) # should be a completed graph 116 | # plt.show() 117 | # qa = QadraticAssignment(flow=flow_matrix, distance=distance_matrix, element_set=subsets, n=n, P=penalty) 118 | # print( qa.get_Qmatrix()) 119 | 120 | 121 | -------------------------------------------------------------------------------- /Qcover/applications/common.py: -------------------------------------------------------------------------------- 1 | """ common module """ 2 | import time 3 | import random 4 | from collections import OrderedDict, defaultdict 5 | 6 | import numpy as np 7 | import networkx as nx 8 | # from qiskit.aqua import aqua_globals 9 | # from qiskit.aqua.operators import StateFn 10 | 11 | from qiskit.utils import algorithm_globals 12 | # from qiskit.opflow import StateFn 13 | 14 | 15 | def get_ising_matrix(qubo_mat: np.array): 16 | """ 17 | calculate the ising matrix according to the Q matrix of problems. 18 | 19 | Args: 20 | q_mat: the matrix Q in QUBO model of the problem 21 | 22 | Returns: 23 | ising_mat (np.array): the the matrix of ising model 24 | """ 25 | 26 | ising_mat = np.zeros_like(qubo_mat, dtype='float') #.copy() 27 | mat_len = np.size(qubo_mat, 0) 28 | for i in range(mat_len): 29 | for j in range(mat_len): 30 | if i == j: 31 | ising_mat[i][i] = 1.0 * np.true_divide(qubo_mat[i][i], 2.0) + \ 32 | np.true_divide(np.sum(qubo_mat[i]) - qubo_mat[i][i], 2.0) 33 | else: 34 | ising_mat[i][j] = 1.0 * np.true_divide(qubo_mat[i][j], 4.0) 35 | 36 | return ising_mat 37 | 38 | 39 | def get_weights_graph(ising_mat: np.array, graph: nx.Graph=None): 40 | """ 41 | use ising matirx as adjacency matrix to generate correspondence graph model 42 | Args: 43 | ising_mat (np.array): the Ising matrix that used to generate graph 44 | 45 | Returns: 46 | graph (nx.Graph): the graph model generated by ising matrix 47 | """ 48 | 49 | cnt = np.size(ising_mat, 0) 50 | node_map = defaultdict(int) 51 | if graph is not None: 52 | node_list = list(graph.nodes) 53 | for i in range(len(node_list)): 54 | node_map[i] = node_list[i] 55 | else: 56 | for i in range(cnt): 57 | node_map[i] = i 58 | 59 | graph = nx.Graph() 60 | for i in range(cnt): 61 | for j in range(i, cnt): 62 | if i == j: 63 | graph.add_node(node_map[i], weight=ising_mat[i][i]) 64 | else: 65 | if abs(ising_mat[i][j] - 0.) <= 1e-8: 66 | continue 67 | graph.add_edge(node_map[i], node_map[j], weight=2 * ising_mat[i][j]) 68 | 69 | return graph 70 | 71 | 72 | def get_most_small_ising(state_count, ising_g): 73 | 74 | nodew = nx.get_node_attributes(ising_g, 'weight') 75 | edw = nx.get_edge_attributes(ising_g, 'weight') 76 | 77 | min_h = 999999 78 | for key, val in state_count.items(): 79 | tmp_h = 0 80 | tw = [] 81 | for i in range(len(key)): 82 | if key[i] == '0': 83 | tw.append(-1) 84 | else: 85 | tw.append(1) 86 | 87 | for nd in nodew: 88 | tmp_h += nodew[nd] * tw[nd] 89 | for ed in edw: 90 | if ed[0] == ed[1]: 91 | continue 92 | tmp_h += edw[ed] * tw[ed[0]] * tw[ed[1]] 93 | 94 | if min_h > tmp_h: 95 | min_h = tmp_h 96 | res_state = [int(key[i]) for i in range(len(key))] 97 | return res_state 98 | 99 | 100 | def random_regular_graph(node_num, degree=3, weight_range=10, negative_weight=False, seed=None): 101 | """Generate random Erdos-Renyi graph. 102 | 103 | Args: 104 | node_num (int): number of nodes. 105 | weight_range (int): weights will be smaller than this value, 106 | in absolute value. range: [1, weight_range). 107 | degree (int): the number of edge belong to every node. 108 | negative_weight (bool): allow to have edge with negative weights 109 | # savefile (str or None): name of file where to save graph. 110 | seed (int or None): random seed - if None, will not initialize. 111 | 112 | Returns: 113 | numpy.ndarray: adjacency matrix (with weights). 114 | 115 | """ 116 | assert (node_num * degree) % 2 == 0 117 | assert weight_range >= 0 118 | if seed is None: 119 | seed = np.random.randint(1, 10598) # seed = 10598 120 | # random.seed(seed) 121 | 122 | random_g = nx.random_regular_graph(d=degree, n=node_num, seed=seed) 123 | 124 | for e in random_g.edges: 125 | w = random.uniform(1, weight_range) # np.random.randint(1, weight_range) 126 | if np.random.random() >= 0.5 and negative_weight: 127 | w *= -1 128 | random_g.add_edge(e[0], e[1], weight=w) 129 | 130 | for e in random_g.nodes: 131 | w = random.uniform(1, weight_range) 132 | if np.random.random() >= 0.5 and negative_weight: 133 | w *= -1 134 | random_g.add_node(e, weight=w) 135 | random_g.add_edge(e, e, weight=w) 136 | return random_g 137 | 138 | 139 | def random_number_list(n, weight_range=(1, 100), seed=None): 140 | """Generate a set of positive integers within the given range. 141 | 142 | Args: 143 | n (int): size of the set of numbers. 144 | weight_range (tuple/list): minimum and maximum absolute value of the numbers. 145 | savefile (str or None): write numbers to this file. 146 | seed (Union(int,None)): random seed - if None, will not initialize. 147 | 148 | Returns: 149 | numpy.ndarray: the list of integer numbers. 150 | """ 151 | if seed: 152 | algorithm_globals.random_seed = seed 153 | 154 | number_list = algorithm_globals.random.integers(low=weight_range[0], high=(weight_range[1] + 1), size=n) 155 | return number_list -------------------------------------------------------------------------------- /Qcover/applications/minimum_vertex_cover.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import logging 14 | import time 15 | import numpy as np 16 | import networkx as nx 17 | import random 18 | from numpy.random import choice 19 | import matplotlib.pyplot as plt 20 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_regular_graph 21 | 22 | 23 | logger = logging.getLogger(__name__) 24 | 25 | 26 | class MinimumVertexCover: 27 | """ 28 | MVC (Minimum Vertex Cover) problem: 29 | For an undirected graph, find a vertex cover (a subset of the vertices/nodes) 30 | with a minimum number of vertices in the subset 31 | such that each edge in the graph is incident to at least one vertex in the subset. 32 | For the weight graph, the weight sum of vertices in the subset is minimum. 33 | """ 34 | def __init__(self, 35 | graph: nx.Graph = None, 36 | node_num: int = None, 37 | node_degree: int = 3, 38 | weight_range: int = 10, 39 | P: float = None, 40 | seed: int = None): 41 | """ 42 | Args: 43 | graph (nx.Graph): an networkx graph generated from input adjacency matrix 44 | node_num (int): number of nodes in the graph 45 | node_degree (int): node degree of the graph, default value is 3 46 | P (int): the penalty value for the penalty terms 47 | (require input: adjacency matrix, P) 48 | 49 | Returns: 50 | node_num, graph, P 51 | 52 | Example: 53 | adjacency_matrix = np.array([[0, 1, 1, 0, 0], 54 | [1, 0, 0, 1, 0], 55 | [1, 0, 0, 1, 1], 56 | [0, 1, 1, 0, 1], 57 | [0, 0, 1, 1, 0]]) 58 | g = nx.from_numpy_matrix(adjacency_matrix) 59 | penalty = 8 60 | """ 61 | 62 | if graph is None: 63 | # generate random graph according to node_num and node_degree and weight_range 64 | self._node_num = node_num 65 | self._graph = random_regular_graph(node_num=self._node_num, 66 | degree=node_degree, 67 | weight_range=weight_range, 68 | seed=seed) 69 | 70 | self._P = P 71 | else: 72 | self._node_num = len(graph.nodes) 73 | self._graph = graph 74 | self._P = P 75 | 76 | self._qmatrix = None 77 | self._shift = None 78 | 79 | @property 80 | def node_num(self): 81 | return self._node_num 82 | 83 | @property 84 | def graph(self): 85 | return self._graph 86 | @property 87 | def weight_range(self): 88 | return self._weight_range 89 | 90 | def update_random_graph(self, node_num, node_degree, weight_range, seed): 91 | self._node_num = node_num 92 | self._graph = random_regular_graph(node_num=self._node_num, 93 | degree=node_degree, 94 | weight_range=weight_range, 95 | seed=seed) 96 | 97 | def get_Qmatrix(self): 98 | """ 99 | get the Q matrix in QUBO model of MVC problem 100 | Args: 101 | adjacent_mat (np.array): the adjacent matrix of graph G of the problem 102 | 103 | Returns: 104 | q_mat (np.array): the the Q matrix of QUBO model. 105 | 106 | ..math:: 107 | minimise x(T)Qx 108 | Q[i][j] = P/2 109 | Q[i][i] = -weight[i] - P * node degree[i] 110 | """ 111 | adj_mat = nx.adjacency_matrix(self._graph).A 112 | qubo_mat = np.array(adj_mat, dtype='float64') 113 | 114 | for i in range(self._node_num): 115 | qubo_mat[i][i] = 1 - self._P * self._graph.degree[i] #node degree 116 | for j in range(self._node_num): 117 | if i == j: 118 | continue 119 | elif abs(adj_mat[i][j] - 0.) <= 1e-8: 120 | qubo_mat[i][j] = 0.0 121 | else: 122 | qubo_mat[i][j] = self._P / 2.0 123 | 124 | shift = self._P 125 | 126 | return qubo_mat, shift 127 | 128 | def minimum_vertex_cover_value(self, x, w): 129 | """Compute the value of a cut. 130 | 131 | Args: 132 | x (numpy.ndarray): binary string as numpy array. 133 | w (numpy.ndarray): adjacency matrix. 134 | 135 | Returns: 136 | float: value of the cut. 137 | """ 138 | if self._qmatrix is None: 139 | self._qmatrix = self.get_Qmatrix() 140 | 141 | X = np.matmul(x, np.matmul(self._qmatrix, np.transpose(x))) 142 | return X 143 | 144 | def run(self): 145 | if self._qmatrix is None: 146 | self._qmatrix, self._shift = self.get_Qmatrix() 147 | 148 | qubo_mat = self._qmatrix 149 | ising_mat = get_ising_matrix(qubo_mat) 150 | mvc_graph = get_weights_graph(ising_mat) 151 | return mvc_graph, self._shift -------------------------------------------------------------------------------- /Qcover/applications/max_2_sat.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import logging 14 | import numpy as np 15 | import networkx as nx 16 | import matplotlib.pyplot as plt 17 | import time 18 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_number_list 19 | 20 | 21 | logger = logging.getLogger(__name__) 22 | 23 | 24 | class Max2Sat: 25 | """ 26 | max-2-sat problem: 27 | each clause consists of two literals and a clause is satisfied if either or both literals are true. 28 | minimizing the number of clauses not satisfied 29 | """ 30 | def __init__(self, 31 | clauses: np.array = None, 32 | variable_no: int = None, 33 | weight_range: tuple = (1, 100), 34 | seed: int = None)-> None: 35 | """ 36 | Args: 37 | clauses (np.array): a matrix of clauses, matrix entry is 1 if the literal variable is true, 38 | 0 if the literal variable is false, -1 for the complement of the variable 39 | variable_no (int): number of variables 40 | (require input: clauses) 41 | 42 | Returns: 43 | clauses, variable_no 44 | 45 | Example: 46 | clauses_matrix = [[1,1,0,0],[1,-1,0,0],[-1,1,0,0],[-1,-1,0,0],[-1,0,1,0],[-1,0,-1,0], 47 | [0,1,-1,0],[0,1,0,1],[0,-1,1,0],[0,-1,-1,0],[0,0,1,1],[0,0,-1,-1]] 48 | variable_number = len(clauses_matrix[0]) 49 | """ 50 | 51 | if clauses is None: 52 | assert variable_no is not None 53 | 54 | self._clauses = np.array(clauses, dtype='float64') 55 | self._variable_no = variable_no 56 | self._weight_range = weight_range 57 | # self._seed = seed 58 | self._clauses = random_number_list(n=self._length, 59 | weight_range=self._weight_range, 60 | seed=seed) #self._seed 61 | else: 62 | self._clauses = np.array(clauses, dtype='float64')#clauses 63 | self._variable_no = variable_no 64 | self._weight_range = weight_range 65 | # self._seed = seed 66 | 67 | self._qmatrix = None 68 | self._shift = None 69 | 70 | def get_Qmatrix(self): 71 | """ 72 | get the Q matrix in QUBO model of number partition problem 73 | 74 | Args: 75 | numbers (np.array): the number set that to be divided 76 | 77 | Returns: 78 | q_mat (np.array): the the Q matrix of QUBO model. 79 | 80 | ..math:: 81 | if a row of self._clauses contains: 82 | literal x and literal j are both true, f(x)=1-x[i]-x[j]+x[i]x[j] 83 | literal x is true, the complement of literal j, f(x)+=x[j]-x[i]x[j] 84 | the complement of literal i, complement of literal j, f(x)+=x[i]x[j] 85 | """ 86 | 87 | set_mat = np.zeros((self._variable_no,self._variable_no)) 88 | q_mat = np.array(set_mat, dtype='float64') 89 | shift = 0.0 90 | 91 | for a in range(len(self._clauses)): 92 | indices_1 = [i for i, x in enumerate(self._clauses[a]) if x == 1.] 93 | if len(indices_1)>1: 94 | q_mat[indices_1[0]][indices_1[1]] = 0.5 95 | q_mat[indices_1[1]][indices_1[0]] = 0.5 96 | q_mat[indices_1[0]][indices_1[0]] += -1.0 97 | q_mat[indices_1[1]][indices_1[1]] += -1.0 98 | shift += 1.0 99 | 100 | indices_2 = [i for i, x in enumerate(self._clauses[a]) if x == -1.] 101 | if len(indices_2)>1: 102 | q_mat[indices_2[0]][indices_2[1]] += 0.5 103 | q_mat[indices_2[1]][indices_2[0]] += 0.5 104 | 105 | for i in range(self._variable_no): 106 | for j in range(self._variable_no): 107 | if self._clauses[a][i]==1. and self._clauses[a][j]==-1.: 108 | indices_3 = [i for i, x in enumerate(self._clauses[a]) if x == 1.] 109 | indices_4 = [i for i, x in enumerate(self._clauses[a]) if x == -1.] 110 | q_mat[indices_3[0]][indices_4[0]] += -0.5 111 | q_mat[indices_4[0]][indices_3[0]] += -0.5 112 | q_mat[indices_4[0]][indices_4[0]] += 1.0 113 | 114 | return q_mat, shift 115 | 116 | 117 | def max_2_sat_value(self, x, w): 118 | """Compute the value of a max-2-sat problem. 119 | 120 | Args: 121 | x (numpy.ndarray): binary string as numpy array. 122 | number_list (numpy.ndarray): list of numbers in the instance. 123 | 124 | Returns: 125 | float: value of the problem. 126 | """ 127 | 128 | if self._qmatrix is None: 129 | self._qmatrix = self.get_Qmatrix() 130 | 131 | X = np.matmul(x, np.matmul(self._qmatrix, np.transpose(x))) 132 | return X 133 | 134 | def run(self): 135 | if self._qmatrix is None: 136 | self._qmatrix, self._shift = self.get_Qmatrix() 137 | 138 | qubo_mat = self._qmatrix 139 | ising_mat = get_ising_matrix(qubo_mat) 140 | m2s_graph = get_weights_graph(ising_mat) 141 | return m2s_graph, self._shift 142 | -------------------------------------------------------------------------------- /Qcover/research/GHZ_Generate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import networkx as nx 3 | from typing import Optional 4 | from collections import defaultdict 5 | 6 | from Qcover.optimizers import Optimizer, COBYLA 7 | from Qcover.backends import Backend, CircuitByQiskit 8 | from Qcover.core import Qcover 9 | import warnings 10 | warnings.filterwarnings("ignore") 11 | 12 | 13 | class GHZ_Generate: 14 | 15 | def __init__(self, 16 | node_num: int, 17 | p: int = 1, 18 | graph: nx.Graph = None, 19 | optimizer: Optional[Optimizer] = COBYLA(), 20 | backend: Optional[Backend] = CircuitByQiskit(), 21 | ) -> None: 22 | self._p = p 23 | self._node_num = node_num 24 | 25 | if graph is None: 26 | self._original_graph = self.get_graph() 27 | else: 28 | if self._node_num != len(graph): 29 | print("Error: node number should be same with the one in graph to initialize") 30 | return 31 | self._original_graph = graph 32 | 33 | self._qc = Qcover(self._original_graph, 34 | self._p, 35 | optimizer=optimizer, 36 | backend=backend, 37 | research_obj="GHZ") 38 | 39 | @property 40 | def p(self): 41 | return self._p 42 | 43 | @p.setter 44 | def p(self, ap): 45 | self._p = ap 46 | 47 | @property 48 | def node_num(self): 49 | return self._node_num 50 | 51 | @node_num.setter 52 | def node_num(self, nd): 53 | self._node_num = nd 54 | 55 | @property 56 | def qc(self): 57 | return self._qc 58 | 59 | def get_graph(self): 60 | g = nx.Graph() 61 | g.add_node(0, weight=0) 62 | for i in range(1, 2 * self._node_num + 1): 63 | g.add_node(i, weight=0) 64 | g.add_edge(i - 1, i, weight=-1) 65 | return g 66 | 67 | def run(self, is_parallel=False, mode='QAQA'): 68 | if self._original_graph is None: 69 | self._original_graph = self.get_graph() 70 | 71 | # nx.draw(self._original_graph) 72 | # if self._qc is None: 73 | # self._qc = Qcover(self._original_graph, 74 | # self._p, 75 | # optimizer=self._optimizer, 76 | # backend=self._backend, 77 | # research_obj="GHZ") 78 | 79 | sol = self._qc.run(is_parallel=is_parallel, mode=mode) # True 80 | return sol 81 | 82 | 83 | if __name__ == '__main__': 84 | p = 1 85 | node_num = 2 86 | 87 | opti_params = [-0.23533129, -0.51984872, -0.63338606, -0.68489186, -0.71214422, 88 | -0.74310004, -0.75405114, -0.75529699, -0.75482341, -0.73029963, 89 | -0.78895398, -0.86082996, 0.73003181, 0.71178062, 0.72944497, 90 | 0.73066588, 0.73455175, 0.7256124 , 0.71018419, 0.68513461, 91 | 0.66249872, 0.55629759, 0.37117664, 0.11451713] 92 | 93 | from Qcover.optimizers import COBYLA, Interp, SLSQP, L_BFGS_B, GradientDescent, SPSA 94 | optc = COBYLA(options={'maxiter': 300, 'disp': True, 'rhobeg': 1.0, 'tol': 1e-12}) #, initial_point=opti_params 95 | opti = Interp(optimize_method="COBYLA", options={'tol': 1e-12, 'disp': False}) #, initial_point=opti_params, "optimal_value": -2*node_num, "approximate_ratio": 0.9 96 | opts = SLSQP(options={'maxiter': 100, 97 | 'ftol': 1e-06, 98 | 'iprint': 1, 99 | 'disp': False, 100 | 'eps': 1.4901161193847656e-08, 101 | 'finite_diff_rel_step': None}) 102 | 103 | optl = L_BFGS_B(options={'disp': None, 104 | 'maxcor': 10, 105 | 'ftol': 2.220446049250313e-09, 106 | 'gtol': 1e-05, 107 | 'eps': 1e-08, 108 | 'maxfun': 15000, 109 | 'maxiter': 15000, 110 | 'iprint': -1, 111 | 'maxls': 20, 112 | 'finite_diff_rel_step': None}) # 113 | 114 | optg = GradientDescent(options={'maxiter':300, 'learning_rate':0.05, 'tol':1e-6}) 115 | opta = SPSA(options={'A':34, 'R': 15, 'maxiter':300, 'tol':1e-6}) 116 | from Qcover.backends import CircuitByQiskit, CircuitByQton, CircuitByQulacs, CircuitByProjectq, CircuitByTensor, CircuitByCirq 117 | 118 | qiskit_bc = CircuitByQiskit(expectation_calc_method="statevector") 119 | qulacs_bc = CircuitByQulacs() 120 | 121 | # cirq_bc = CircuitByCirq() 122 | # projectq_bc = CircuitByProjectq() # Bugs need to fix 123 | # ts = CircuitByTensor() 124 | # qt = CircuitByQton() #expectation_calc_method="tensor" 125 | 126 | ghz = GHZ_Generate(node_num=node_num, 127 | p=p, 128 | optimizer=optc, 129 | backend=qiskit_bc) #qt, , cirq_bc, projectq_bc, qulacs_bc 130 | # g = ghz.get_graph() 131 | # ghz._g = g 132 | # ghz._qc = Qcover(g, p, 133 | # optimizer=optc, 134 | # backend=qiskit_bc, 135 | # research_obj="GHZ") 136 | sol = ghz.run() 137 | 138 | print("solution is:", sol) 139 | params = sol["Optimal parameter value"] 140 | # params = [2.40756694, 2.37257408, 2.34787417, 0.79040416, 0.78994118, 141 | # 0.73654257] 142 | # ghz._qc.backend._pargs = params 143 | 144 | res_exp = ghz._qc.backend.expectation_calculation() 145 | print("optimal parameter value:", ghz._qc.backend._pargs) 146 | print("the optimal expectation is: ", res_exp) 147 | out_count = ghz._qc.backend.get_result_counts(params, ghz._g) 148 | import matplotlib.pyplot as plt 149 | from qiskit.visualization import plot_histogram 150 | plot_histogram(out_count) 151 | plt.show() 152 | 153 | -------------------------------------------------------------------------------- /Qcover/research/QAOA_Generate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import networkx as nx 3 | from typing import Optional 4 | from collections import defaultdict 5 | 6 | from Qcover.utils import get_graph_weights, generate_weighted_graph, generate_graph_data 7 | from Qcover.optimizers import Optimizer, COBYLA 8 | from Qcover.backends import Backend, CircuitByQiskit 9 | from Qcover.core import Qcover 10 | import warnings 11 | warnings.filterwarnings("ignore") 12 | 13 | 14 | class QAOA_Generate: 15 | def __init__(self, 16 | graph: nx.Graph = None, 17 | p: int = 1, 18 | optimizer: Optional[Optimizer] = COBYLA(), 19 | backend: Optional[Backend] = CircuitByQiskit(), 20 | ) -> None: 21 | 22 | assert graph is not None 23 | self._p = p 24 | self._original_graph = graph 25 | self._qc = Qcover(self._original_graph, 26 | self._p, 27 | optimizer=optimizer, 28 | backend=backend, 29 | research_obj="QAOA") 30 | 31 | @property 32 | def p(self): 33 | return self._p 34 | 35 | @p.setter 36 | def p(self, ap): 37 | self._p = ap 38 | 39 | @property 40 | def qc(self): 41 | return self._qc 42 | 43 | @property 44 | def original_graph(self): 45 | return self._original_graph 46 | 47 | @original_graph.setter 48 | def original_graph(self, graph): 49 | """ 50 | according to the type of graph(nx.graph / tuple) to set the value of 51 | self._original_graph 52 | """ 53 | if isinstance(graph, nx.Graph): 54 | self._original_graph = graph 55 | elif isinstance(graph, tuple): 56 | assert (len(graph) >= 2) and (len(graph) <= 3) 57 | 58 | if len(graph) == 2: 59 | node_num, edge_num = graph 60 | wr = None 61 | elif len(graph) == 3: 62 | node_num, edge_num, wr = graph 63 | 64 | nodes, edges = generate_graph_data(node_num, edge_num, wr) 65 | self._original_graph = generate_weighted_graph(nodes, edges) 66 | elif isinstance(graph, list): 67 | assert len(graph) == 3 68 | node_list, edge_list, weight_range = graph 69 | self._original_graph = generate_weighted_graph(node_list, edge_list, weight_range) 70 | else: 71 | print("Error: the argument graph should be a instance of nx.Graph " 72 | "or a tuple formed as (node_num, edge_num)") 73 | 74 | def run(self, is_parallel=False): 75 | # nx.draw(self._g) 76 | # if self._qc is None: 77 | # self._qc = Qcover(self._g, 78 | # self._p, 79 | # optimizer=self._optimizer, 80 | # backend=self._backend, 81 | # research_obj="GHZ") 82 | res = self._qc.run(is_parallel=is_parallel) # True 83 | return res 84 | 85 | 86 | if __name__ == '__main__': 87 | p = 3 88 | g = nx.Graph() 89 | nodes = [(0, 0), (1, 0), (2, 0)] 90 | edges = [(0, 1, 1), (1, 2, 1)] 91 | 92 | for nd in nodes: 93 | u, w = nd[0], nd[1] 94 | g.add_node(int(u), weight=int(w)) 95 | for ed in edges: 96 | u, v, w = ed[0], ed[1], ed[2] 97 | g.add_edge(int(u), int(v), weight=int(w)) 98 | 99 | from Qcover.optimizers import COBYLA, Interp, SLSQP, L_BFGS_B, GradientDescent, SPSA, Fourier 100 | optf = Fourier(p=p, q=4, r=3) 101 | optc = COBYLA(options={'maxiter': 300, 'disp': True, 'rhobeg': 1.0, 'tol': 1e-12}) #, initial_point=opti_params 102 | opti = Interp(optimize_method="COBYLA", options={'tol': 1e-12, 'disp': False}) #, initial_point=opti_params, "optimal_value": -2*node_num, "approximate_ratio": 0.9 103 | opts = SLSQP(options={'maxiter': 100, 104 | 'ftol': 1e-06, 105 | 'iprint': 1, 106 | 'disp': False, 107 | 'eps': 1.4901161193847656e-08, 108 | 'finite_diff_rel_step': None}) 109 | 110 | optl = L_BFGS_B(options={'disp': None, 111 | 'maxcor': 10, 112 | 'ftol': 2.220446049250313e-09, 113 | 'gtol': 1e-05, 114 | 'eps': 1e-08, 115 | 'maxfun': 15000, 116 | 'maxiter': 15000, 117 | 'iprint': -1, 118 | 'maxls': 20, 119 | 'finite_diff_rel_step': None}) # 120 | 121 | optg = GradientDescent(options={'maxiter':300, 'learning_rate':0.05, 'tol':1e-6}) 122 | opta = SPSA(options={'A':34, 'R': 15, 'maxiter':300, 'tol':1e-6}) 123 | from Qcover.backends import CircuitByQiskit, CircuitByQton, CircuitByQulacs, CircuitByProjectq, CircuitByTensor, CircuitByCirq 124 | 125 | qiskit_bc = CircuitByQiskit(expectation_calc_method="statevector") 126 | qulacs_bc = CircuitByQulacs() 127 | 128 | # cirq_bc = CircuitByCirq() 129 | # projectq_bc = CircuitByProjectq() # Bugs need to fix 130 | # ts = CircuitByTensor() 131 | # qt = CircuitByQton() #expectation_calc_method="tensor" 132 | 133 | qaoa = QAOA_Generate(graph=g, 134 | p=p, 135 | optimizer=optc, 136 | backend=qiskit_bc) #qt, , cirq_bc, projectq_bc, qulacs_bc 137 | res = qaoa.run() 138 | 139 | print("solution is:", res) 140 | params = res["Optimal parameter value"] 141 | # params = [2.40756694, 2.37257408, 2.34787417, 0.79040416, 0.78994118, 142 | # 0.73654257] 143 | qaoa._qc.backend._pargs = params 144 | 145 | res_exp = qaoa._qc.backend.expectation_calculation() 146 | print("optimal parameter value:", qaoa._qc.backend._pargs) 147 | print("the optimal expectation is: ", res_exp) 148 | out_count = qaoa._qc.backend.get_result_counts(params) 149 | import matplotlib.pyplot as plt 150 | from qiskit.visualization import plot_histogram 151 | plot_histogram(out_count) 152 | plt.show() 153 | 154 | -------------------------------------------------------------------------------- /Qcover/backends/circuitbytensor.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import os 3 | import time 4 | import warnings 5 | from collections import defaultdict, Callable 6 | import matplotlib.pyplot as plt 7 | from multiprocessing import Pool, cpu_count 8 | import quimb as qu 9 | import quimb.tensor as qtn 10 | from multiprocessing import cpu_count 11 | from Qcover.backends import Backend 12 | from Qcover.utils import get_graph_weights 13 | 14 | 15 | class CircuitByTensor(Backend): 16 | """generate a instance of tensor network circuit generated by quimb""" 17 | 18 | def __init__(self, 19 | contract_opt: str = 'greedy', 20 | is_parallel: bool = None) -> None: 21 | 22 | """initialize a instance of tensor network circuit:""" 23 | 24 | super(CircuitByTensor, self).__init__() 25 | self._p = None 26 | self._nodes_weight = None 27 | self._edges_weight = None 28 | self._is_parallel = False if is_parallel is None else is_parallel 29 | self._opt = contract_opt 30 | self._origin_graph = None 31 | 32 | self._element_to_graph = None 33 | self._pargs = None 34 | self._expectation_path = [] 35 | self._element_expectation = dict() 36 | 37 | @property 38 | def element_expectation(self): 39 | return self._element_expectation 40 | 41 | def get_operator(self, element, qubit_num): 42 | pass 43 | 44 | def get_expectation(self, element_graph, p=None): 45 | if self._is_parallel is False: 46 | p = self._p if p is None else p 47 | original_e, graph = element_graph 48 | else: 49 | p = self._p if len(element_graph) == 1 else element_graph[1] 50 | original_e, graph = element_graph[0] 51 | 52 | node_to_qubit = defaultdict(int) 53 | node_list = list(graph.nodes) 54 | for i in range(len(node_list)): 55 | node_to_qubit[node_list[i]] = i 56 | 57 | circ = qtn.Circuit(len(graph.nodes)) 58 | gamma_list, beta_list = self._pargs[: p], self._pargs[p:] 59 | for k in range(p): 60 | for nd in graph.nodes: 61 | u = node_to_qubit[nd] 62 | if k == 0: 63 | circ.apply_gate('H', u) 64 | circ.apply_gate('rz', 2 * gamma_list[k] * self._nodes_weight[nd], u) 65 | 66 | for edge in graph.edges: 67 | u, v = node_to_qubit[edge[0]], node_to_qubit[edge[1]] 68 | if u == v: 69 | continue 70 | circ.apply_gate('RZZ', 2 * gamma_list[k] * self._edges_weight[edge[0], edge[1]], u, v) 71 | 72 | for nd in graph.nodes: 73 | circ.apply_gate('rx', 2 * beta_list[k], node_to_qubit[nd]) 74 | 75 | if isinstance(original_e, int): 76 | weight = self._nodes_weight[original_e] 77 | where = node_to_qubit[original_e] 78 | exp_res = circ.local_expectation(qu.pauli('Z'), where, optimize=self._opt) 79 | else: 80 | weight = self._edges_weight[original_e] 81 | ZZ = qu.pauli('Z') & qu.pauli('Z') 82 | where = (node_to_qubit[original_e[0]], node_to_qubit[original_e[1]]) 83 | exp_res = circ.local_expectation(ZZ, where, optimize=self._opt) 84 | return weight, exp_res.real 85 | 86 | def expectation_calculation(self, p=None): 87 | if self._nodes_weight is None or self._edges_weight is None: 88 | nodes_weight, edges_weight = get_graph_weights(self._origin_graph) 89 | self._nodes_weight, self._edges_weight = nodes_weight, edges_weight 90 | 91 | self._element_expectation = {} 92 | if self._is_parallel: 93 | return self.expectation_calculation_parallel(p) 94 | else: 95 | return self.expectation_calculation_serial(p) 96 | 97 | def expectation_calculation_serial(self, p=None): 98 | cpu_num = cpu_count() 99 | os.environ['OMP_NUM_THREADS'] = str(cpu_num) 100 | os.environ['OPENBLAS_NUM_THREADS'] = str(cpu_num) 101 | os.environ['MKL_NUM_THREADS'] = str(cpu_num) 102 | os.environ['VECLIB_MAXIMUM_THREADS'] = str(cpu_num) 103 | os.environ['NUMEXPR_NUM_THREADS'] = str(cpu_num) 104 | res = 0 105 | for item in self._element_to_graph.items(): 106 | w_i, exp_i = self.get_expectation(item, p) 107 | if isinstance(item[0], tuple): 108 | self._element_expectation[item[0]] = exp_i 109 | res += w_i * exp_i 110 | 111 | print("Total expectation of original graph is: ", res) 112 | self._expectation_path.append(res) 113 | return res 114 | 115 | def expectation_calculation_parallel(self, p=None): 116 | cpu_num = 1 117 | os.environ['OMP_NUM_THREADS'] = str(cpu_num) 118 | os.environ['OPENBLAS_NUM_THREADS'] = str(cpu_num) 119 | os.environ['MKL_NUM_THREADS'] = str(cpu_num) 120 | os.environ['VECLIB_MAXIMUM_THREADS'] = str(cpu_num) 121 | os.environ['NUMEXPR_NUM_THREADS'] = str(cpu_num) 122 | 123 | circ_res = [] 124 | args = list(itertools.product(self._element_to_graph.items(), [p])) 125 | pool = Pool(os.cpu_count()) 126 | circ_res.append(pool.map(self.get_expectation, args)) 127 | # circ_res.append(pool.map(self.get_expectation, list(self._element_to_graph.items()), chunksize=1)) 128 | 129 | pool.terminate() # pool.close() 130 | pool.join() 131 | res = 0 132 | for it in circ_res[0]: 133 | res += it[0] * it[1] 134 | 135 | # res = sum(circ_res[0]) 136 | print("Total expectation of original graph is: ", res) 137 | self._expectation_path.append(res) 138 | return res 139 | 140 | def get_result_counts(self, params): 141 | pass 142 | 143 | def sampling_visualization(self, counts): 144 | pass 145 | 146 | def optimization_visualization(self): 147 | plt.figure() 148 | plt.plot(range(1, len(self._expectation_path) + 1), self._expectation_path, "ob-", label="quimb") 149 | plt.ylabel('Expectation value') 150 | plt.xlabel('Number of iterations') 151 | plt.legend() 152 | plt.show() 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /Qcover/simulator/qton.py: -------------------------------------------------------------------------------- 1 | # 2 | # This is a quantum computer simulator. 3 | # This is a special variant of Qton. 4 | # This is for powering Qcover. 5 | # 6 | # Author(s): Yunheng Ma 7 | # Timestamp: 2022-03-30 8 | # 9 | 10 | 11 | import numpy as np 12 | from random import choices 13 | 14 | # alphabet 15 | alp = [ 16 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 17 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 18 | 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 19 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 20 | ] 21 | 22 | class Qcircuit: 23 | 24 | # num_qubits = 0 25 | # state = None 26 | def __init__(self, num_qubits=1, backend="statevector"): 27 | self.num_qubits = num_qubits 28 | self.state = np.zeros(2**num_qubits, complex) 29 | self.state[0] = 1.0 30 | self.backend = backend 31 | 32 | def apply_tensor(self, gate, num_qubits, *qubits): 33 | # based on `numpy.tensordot` and `numpy.einsum` 34 | if num_qubits > len(set(qubits)): 35 | raise Exception('Duplicate qubits in input.') 36 | global alp 37 | 38 | a_idx = [*range(num_qubits, 2 * num_qubits)] 39 | b_idx = [self.num_qubits - i - 1 for i in qubits] 40 | 41 | rep = gate.reshape([2] * 2 * num_qubits) 42 | self.state = self.state.reshape([2] * self.num_qubits) 43 | self.state = np.tensordot(rep, self.state, axes=(a_idx, b_idx)) 44 | 45 | s = ''.join(alp[:self.num_qubits]) 46 | end = s 47 | start = '' 48 | for i in range(num_qubits): 49 | start += end[self.num_qubits - qubits[i] - 1] 50 | s = s.replace(start[i], '') 51 | start = start + s 52 | self.state = np.einsum(start + '->' + end, self.state).reshape(-1) 53 | return None 54 | 55 | def _apply_1q_(self, gate, qubit): 56 | L = int(2**qubit) 57 | for i in range(0, int(2**self.num_qubits), int(2**(qubit + 1))): 58 | for j in range(i, i + L): 59 | self.state[j], \ 60 | self.state[j + L] = np.matmul(gate, [ 61 | self.state[j], 62 | self.state[j + L] 63 | ]) 64 | return None 65 | 66 | def _apply_2q_(self, gate, qubit1, qubit2): 67 | if qubit1 == qubit2: 68 | raise Exception('Cannot be same qubits.') 69 | if qubit1 > qubit2: 70 | q1, q2 = qubit1, qubit2 71 | else: 72 | q1, q2 = qubit2, qubit1 73 | L1, L2, L3 = 2**qubit2, 2**qubit1, 2**qubit2 + 2**qubit1 74 | for i in range(0, 2**self.num_qubits, 2**(q1 + 1)): 75 | for j in range(0, 2**q1, 2**(q2 + 1)): 76 | for k in range(0, 2**q2): 77 | step = i + j + k 78 | self.state[step], \ 79 | self.state[step + L1], \ 80 | self.state[step + L2], \ 81 | self.state[step + L3] = np.matmul(gate, [ 82 | self.state[step], 83 | self.state[step + L1], 84 | self.state[step + L2], 85 | self.state[step + L3] 86 | ]) 87 | return None 88 | 89 | def apply(self, gate, num_qubits, *qubits, mode): 90 | if mode == "tensor": 91 | self.apply_tensor(gate, num_qubits, qubits) 92 | else: 93 | if num_qubits == 1: 94 | self._apply_1q_(gate, qubits[0]) 95 | else: 96 | self._apply_2q_(gate, qubits[0], qubits[1]) 97 | 98 | def z(self, qubit): 99 | gate = np.array([[1, 0], [0, -1.]]) 100 | if self.mode == "tensor": 101 | self.apply_tensor(gate, 1, qubit) 102 | else: 103 | self._apply_1q_(gate, qubit) 104 | return gate 105 | 106 | def h(self, qubit): 107 | gate = np.array( 108 | [[1, 1.], 109 | [1, -1.]]) * np.sqrt(0.5) 110 | if self.backend == "tensor": 111 | self.apply_tensor(gate, 1, qubit) 112 | else: 113 | self._apply_1q_(gate, qubit) 114 | return gate 115 | 116 | def rz(self, qubit, theta): 117 | gate = np.array([ 118 | [np.exp(-1j * theta * 0.5), 0], 119 | [0, np.exp(1j * theta * 0.5)], 120 | ]) 121 | if self.backend == "tensor": 122 | self.apply_tensor(gate, 1, qubit) 123 | else: 124 | self._apply_1q_(gate, qubit) 125 | return gate 126 | 127 | def rx(self, qubit, theta): 128 | t = theta * 0.50 129 | gate = np.array([ 130 | [np.cos(t), -1j * np.sin(t)], 131 | [-1j * np.sin(t), np.cos(t)]]) 132 | if self.backend == "tensor": 133 | self.apply_tensor(gate, 1, qubit) 134 | else: 135 | self._apply_1q_(gate, qubit) 136 | return gate 137 | 138 | def rzz(self, qubit1, qubit2, theta): 139 | a, b = np.exp(-0.5j*theta), np.exp(0.5j*theta) 140 | gate = np.diag([a, b, b, a]) 141 | if self.backend == "tensor": 142 | self.apply_tensor(gate, 2, qubit1, qubit2) 143 | else: 144 | self._apply_2q_(gate, qubit1, qubit2) 145 | return gate 146 | 147 | def sample(self, shots=1024): 148 | p = self.state * self.state.conj() 149 | N = self.state.shape[0] 150 | memory = choices(range(N), weights=p, k=shots) 151 | counts = {} 152 | for i in memory: 153 | key = format(i, '0%db' % self.num_qubits) 154 | if key in counts: 155 | counts[key] += 1 156 | else: 157 | counts[key] = 1 158 | return counts 159 | 160 | 161 | 162 | class Qcodes: 163 | # codes = [] 164 | def __init__(self, num_qubits=1): 165 | self.codes = ['qc = Qcircuit(%d)'%num_qubits] 166 | return None 167 | 168 | 169 | def z(self, qubit): 170 | self.codes.append('qc.z(%d)'%qubit) 171 | return None 172 | 173 | 174 | def h(self, qubit): 175 | self.codes.append('qc.h(%d)'%qubit) 176 | return None 177 | 178 | 179 | def rx(self, theta, qubit): 180 | self.codes.append('qc.rx(%f, %d)'%(theta, qubit)) 181 | return None 182 | 183 | 184 | def rzz(self, theta, qubit1, qubit2): 185 | self.codes.append('qc.rzz(%f, %d, %d)'%(theta, qubit1, qubit2)) 186 | return None 187 | 188 | 189 | def run(self): 190 | from Qcover.simulator import Qcircuit 191 | dic = locals() 192 | for code in self.codes: 193 | exec(code, dic) 194 | return dic['qc'] 195 | -------------------------------------------------------------------------------- /Qcover/applications/quadratic_assignment.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import logging 14 | import numpy as np 15 | import random 16 | import networkx as nx 17 | import matplotlib.pyplot as plt 18 | import time, os 19 | from numbers import Number 20 | import pandas as pd 21 | from numpy.random import choice 22 | import math 23 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_number_list 24 | 25 | 26 | logger = logging.getLogger(__name__) 27 | 28 | 29 | class QadraticAssignment: 30 | """ 31 | Qadratic Assignment problem: 32 | Given n facilities and n locations along with a flow matrix (f_ij) 33 | denoting the flow of material between facilities i and j. 34 | A distance matrix (d_ij) specifies the distance between sites i and j. 35 | The optimization problem is to find an assignment of facilities to locations 36 | to minimize the weighted flow across the system. 37 | """ 38 | def __init__(self, 39 | flow: np.array = None, 40 | distance: np.array = None, 41 | n: int = None, 42 | weight_range: tuple = (1, 100), 43 | element_set: np.array = None, #constriants from subsets 44 | P: float = None, 45 | seed: int = None): 46 | """ 47 | Args: 48 | flow (np.array): flow matrix, denoting the flow of material between facilities 49 | distance (np.array): distance matrix, specifies the distance between sites 50 | n (int): number of sites = number of facilities 51 | element_set (np.array): matrix of coefficient of constraints 52 | P (int): the penalty value for the penalty terms 53 | (require input: flow, distance, element_set, P) 54 | 55 | Returns: 56 | flow, distance, n, element_set, P 57 | 58 | Example: 59 | flow_matrix = [[0,5,2],[5,0,3],[2,3,0]] 60 | distance_matrix = [[0,8,15],[8,0,13],[15,13,0]] 61 | n = len(flow_matrix) 62 | subsets = [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9]] 63 | penalty = 200 64 | """ 65 | 66 | 67 | if flow is None and element_set is None: #and distance is None 68 | assert n is not None 69 | assert len(element_set) is not None 70 | 71 | self._distance = distance 72 | self._n = n 73 | self._weight_range = weight_range 74 | self._element_set = element_set 75 | self._P = P 76 | self._seed = seed 77 | self._flow = np.array(random_number_list(n=self._n**2, 78 | weight_range=self._weight_range, 79 | seed=self._seed)).reshpae(self._n,self._n) 80 | 81 | else: 82 | self._flow = flow 83 | self._distance = distance 84 | self._n = len(flow) 85 | self._seed = seed 86 | self._element_set = element_set 87 | self._P = P 88 | 89 | self._qmatrix = None 90 | self._shift = None 91 | 92 | # def penalty(self): 93 | # """ 94 | # penalty: some percentage (75% to 150%) of the estimate of the original objective function value 95 | 96 | # Args: 97 | # x (numpy.ndarray): binary string as numpy array. 98 | 99 | # Returns: 100 | # the penalty value 101 | # """ 102 | # # a = choice([0,1], size = [1,self.length], p=[0.5, 0.5]) 103 | 104 | # # estimation = np.dot(a, self._weight).item()*1.5 105 | # # return estimation 106 | # penalty = 200 107 | # return penalty 108 | 109 | def get_Qmatrix(self): 110 | """ 111 | get the Q matrix in QUBO model of set packing problem 112 | 113 | Args: 114 | numbers (np.array): the number set that to be divided 115 | 116 | Returns: 117 | q_mat (np.array): the the Q matrix of QUBO model. 118 | 119 | ..math:: 120 | minimise x(T)Qx 121 | Q[(j-1)*n+i][(i-1)*n+j] = flow[j][i] * distance[i][j] + P 122 | Q[i][i] = -2 * P 123 | """ 124 | matrix_dimension = self._n * self._n 125 | q_mat = -2.0 * self._P * np.eye(matrix_dimension, dtype='float64') 126 | for i in range(matrix_dimension): 127 | for j in range(matrix_dimension): 128 | x, y = i // self._n, j // self._n 129 | idl, idr = x * self._n, (x + 1.0) * self._n - 1.0 130 | if (i >= idl and i <= idr) and (j >= idl and j <= idr): 131 | if i == j: 132 | q_mat[i][i] = -2.0 * self._P 133 | else: 134 | q_mat[i][j] = self._P 135 | else: 136 | for l in range(self._n): 137 | for k in range(self._n): 138 | q_mat[x*self._n+k][y*self._n+l] = self._flow[x][y]*self._distance[k][l] 139 | if self._flow[x][y]*self._distance[k][l] == 0.0: 140 | q_mat[x*self._n+k][y*self._n+l] = self._P 141 | 142 | shift = 2.0 * self._P 143 | 144 | return q_mat, shift 145 | 146 | def quadratic_assignment_value(self, x,w): 147 | """Compute the value of a quadratic assignment. 148 | 149 | Args: 150 | x (numpy.ndarray): binary string as numpy array. 151 | number_list (numpy.ndarray): list of numbers in the instance. 152 | 153 | Returns: 154 | float: value of the assignment. 155 | """ 156 | if self._qmatrix is None: 157 | self._qmatrix = self.get_Qmatrix() 158 | 159 | X = np.matmul(x, np.matmul(self._qmatrix, np.transpose(x))) 160 | return X 161 | 162 | def run(self): 163 | if self._qmatrix is None: 164 | self._qmatrix, self._shift= self.get_Qmatrix() 165 | 166 | qubo_mat = self._qmatrix 167 | ising_mat = get_ising_matrix(qubo_mat) 168 | qa_graph = get_weights_graph(ising_mat) 169 | return qa_graph, self._shift -------------------------------------------------------------------------------- /tests/GradientDescent_qiskit.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2018, 2021. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | # 13 | # Notice: This document has been modified on the basis of the original code 14 | 15 | import sys 16 | import numpy as np 17 | from typing import Iterator, Optional, Union, Callable, Dict, Any 18 | from functools import partial 19 | from Qcover.optimizers import Optimizer 20 | from Qcover.exceptions import ArrayShapeError 21 | 22 | CALLBACK = Callable[[int, np.ndarray, float, float], None] 23 | 24 | 25 | class GradientDescent: 26 | 27 | def __init__(self, 28 | # p: int = 1, 29 | maxiter: int = 100, 30 | learning_rate: Union[float, Callable[[], Iterator]] = 0.0005, 31 | tol: float = 1e-7, 32 | callback: Optional[CALLBACK] = None, 33 | perturbation: Optional[float] = None, 34 | initial_point: Optional[np.ndarray] = None) -> None: 35 | 36 | super().__init__() 37 | 38 | self._p = None 39 | self.maxiter = maxiter 40 | self.learning_rate = learning_rate 41 | self.perturbation = perturbation 42 | self.tol = tol 43 | self.callback = callback 44 | self._max_evals_grouped = 1 45 | self._initial_point = initial_point 46 | 47 | @property 48 | def settings(self) -> Dict[str, Any]: 49 | # if learning rate or perturbation are custom iterators expand them 50 | if callable(self.learning_rate): 51 | iterator = self.learning_rate() 52 | learning_rate = np.array([next(iterator) for _ in range(self.maxiter)]) 53 | else: 54 | learning_rate = self.learning_rate 55 | 56 | return { 57 | "maxiter": self.maxiter, 58 | "tol": self.tol, 59 | "learning_rate": learning_rate, 60 | "perturbation": self.perturbation, 61 | "callback": self.callback, 62 | } 63 | 64 | @staticmethod 65 | def gradient_num_diff(x_center, f, epsilon, max_evals_grouped=1): 66 | """ 67 | We compute the gradient with the numeric differentiation in the parallel way, 68 | around the point x_center. 69 | 70 | Args: 71 | x_center (ndarray): point around which we compute the gradient 72 | f (func): the function of which the gradient is to be computed. 73 | epsilon (float): the epsilon used in the numeric differentiation. 74 | max_evals_grouped (int): max evals grouped 75 | Returns: 76 | grad: the gradient computed 77 | 78 | """ 79 | forig = f(*((x_center,))) 80 | grad = [] 81 | ei = np.zeros((len(x_center),), float) 82 | todos = [] 83 | for k in range(len(x_center)): 84 | ei[k] = 1.0 85 | d = epsilon * ei 86 | todos.append(x_center + d) 87 | ei[k] = 0.0 88 | 89 | counter = 0 90 | chunk = [] 91 | chunks = [] 92 | length = len(todos) 93 | # split all points to chunks, where each chunk has batch_size points 94 | for i in range(length): 95 | x = todos[i] 96 | chunk.append(x) 97 | counter += 1 98 | # the last one does not have to reach batch_size 99 | if counter == max_evals_grouped or i == length - 1: 100 | chunks.append(chunk) 101 | chunk = [] 102 | counter = 0 103 | 104 | for chunk in chunks: # eval the chunks in order 105 | parallel_parameters = np.concatenate(chunk) 106 | todos_results = f(parallel_parameters) # eval the points in a chunk (order preserved) 107 | if isinstance(todos_results, float): 108 | grad.append((todos_results - forig) / epsilon) 109 | else: 110 | for todor in todos_results: 111 | grad.append((todor - forig) / epsilon) 112 | 113 | return np.array(grad) 114 | 115 | def _minimize(self, loss, grad): #, initial_point 116 | # set learning rate 117 | if isinstance(self.learning_rate, float): 118 | eta = constant(self.learning_rate) 119 | else: 120 | eta = self.learning_rate() 121 | 122 | if grad is None: 123 | eps = 0.01 if self.perturbation is None else self.perturbation 124 | grad = partial( 125 | GradientDescent.gradient_num_diff, 126 | f=loss, 127 | epsilon=eps, 128 | max_evals_grouped=self._max_evals_grouped, 129 | ) 130 | 131 | # prepare some initials 132 | x = np.asarray(self._initial_point) 133 | nfevs = 0 134 | 135 | for _ in range(1, self.maxiter + 1): 136 | # compute update -- gradient evaluation counts as one function evaluation 137 | update = grad(x) 138 | nfevs += 1 139 | 140 | # compute next parameter value 141 | x_next = x - next(eta) * update 142 | 143 | # send information to callback 144 | stepsize = np.linalg.norm(update) 145 | if self.callback is not None: 146 | self.callback(nfevs, x_next, loss(x_next), stepsize) 147 | 148 | # update parameters 149 | x = x_next 150 | 151 | # check termination 152 | if stepsize < self.tol: 153 | break 154 | 155 | return x, loss(x), nfevs 156 | 157 | def optimize(self, objective_function, gradient_function=None): 158 | if self._initial_point is None: 159 | self._initial_point = np.array([np.random.random() for x in range(2 * self._p)]) 160 | else: 161 | try: 162 | if len(self._initial_point) != 2 * self._p: 163 | raise ArrayShapeError("The shape of initial parameters is not match with p") 164 | except ArrayShapeError as e: 165 | print(e) 166 | sys.exit() 167 | return self._minimize(objective_function, gradient_function) 168 | 169 | 170 | def constant(eta=0.01): 171 | """Yield a constant.""" 172 | 173 | while True: 174 | yield eta 175 | -------------------------------------------------------------------------------- /tests/max_2_sat_test.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import logging 14 | import numpy as np 15 | import networkx as nx 16 | import matplotlib.pyplot as plt 17 | import time 18 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_number_list 19 | 20 | 21 | logger = logging.getLogger(__name__) 22 | 23 | 24 | class Max2Sat: 25 | """ 26 | max-2-sat problem: 27 | each clause consists of two literals and a clause is satisfied if either or both literals are true. 28 | minimizing the number of clauses not satisfied 29 | """ 30 | def __init__(self, 31 | clauses: np.array = None, 32 | variable_no: int = None, 33 | weight_range: tuple = (1, 100), 34 | seed: int = None)-> None: 35 | """ 36 | Args: 37 | clauses (np.array): a matrix of clauses, matrix entry is 1 if the literal variable is true, 38 | 0 if the literal variable is false, -1 for the complement of the variable 39 | variable_no (int): number of variables 40 | (require input: clauses) 41 | 42 | Returns: 43 | clauses, variable_no 44 | 45 | Example: 46 | clauses_matrix = [[1,1,0,0],[1,-1,0,0],[-1,1,0,0],[-1,-1,0,0],[-1,0,1,0],[-1,0,-1,0], 47 | [0,1,-1,0],[0,1,0,1],[0,-1,1,0],[0,-1,-1,0],[0,0,1,1],[0,0,-1,-1]] 48 | variable_number = len(clauses_matrix[0]) 49 | """ 50 | 51 | if clauses is None: 52 | assert variable_no is not None 53 | 54 | self._clauses = np.array(clauses, dtype='float64') 55 | self._variable_no = variable_no 56 | self._weight_range = weight_range 57 | # self._seed = seed 58 | self._clauses = random_number_list(n=self._length, 59 | weight_range=self._weight_range, 60 | seed=seed) #self._seed 61 | else: 62 | self._clauses = np.array(clauses, dtype='float64')#clauses 63 | self._variable_no = variable_no 64 | self._weight_range = weight_range 65 | # self._seed = seed 66 | 67 | self._qmatrix = None 68 | self._shift = None 69 | 70 | def get_Qmatrix(self): 71 | """ 72 | get the Q matrix in QUBO model of number partition problem 73 | 74 | Args: 75 | numbers (np.array): the number set that to be divided 76 | 77 | Returns: 78 | q_mat (np.array): the the Q matrix of QUBO model. 79 | 80 | ..math:: 81 | if a row of self._clauses contains: 82 | literal x and literal j are both true, f(x)=1-x[i]-x[j]+x[i]x[j] 83 | literal x is true, the complement of literal j, f(x)+=x[j]-x[i]x[j] 84 | the complement of literal i, complement of literal j, f(x)+=x[i]x[j] 85 | """ 86 | 87 | set_mat = np.zeros((self._variable_no,self._variable_no)) 88 | q_mat = np.array(set_mat, dtype='float64') 89 | shift = 0.0 90 | 91 | for a in range(len(self._clauses)): 92 | indices_1 = [i for i, x in enumerate(self._clauses[a]) if x == 1.] 93 | if len(indices_1)>1: 94 | q_mat[indices_1[0]][indices_1[1]] = 0.5 95 | q_mat[indices_1[1]][indices_1[0]] = 0.5 96 | q_mat[indices_1[0]][indices_1[0]] += -1.0 97 | q_mat[indices_1[1]][indices_1[1]] += -1.0 98 | shift += 1.0 99 | 100 | indices_2 = [i for i, x in enumerate(self._clauses[a]) if x == -1.] 101 | if len(indices_2)>1: 102 | q_mat[indices_2[0]][indices_2[1]] += 0.5 103 | q_mat[indices_2[1]][indices_2[0]] += 0.5 104 | 105 | for i in range(self._variable_no): 106 | for j in range(self._variable_no): 107 | if self._clauses[a][i]==1. and self._clauses[a][j]==-1.: 108 | indices_3 = [i for i, x in enumerate(self._clauses[a]) if x == 1.] 109 | indices_4 = [i for i, x in enumerate(self._clauses[a]) if x == -1.] 110 | q_mat[indices_3[0]][indices_4[0]] += -0.5 111 | q_mat[indices_4[0]][indices_3[0]] += -0.5 112 | q_mat[indices_4[0]][indices_4[0]] += 1.0 113 | 114 | qubo_mat = q_mat.copy() 115 | for i in range(self._variable_no): 116 | for j in range(self._variable_no): 117 | if i == j: 118 | continue 119 | qubo_mat[i][j] = qubo_mat[i][j] / 2.0 120 | 121 | return qubo_mat, shift 122 | 123 | 124 | def max_2_sat_value(self, x, w): 125 | """Compute the value of a max-2-sat problem. 126 | 127 | Args: 128 | x (numpy.ndarray): binary string as numpy array. 129 | number_list (numpy.ndarray): list of numbers in the instance. 130 | 131 | Returns: 132 | float: value of the problem. 133 | """ 134 | 135 | if self._qmatrix is None: 136 | self._qmatrix = self.get_Qmatrix() 137 | 138 | X = np.matmul(x, np.matmul(self._qmatrix, np.transpose(x))) 139 | return X 140 | 141 | def run(self): 142 | if self._qmatrix is None: 143 | self._qmatrix, self._shift = self.get_Qmatrix() 144 | 145 | qubo_mat = self._qmatrix 146 | ising_mat = get_ising_matrix(qubo_mat) 147 | m2s_graph = get_weights_graph(ising_mat) 148 | return m2s_graph, self._shift 149 | 150 | if __name__ == '__main__': 151 | 152 | clauses_matrix = [[1,1,0,0],[1,-1,0,0],[-1,1,0,0],[-1,-1,0,0],[-1,0,1,0],[-1,0,-1,0], 153 | [0,1,-1,0],[0,1,0,1],[0,-1,1,0],[0,-1,-1,0],[0,0,1,1],[0,0,-1,-1]] 154 | variable_number = len(clauses_matrix[0]) 155 | 156 | m2s = Max2Sat(clauses=clauses_matrix,variable_no=variable_number) 157 | # print(m2s._clauses) 158 | print(m2s.get_Qmatrix()) 159 | ising_g, shift = m2s.run() #ising_mat, 160 | # print(ising_mat) 161 | -------------------------------------------------------------------------------- /Qcover/optimizers/Fourier.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | from typing import Optional 4 | from scipy import optimize as opt 5 | from math import * 6 | from Qcover.optimizers import Optimizer 7 | from Qcover.exceptions import ArrayShapeError 8 | 9 | 10 | class Fourier(Optimizer): 11 | """ 12 | Fourier optimizer: a heuristic optimization method for QAOA, 13 | implemented according to the paper 14 | "Quantum Approximate Optimization Algorithm: Performance, Mechanism, and Implementation on Near-Term Devices" 15 | """ 16 | def __init__(self, 17 | p: int = 1, 18 | q: Optional[int] = None, # 4 19 | r: Optional[int] = 0, 20 | alpha: Optional[float] = 0.6, 21 | optimize_method: Optional[str] = 'COBYLA', 22 | options: dict = None, #{'maxiter':300, 'disp':True, 'rhobeg': 1.0, 'tol':1e-6}, 23 | initial_point: Optional[list] = None 24 | ) -> None: 25 | """ 26 | initialize a optimizer of FOURIER with parameters q and R 27 | Args: 28 | p: the parameter in QAOA paper 29 | q: the maximum frequency component allowed in the amplitude parameters <⃗u, ⃗v> 30 | r: the number of random perturbations to add 31 | alpha: 32 | """ 33 | super().__init__() 34 | self._p = p 35 | self._q = q if q is not None and (q < self._p and q >= 1) else self._p 36 | self._r = r 37 | self._alpha = alpha 38 | self._optimize_method = optimize_method 39 | self._options = options 40 | self._initial_point = initial_point # used to initialize (u, v) that shape are defined by q 41 | 42 | self._objective_function = None 43 | 44 | @property 45 | def q(self): 46 | return self._q 47 | 48 | @q.setter 49 | def q(self, aq): 50 | self._q = aq if (aq < self._p and aq >= 1) else self._p 51 | 52 | @property 53 | def r(self): 54 | return self._r 55 | 56 | @r.setter 57 | def r(self, ar): 58 | self._r = ar 59 | 60 | def calculate_gb(self, step, pargs): 61 | upb = min(self._q, step) 62 | u, v = pargs[:upb], pargs[upb:] 63 | gamma, beta = np.zeros(step), np.zeros(step) 64 | for i in range(1, step + 1): 65 | for k in range(1, upb + 1): 66 | gamma[i - 1] += u[k - 1] * sin((k - 0.5) * (i - 0.5) * pi / step) 67 | beta[i - 1] += v[k - 1] * cos((k - 0.5) * (i - 0.5) * pi / step) 68 | 69 | if gamma[i - 1] < -np.pi / 2: 70 | gamma[i - 1] = -np.pi / 2 + 0.01 71 | if beta[i - 1] < -np.pi / 4: 72 | beta[i - 1] = -np.pi / 4 + 0.01 73 | if gamma[i - 1] > np.pi / 2: 74 | gamma[i - 1] = np.pi / 2 - 0.01 75 | if beta[i - 1] > np.pi / 4: 76 | beta[i - 1] = np.pi / 4 - 0.01 77 | return gamma, beta 78 | 79 | def loss_function(self, pargs, step): 80 | gamma, beta = self.calculate_gb(step, pargs) 81 | return self._objective_function(np.append(gamma, beta), step) 82 | 83 | def _minimize(self, objective_function): 84 | """ 85 | minimize the loss function 86 | Args: 87 | loss: the loss function 88 | 89 | Returns: 90 | x: the optimized gamma and beta 91 | value: the optimized value of loss function 92 | nfev: is the number of objective function calls 93 | """ 94 | self._objective_function = objective_function 95 | 96 | nfev = 0 97 | ul, vl = None, None 98 | u_best, v_best = None, None 99 | for j in range(1, self._p + 1): 100 | u_list, v_list = [], [] 101 | min_val = float("inf") 102 | if j == 1: 103 | # Attention: 104 | # though only used in j=1, but to be consistent with other optimizers, 105 | # self._initial_point should be defined according to q, 106 | # which is different with other optimizes 107 | ul, vl = list(self._initial_point[: j]), list(self._initial_point[self._q: self._q+j]) 108 | else: 109 | if j <= self._q: 110 | ul.append(0) 111 | vl.append(0) 112 | 113 | for r in range(self._r + 1): 114 | u_nx, v_nx = u_best.copy(), v_best.copy() 115 | if r > 0: 116 | for i, _ in enumerate(u_best): 117 | u_nx[i] = u_nx[i] + self._alpha * np.random.normal(loc=0, scale=fabs(u_best[i])) 118 | v_nx[i] = v_nx[i] + self._alpha * np.random.normal(loc=0, scale=fabs(v_best[i])) 119 | if j <= self._q: 120 | u_nx.append(0) 121 | v_nx.append(0) 122 | u_list.append(u_nx) 123 | v_list.append(v_nx) 124 | 125 | for idx in range(len(u_list) + 1): 126 | if idx == 0: 127 | u_cal, v_cal = ul, vl 128 | else: 129 | u_cal, v_cal = u_list[idx - 1], v_list[idx - 1] 130 | 131 | res = opt.minimize(self.loss_function, 132 | x0=np.append(u_cal, v_cal), 133 | args=j, 134 | method=self._optimize_method, 135 | jac=opt.rosen_der, 136 | options=self._options) 137 | 138 | upb = min(self._q, j) 139 | if idx == 0: 140 | ul, vl = list(res["x"][:upb]), list(res["x"][upb:]) 141 | 142 | func_val = res["fun"] 143 | if func_val < min_val: 144 | min_val = func_val 145 | u_best, v_best = list(res["x"][:upb]), list(res["x"][upb:]) 146 | 147 | nfev += res["nfev"] 148 | 149 | gamma_list, beta_list = self.calculate_gb(self._p, u_best + v_best) 150 | return np.append(gamma_list, beta_list), min_val, nfev 151 | # return {"gamma": gamma_list, "beta": beta_list, "optimal value": min_val, "nfev": nfev} 152 | 153 | def optimize(self, objective_function): 154 | if self._initial_point is None: 155 | self._initial_point = np.array([np.random.random() for x in range(2 * self._q)]) 156 | else: 157 | try: 158 | if len(self._initial_point) != 2 * self._q: 159 | raise ArrayShapeError("The shape of initial parameters is not match with q") 160 | except ArrayShapeError as e: 161 | print(e) 162 | sys.exit() 163 | 164 | return self._minimize(objective_function) 165 | -------------------------------------------------------------------------------- /Qcover/applications/set_packing.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import logging 14 | import numpy as np 15 | import random 16 | import networkx as nx 17 | import matplotlib.pyplot as plt 18 | import time, os 19 | from numbers import Number 20 | import pandas as pd 21 | from numpy.random import choice 22 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_number_list 23 | 24 | 25 | logger = logging.getLogger(__name__) 26 | 27 | 28 | class SetPacking: 29 | """ 30 | Set Packing problem: 31 | For a finite set S, find a packing (subset of S) that miximises 32 | the total weight of the contained objects without violating any constraint. 33 | 34 | For the weighted graph, the weight sum of vertices in the subset is maximum. 35 | """ 36 | def __init__(self, 37 | element_list: list = None, 38 | length: int = None, 39 | weight_range: tuple = (1, 100), 40 | element_set: list = None, #constriants from subsets 41 | weight: list = None, 42 | P: float = None, 43 | seed: int = None)-> None: 44 | """ 45 | Args: 46 | element_list (list): a list of elements 47 | length (int): length of number_list 48 | element_set (list): a list of constraints 49 | weight (list): list of weight for element in number_list, default values are 1s 50 | P (int): the penalty value for the penalty terms 51 | (require input: element_list, element_set, weight, P) 52 | 53 | Returns: 54 | element_list, length, weight_range, weight, element_set, constraints, P 55 | 56 | Example: 57 | element_list = ['a','b','c','d'] 58 | element_list_len = len(element_list) 59 | element_weight = [10,8,10,12] 60 | subsets = [[1,2],[1,3,4]] 61 | penalty = 6 62 | """ 63 | if element_list is None and element_set is None: 64 | assert length is not None 65 | assert len(element_set) is not None 66 | 67 | self._length = length 68 | self._weight_range = weight_range 69 | self._weight = weight 70 | # self._weight = np.ones((self._length,), dtype=int) 71 | self._element_set = element_set 72 | self._constraints = self.constraints() 73 | self._P = P 74 | # self._seed = seed 75 | self._element_list = random_number_list(n=self._length, 76 | weight_range=self._weight_range, 77 | seed=seed) #self._seed 78 | 79 | else: 80 | for n in [element_list]: 81 | if isinstance(n, Number) == False: 82 | self._element_list = pd.factorize(element_list)[0] 83 | self._length = len(self._element_list) 84 | self._weight_range = (np.abs(self._element_list).min(), np.abs(self._element_list).max()) 85 | # self._seed = seed 86 | self._weight = weight 87 | # self._weight = np.ones((self._length,), dtype=int) 88 | self._element_set = element_set 89 | self._constraints = self.constraints() 90 | self._P = P 91 | 92 | self._qmatrix = None 93 | self._shift = None 94 | 95 | @property 96 | def length(self): 97 | return self._length 98 | 99 | @property 100 | def weight(self): 101 | return self._weight 102 | 103 | def constraints(self): 104 | """ 105 | If two elements in the list are included in the same constraint, 106 | the constraints matrix entry is 1, vice versa 107 | """ 108 | constraints_matrix = np.zeros((self._length,self._length), dtype='float64') 109 | for i in range(self._length): #list length 110 | for j in range(self._length): 111 | if i!=j: 112 | for a in range(len(self._element_set)): #number of constraints 113 | if i+1 in self._element_set[a] and j+1 in self._element_set[a]: 114 | constraints_matrix[i][j] = 1.0 115 | 116 | return constraints_matrix 117 | 118 | def update_args(self, length, weight,constraints): 119 | self._length = length 120 | self._weight = weight 121 | 122 | def get_Qmatrix(self): 123 | """ 124 | get the Q matrix in QUBO model of set packing problem 125 | 126 | Args: 127 | numbers (np.array): the number set that to be divided 128 | 129 | Returns: 130 | q_mat (np.array): the the Q matrix of QUBO model. 131 | 132 | ..math:: 133 | minimise x(T)Qx 134 | Q[i][j] = (P * constraints[i][j])/2 135 | Q[i][i] = -weight[i] 136 | """ 137 | set_mat = np.eye(self._length) 138 | q_mat = np.array(set_mat, dtype='float64') 139 | # q_mat = np.eye(self._length, dtype='float64') 140 | 141 | for i in range(self._length): 142 | q_mat[i][i] = -self._weight[i] 143 | for j in range(self._length): 144 | if i==j: 145 | continue 146 | elif abs(self._constraints[i][j] - 0.) <= 1e-8: 147 | q_mat[i][j] = 0.0 148 | else: 149 | q_mat[i][j] = self._P/2.0 150 | 151 | shift = 0.0 152 | 153 | return q_mat, shift 154 | 155 | def set_packing_value(self, x,w): 156 | """Compute the value of a partition. 157 | 158 | Args: 159 | x (numpy.ndarray): binary string as numpy array. 160 | number_list (numpy.ndarray): list of numbers in the instance. 161 | 162 | Returns: 163 | float: value of the packing. 164 | """ 165 | if self._qmatrix is None: 166 | self._qmatrix = self.get_Qmatrix() 167 | 168 | X = np.matmul(x, np.matmul(self._qmatrix, np.transpose(x))) 169 | return X 170 | 171 | def run(self): 172 | if self._qmatrix is None: 173 | self._qmatrix, self._shift= self.get_Qmatrix() 174 | 175 | qubo_mat = self._qmatrix 176 | ising_mat = get_ising_matrix(qubo_mat) 177 | sp_graph = get_weights_graph(ising_mat) 178 | return sp_graph, self._shift -------------------------------------------------------------------------------- /Qcover/examples/g002212.txt: -------------------------------------------------------------------------------- 1 | # NAME: wrp3-21. 2 | # COMMENT: 33d32945 STP File, STP Format Version 1.00, Section Comment, Name "wrp3-21.stp", Remark "Group Steiner Tree problem from VLSI wire routing", End 3 | 237 444 4 | 1 6 7 5 | 1 214 1 6 | 1 215 1 7 | 1 216 6 8 | 2 216 5 9 | 3 214 6 10 | 3 216 1 11 | 4 215 6 12 | 4 216 1 13 | 5 10 7 14 | 5 209 1 15 | 5 210 1 16 | 5 211 5 17 | 6 211 6 18 | 6 212 1 19 | 6 213 1 20 | 7 209 5 21 | 7 211 1 22 | 7 212 6 23 | 8 210 5 24 | 8 211 1 25 | 8 213 6 26 | 9 14 7 27 | 9 204 1 28 | 9 205 1 29 | 9 206 6 30 | 10 206 5 31 | 10 207 1 32 | 10 208 1 33 | 11 204 6 34 | 11 206 1 35 | 11 207 5 36 | 12 205 6 37 | 12 206 1 38 | 12 208 5 39 | 13 190 6 40 | 13 196 1 41 | 13 197 1 42 | 13 198 5 43 | 14 198 6 44 | 14 202 1 45 | 14 203 1 46 | 15 196 5 47 | 15 198 1 48 | 15 202 6 49 | 16 197 5 50 | 16 198 1 51 | 16 203 6 52 | 17 191 3 53 | 17 194 1 54 | 17 195 1 55 | 18 20 1 56 | 18 201 1 57 | 19 21 1 58 | 19 194 1 59 | 19 195 1 60 | 19 200 11 61 | 20 22 1 62 | 20 200 1 63 | 21 178 14 64 | 21 193 1 65 | 21 199 11 66 | 22 192 14 67 | 22 199 1 68 | 23 148 1 69 | 24 145 3 70 | 24 148 1 71 | 24 150 1 72 | 24 151 1 73 | 25 149 11 74 | 25 158 1 75 | 26 170 1 76 | 26 179 6 77 | 27 28 1 78 | 27 172 14 79 | 27 182 2 80 | 27 193 3 81 | 28 184 2 82 | 28 191 1 83 | 28 194 3 84 | 29 162 14 85 | 29 170 6 86 | 29 179 1 87 | 29 180 6 88 | 30 73 1 89 | 31 33 1 90 | 31 138 6 91 | 31 155 26 92 | 32 142 1 93 | 32 143 1 94 | 32 146 2 95 | 32 165 26 96 | 33 35 1 97 | 33 137 6 98 | 34 36 1 99 | 34 137 6 100 | 34 142 1 101 | 34 143 1 102 | 35 136 6 103 | 36 136 6 104 | 36 141 1 105 | 37 42 7 106 | 37 139 1 107 | 37 140 1 108 | 37 147 6 109 | 38 147 5 110 | 38 153 1 111 | 38 154 1 112 | 38 156 6 113 | 39 139 6 114 | 39 147 1 115 | 39 153 5 116 | 40 140 6 117 | 40 147 1 118 | 40 154 5 119 | 41 132 1 120 | 41 133 5 121 | 42 133 6 122 | 42 134 1 123 | 42 135 1 124 | 43 132 5 125 | 43 133 1 126 | 43 134 6 127 | 44 133 1 128 | 44 135 6 129 | 45 50 7 130 | 45 127 1 131 | 45 129 1 132 | 45 130 6 133 | 46 130 5 134 | 46 131 1 135 | 47 127 6 136 | 47 130 1 137 | 48 129 6 138 | 48 130 1 139 | 48 131 5 140 | 49 106 21 141 | 49 115 1 142 | 49 119 5 143 | 50 119 6 144 | 50 122 1 145 | 50 123 1 146 | 51 103 26 147 | 51 119 1 148 | 51 122 6 149 | 52 115 5 150 | 52 119 1 151 | 52 123 6 152 | 53 60 1 153 | 53 74 1 154 | 53 117 1 155 | 53 124 9 156 | 54 61 1 157 | 54 74 1 158 | 54 121 1 159 | 54 126 9 160 | 55 124 6 161 | 55 128 1 162 | 56 117 9 163 | 56 124 1 164 | 56 128 6 165 | 57 121 9 166 | 57 126 1 167 | 58 75 1 168 | 58 86 24 169 | 58 110 5 170 | 58 112 6 171 | 59 75 1 172 | 59 76 5 173 | 59 111 1 174 | 59 114 6 175 | 60 112 9 176 | 60 116 1 177 | 60 118 1 178 | 61 114 9 179 | 61 118 1 180 | 61 120 1 181 | 62 97 23 182 | 62 109 11 183 | 62 112 1 184 | 62 116 9 185 | 63 111 6 186 | 63 114 1 187 | 63 120 9 188 | 64 88 3 189 | 64 92 1 190 | 64 96 1 191 | 64 101 9 192 | 65 91 3 193 | 65 96 1 194 | 65 98 1 195 | 65 107 9 196 | 66 75 5 197 | 66 76 1 198 | 66 101 6 199 | 66 110 1 200 | 67 76 1 201 | 67 107 6 202 | 67 111 5 203 | 68 92 9 204 | 68 99 1 205 | 68 101 1 206 | 68 110 6 207 | 69 98 9 208 | 69 107 1 209 | 70 77 12 210 | 71 77 1 211 | 72 94 172 212 | 73 179 1 213 | 73 183 6 214 | 74 118 1 215 | 74 125 9 216 | 75 113 6 217 | 76 104 6 218 | 77 78 29 219 | 77 85 42 220 | 78 79 1 221 | 78 80 6 222 | 79 81 6 223 | 79 99 24 224 | 80 81 1 225 | 80 83 5 226 | 81 82 1 227 | 81 84 5 228 | 82 86 5 229 | 82 109 23 230 | 83 84 1 231 | 83 93 6 232 | 84 86 1 233 | 84 95 6 234 | 85 87 1 235 | 85 90 3 236 | 86 97 6 237 | 87 88 1 238 | 87 92 3 239 | 88 89 1 240 | 89 91 1 241 | 89 96 3 242 | 90 92 1 243 | 90 99 9 244 | 91 94 1 245 | 93 95 1 246 | 93 100 9 247 | 94 98 3 248 | 95 97 1 249 | 95 102 9 250 | 96 104 9 251 | 97 105 9 252 | 99 109 6 253 | 100 102 1 254 | 100 103 1 255 | 101 104 1 256 | 102 105 1 257 | 102 106 1 258 | 103 106 1 259 | 104 107 1 260 | 105 108 1 261 | 105 116 23 262 | 106 108 1 263 | 108 115 21 264 | 108 117 23 265 | 109 110 1 266 | 112 113 1 267 | 113 114 1 268 | 113 118 9 269 | 115 128 29 270 | 116 117 1 271 | 120 121 1 272 | 122 127 7 273 | 123 129 7 274 | 124 125 1 275 | 125 126 1 276 | 131 132 7 277 | 134 139 7 278 | 135 140 7 279 | 136 137 1 280 | 137 138 1 281 | 138 142 6 282 | 138 160 26 283 | 141 143 1 284 | 141 144 2 285 | 142 163 26 286 | 143 145 2 287 | 144 145 1 288 | 144 148 3 289 | 145 146 1 290 | 146 150 3 291 | 146 169 26 292 | 148 149 1 293 | 149 151 1 294 | 150 152 1 295 | 150 173 26 296 | 151 152 1 297 | 151 158 11 298 | 152 159 11 299 | 152 175 26 300 | 153 155 6 301 | 154 157 6 302 | 155 156 1 303 | 155 160 6 304 | 156 157 1 305 | 156 161 6 306 | 157 162 6 307 | 157 170 14 308 | 158 159 1 309 | 159 185 26 310 | 160 161 1 311 | 160 163 6 312 | 161 162 1 313 | 161 164 6 314 | 162 166 6 315 | 163 164 1 316 | 163 165 1 317 | 164 166 1 318 | 164 167 1 319 | 165 167 1 320 | 165 169 2 321 | 166 168 1 322 | 166 180 14 323 | 167 168 1 324 | 167 171 2 325 | 168 172 2 326 | 168 182 14 327 | 169 171 1 328 | 169 173 3 329 | 171 172 1 330 | 171 174 3 331 | 172 176 3 332 | 173 174 1 333 | 173 175 1 334 | 174 176 1 335 | 174 177 1 336 | 175 177 1 337 | 175 185 11 338 | 176 178 1 339 | 176 193 14 340 | 177 178 1 341 | 177 187 11 342 | 178 189 11 343 | 179 181 6 344 | 180 181 1 345 | 180 182 1 346 | 181 183 1 347 | 181 184 1 348 | 182 184 1 349 | 183 186 1 350 | 184 186 1 351 | 185 187 1 352 | 185 188 1 353 | 186 191 2 354 | 187 189 1 355 | 187 190 1 356 | 188 190 1 357 | 188 196 6 358 | 189 192 1 359 | 189 199 14 360 | 190 192 1 361 | 192 197 6 362 | 193 194 1 363 | 195 201 11 364 | 199 200 1 365 | 200 201 1 366 | 202 204 7 367 | 203 205 7 368 | 207 209 7 369 | 208 210 7 370 | 212 214 7 371 | 213 215 7 372 | 1 217 100000 373 | 2 217 100000 374 | 3 217 100000 375 | 4 217 100000 376 | 5 218 100000 377 | 6 218 100000 378 | 7 218 100000 379 | 8 218 100000 380 | 9 219 100000 381 | 10 219 100000 382 | 11 219 100000 383 | 12 219 100000 384 | 13 220 100000 385 | 14 220 100000 386 | 15 220 100000 387 | 16 220 100000 388 | 17 221 100000 389 | 18 221 100000 390 | 19 222 100000 391 | 20 222 100000 392 | 21 223 100000 393 | 22 223 100000 394 | 23 224 100000 395 | 24 224 100000 396 | 25 224 100000 397 | 26 225 100000 398 | 27 225 100000 399 | 28 225 100000 400 | 29 225 100000 401 | 30 225 100000 402 | 31 226 100000 403 | 32 226 100000 404 | 33 227 100000 405 | 34 227 100000 406 | 35 228 100000 407 | 36 228 100000 408 | 37 229 100000 409 | 38 229 100000 410 | 39 229 100000 411 | 40 229 100000 412 | 41 230 100000 413 | 42 230 100000 414 | 43 230 100000 415 | 44 230 100000 416 | 45 231 100000 417 | 46 231 100000 418 | 47 231 100000 419 | 48 231 100000 420 | 49 232 100000 421 | 50 232 100000 422 | 51 232 100000 423 | 52 232 100000 424 | 53 233 100000 425 | 54 233 100000 426 | 55 233 100000 427 | 56 233 100000 428 | 57 233 100000 429 | 58 234 100000 430 | 59 234 100000 431 | 60 234 100000 432 | 61 234 100000 433 | 62 234 100000 434 | 63 234 100000 435 | 64 235 100000 436 | 65 235 100000 437 | 66 235 100000 438 | 67 235 100000 439 | 68 235 100000 440 | 69 235 100000 441 | 70 236 100000 442 | 71 236 100000 443 | 72 237 100000 444 | 73 225 100000 445 | 74 233 100000 446 | 75 234 100000 447 | 76 235 100000 448 | -------------------------------------------------------------------------------- /Qcover/applications/set_partitioning.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qcover. 2 | # 3 | # (C) Copyright BAQIS 2021, 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import logging 14 | import numpy as np 15 | import random 16 | import networkx as nx 17 | import matplotlib.pyplot as plt 18 | import time, os 19 | from numbers import Number 20 | import pandas as pd 21 | from numpy.random import choice 22 | from Qcover.applications.common import get_ising_matrix, get_weights_graph, random_number_list 23 | 24 | 25 | logger = logging.getLogger(__name__) 26 | 27 | 28 | class SetPartitioning: 29 | """ 30 | Set Partitioning problem: 31 | For a finite set S, partitioning S into several subsets such that each element of S is in one and only one subset, 32 | find the partition that minimises the cost without violating any constraint. 33 | 34 | """ 35 | def __init__(self, 36 | element_list: list = None, 37 | length: int = None, 38 | weight_range: tuple = (1, 100), 39 | element_set: np.array = None, #constriants from subsets 40 | weight: np.array = None, 41 | P: float = None, 42 | seed: int = None): 43 | """ 44 | Args: 45 | element_list (list): a list of elements 46 | length (int): length of number_list 47 | element_set (list): a list of constraints 48 | weight (list): list of weight for element in number_list 49 | P (int): the penalty value for the penalty terms 50 | (require input: element_list, element_set, weight, P) 51 | 52 | Returns: 53 | element_list, length, weight_range, weight, element_set, constraints, P 54 | 55 | Example: 56 | element_list = ['a','b','c','d','e','f'] 57 | element_list_len = len(element_list) 58 | element_weight = [3,2,1,1,3,2] 59 | subsets = [[1,3,6],[2,3,5,6],[3,4,5],[1,2,4,6]] 60 | penalty = 10 61 | """ 62 | 63 | if element_list is None and element_set is None: 64 | assert length is not None 65 | assert len(element_set) is not None 66 | 67 | self._length = length 68 | self._weight_range = weight_range 69 | self._weight = weight 70 | self._element_set = element_set 71 | self._constraints = self.constraints() 72 | self._P = P 73 | self._seed = seed 74 | self._element_list = random_number_list(n=self._length, 75 | weight_range=self._weight_range, 76 | seed=self._seed) 77 | 78 | else: 79 | 80 | for n in [element_list]: 81 | if isinstance(n, Number) == False: 82 | self._element_list = pd.factorize(element_list)[0] 83 | self._length = len(self._element_list) 84 | self._weight_range = (np.abs(self._element_list).min(), np.abs(self._element_list).max()) 85 | self._seed = seed 86 | self._weight = weight 87 | self._element_set = element_set 88 | self._constraints = self.constraints() 89 | self._P = P 90 | 91 | self._qmatrix = None 92 | self._shift = None 93 | 94 | @property 95 | def length(self): 96 | return self._length 97 | 98 | @property 99 | def weight(self): 100 | return self._weight 101 | 102 | def constraints(self): 103 | """ 104 | If the element in the list is included in the subset, 105 | the constraints matrix entry is 1, vice versa 106 | """ 107 | constraints_mat = np.zeros((self._length,self._length), dtype='float64') 108 | for i in range(self._length): #list length 109 | for j in range(self._length): 110 | for a in range(len(self._element_set)): #number of constraints 111 | if i+1 in self._element_set[a] and j+1 in self._element_set[a]: 112 | constraints_mat[a][i] = 1.0 113 | return constraints_mat #binary matrix 114 | 115 | def update_args(self, length, weight,constraints): 116 | self._length = length 117 | self._weight = weight 118 | self._constraints = constraints 119 | 120 | def get_Qmatrix(self): 121 | """ 122 | get the Q matrix in QUBO model of number partition problem 123 | 124 | Args: 125 | numbers (np.array): the number set that to be divided 126 | 127 | Returns: 128 | q_mat (np.array): the the Q matrix of QUBO model. 129 | 130 | ..math:: 131 | minimise x(T)Qx 132 | Q[i][j] = P * number of times elements i and j appear in the same constraint 133 | Q[i][i] = -weight[i] - P * number of ones in the ith column of the constraint matrix 134 | """ 135 | 136 | q_mat = np.eye(self._length, dtype='float64') 137 | 138 | for i in range(self._length): 139 | q_mat[i][i] = self._weight[i] - self._P * np.sum(self._constraints, axis = 0)[i] 140 | for j in range(i): 141 | r = 0.0 142 | for a in range(len(self._element_set)): #number of constraints 143 | if i+1 in self._element_set[a] and j+1 in self._element_set[a]: 144 | r += 1.0 #number of times elements i and j appear in the same constraint 145 | q_mat[i][j] = self._P * r 146 | q_mat[j][i] = q_mat[i][j] 147 | 148 | shift = self._P 149 | 150 | return q_mat, shift 151 | 152 | def set_partitioning_value(self, x,w): 153 | """Compute the value of a packing. 154 | 155 | Args: 156 | x (numpy.ndarray): binary string as numpy array. 157 | weight (numpy.ndarray): weights of elements 158 | 159 | Returns: 160 | float: value of the partitioning. 161 | """ 162 | if self._qmatrix is None: 163 | self._qmatrix = self.get_Qmatrix() 164 | 165 | X = np.matmul(x, np.matmul(self._qmatrix, np.transpose(x)) ) 166 | return X 167 | 168 | def run(self): 169 | if self._qmatrix is None: 170 | self._qmatrix, self._shift = self.get_Qmatrix() 171 | 172 | qubo_mat = self._qmatrix 173 | ising_mat = get_ising_matrix(qubo_mat) 174 | sp_graph = get_weights_graph(ising_mat) 175 | return sp_graph, self._shift --------------------------------------------------------------------------------