From 11533dcc46b2093861c8a4cc1e61b54db449b19a Mon Sep 17 00:00:00 2001 From: Rene Zander Date: Wed, 16 Oct 2024 21:21:58 +0200 Subject: [PATCH 1/8] Update QIROProblem documentation --- src/qrisp/algorithms/qiro/qiro_problem.py | 132 ++++++++++------------ 1 file changed, 59 insertions(+), 73 deletions(-) diff --git a/src/qrisp/algorithms/qiro/qiro_problem.py b/src/qrisp/algorithms/qiro/qiro_problem.py index 595230a0..059ac78e 100644 --- a/src/qrisp/algorithms/qiro/qiro_problem.py +++ b/src/qrisp/algorithms/qiro/qiro_problem.py @@ -25,110 +25,95 @@ class QIROProblem(QAOAProblem): r""" Structure to run QIRO algorithms. The idea is based on the paper by J. Finzgar et al. (https://arxiv.org/pdf/2308.13607.pdf). - This class encapsulates the cost operator, mixer operator, and classical cost function for a specific QIRO problem instance. It also provides methods to set the initial state preparation function, classical cost post-processing function, and optimizer for the problem. + This class encapsulates the replacement routine, cost operator, mixer operator, classical cost function + and initial state preparation function for a specific QIRO problem instance. - For a quick demonstration, we import the relevant functions from already implemented problem instances: + For a quick demonstration, we compare QAOA and QIRO for solving a MaxClique problem instance: :: - # imports - from qrisp.qaoa.qiro_problem import QIROProblem - from qrisp.qaoa.qaoa_problem import QAOAProblem - from qrisp.qaoa.problems.create_rdm_graph import create_rdm_graph - from qrisp.qaoa.problems.maxCliqueInfrastr import maxCliqueCostfct, maxCliqueCostOp - from qrisp.qaoa.qiroproblems.qiroMaxCliqueInfrastr import * - from qrisp.qaoa.mixers import RX_mixer - from qrisp.qaoa.qiro_mixers import qiro_init_function, qiro_RXMixer from qrisp import QuantumVariable + from qrisp.qiro import QIROProblem, create_maxClique_replacement_routine, create_maxClique_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qiro.qiro_problem import QIROProblem + from qrisp.qaoa import max_clique_problem, create_max_clique_cl_cost_function import matplotlib.pyplot as plt import networkx as nx - - - # First we define a graph via the number of nodes and the QuantumVariable arguments + # Define a random graph via the number of nodes and the QuantumVariable arguments num_nodes = 15 - G = create_rdm_graph(num_nodes,0.7, seed = 99) - Gtwo = create_rdm_graph(num_nodes,0.7, seed = 99) + G = nx.erdos_renyi_graph(num_nodes, 0.7, seed = 99) + Gtwo = G.copy() + qarg = QuantumVariable(G.number_of_nodes()) qarg2 = QuantumVariable(Gtwo.number_of_nodes()) - # set simulator shots - mes_kwargs = { - #below should be 5k - "shots" : 5000 - } - - #assign cost_function and maxclique_instance, normal QAOA - testCostFun = maxCliqueCostfct(Gtwo) - maxclique_instance = QAOAProblem(maxCliqueCostOp(G), RX_mixer, maxCliqueCostfct(G)) + # QAOA + qaoa_instance = max_clique_problem(G) + res_qaoa = qaoa_instance.run(qarg=qarg, depth=3) - # assign the correct new update functions for qiro from above imports + # QIRO qiro_instance = QIROProblem(problem = Gtwo, replacement_routine = create_maxClique_replacement_routine, cost_operator = create_maxClique_cost_operator_reduced, mixer = qiro_RXMixer, - cl_cost_function = maxCliqueCostfct, + cl_cost_function = create_max_clique_cl_cost_function, init_function = qiro_init_function ) + res_qiro = qiro_instance.run_qiro(qarg=qarg2, depth=3, n_recursions = 2) + # The final graph that has been adjusted + final_graph = qiro_instance.problem - # We run the qiro instance and get the results! - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2, - #mes_kwargs = mes_kwargs - ) - # and also the final graph, that has been adjusted - final_Graph = qiro_instance.problem + cl_cost = create_max_clique_cl_cost_function(G) - # get the normal QAOA results for a comparison - res_qaoa = maxclique_instance.run( qarg = qarg2, depth = 3) + print("5 most likely QAOA solutions") + max_five_qaoa = sorted(res_qaoa, key=res_qaoa.get, reverse=True)[:5] + for res in max_five_qaoa: + print([index for index, value in enumerate(res) if value == '1']) + print(cl_cost({res : 1})) + print("5 most likely QIRO solutions") + max_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] + for res in max_five_qiro: + print([index for index, value in enumerate(res) if value == '1']) + print(cl_cost({res : 1})) - # We can then also print the top 5 results for each... - print("QAOA 5 best results") - maxfive = sorted(res_qaoa, key=res_qaoa.get, reverse=True)[:5] - for key, val in res_qaoa.items(): - if key in maxfive: - print(key) - print(testCostFun({key:1})) - - - print("QIRO 5 best results") - maxfive = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] - for key, val in res_qiro.items(): - if key in maxfive: - - print(key) - print(testCostFun({key:1})) - - # or compare it with the networkx result of the max_clique algorithm... print("Networkx solution") - print(nx.approximation.max_clique(Gtwo)) + print(nx.approximation.max_clique(G)) - # and finally, we draw the final graph and the original graphs to compare them! + # Draw the final graph and the original graph for comparison plt.figure(1) - nx.draw(final_Graph, with_labels = True) + nx.draw(final_graph, with_labels = True, node_color='#ADD8E6', edge_color='#D3D3D3') + plt.title('final QIRO graph') plt.figure(2) - nx.draw(G, with_labels = True) + nx.draw(G, with_labels = True, node_color='#ADD8E6', edge_color='#D3D3D3') + plt.title('Original graph') plt.show() - For an in-depth tutorial, make sure to check out :ref:` the QIRO tutorial`! + For an in-depth tutorial, make sure to check out :ref:`the QIRO tutorial `! Parameters ---------- problem : Any - The problem structure to be considered for the algorithm. For example in the case of MaxClique a graph, or MaxSat a list of clauses - qaoaProblem : QAOAProblem - A :ref:`QAOAProblem` instance to be used for establishing the correlations. + The problem structure to be considered for the algorithm. For example, in the case of MaxClique a graph, or MaxSat a list of clauses. replacement_routine : function - A routine for adjusting the problem after the highest correlation value has been found. - qiro_cost_operator : function - New ``cost_operator`` for the :ref:`QAOAProblem` instance. - qiro_mixer_operator : function - New ``mixer_operator`` for the :ref:`QAOAProblem` instance. - qiro_init_function : function - New ``mixer_operator`` for the :ref:`QAOAProblem` instance. + A routine for adjusting the problem after the highest correlation value was found. + cost_operator : function + Prepares the new ``cost_operator`` for the updated :ref:`QAOAProblem` instance. + A function that receives a ``problem`` and a list of ``solutions``, and returns a function + that is applied to a :ref:`QuantumVariable` and a real parameter $\gamma$. + mixer : function + Prepares the new ``mixer`` for the updated :ref:`QAOAProblem` instance. + A function that receives a list of ``solutions`` and a list of ``exclusions``, and returns a function + that is applied to a :ref:`QuantumVariable` and a real parameter $\beta$. + cl_cost_function : function + The classical cost function for the problem instance, which takes a dictionary of measurement results as input. + init_function : function + Prepares the new ``init_function`` for the updated :ref:`QAOAProblem` instance. + A function that receives a list of ``solutions`` and a list of ``exclusions``, and returns a function + that is applied to a :ref:`QuantumVariable`. """ @@ -152,15 +137,15 @@ def __init__(self, problem, replacement_routine , cost_operator, mixer, cl_cost_ def run_qiro(self, qarg, depth, n_recursions, mes_kwargs = {}, max_iter = 50): """ - Run the specific QIRO problem instance with given quantum arguments, depth of QAOA circuit, + Run the specific QIRO problem instance with given quantum argument, depth of QAOA circuit, number of recursions, measurement keyword arguments (mes_kwargs) and maximum iterations for optimization (max_iter). Parameters ---------- - qarg : QuantumVariable + qarg : :ref:`QuantumVariable` The quantum variable to which the QAOA circuit is applied. depth : int - The depth of the QAOA circuit. + The amount of QAOA layers. n_recursions : int The number of QIRO replacement iterations. mes_kwargs : dict, optional @@ -172,10 +157,11 @@ def run_qiro(self, qarg, depth, n_recursions, mes_kwargs = {}, max_iter = 50): ------- opt_res : dict The optimal result after running QAOA problem for a specific problem instance. It contains the measurement results after applying the optimal QAOA circuit to the quantum variable. + """ from qrisp import QuantumVariable - res= QAOAProblem.run(self, qarg, depth, mes_kwargs, max_iter) + res= QAOAProblem.run(self, qarg, depth, mes_kwargs, max_iter) corr_vals = [] solutions = [] @@ -183,7 +169,7 @@ def run_qiro(self, qarg, depth, n_recursions, mes_kwargs = {}, max_iter = 50): for index in range(n_recursions): - new_problem, solutions , sign, exclusions = self.replacement_routine(res, self.problem, solutions , exclusions) + new_problem, solutions , sign, exclusions = self.replacement_routine(res, self.problem, solutions, exclusions) corr_vals.append(sign) self.problem = new_problem @@ -193,7 +179,7 @@ def run_qiro(self, qarg, depth, n_recursions, mes_kwargs = {}, max_iter = 50): solutions=solutions, exclusions = exclusions) new_qarg = QuantumVariable(len(qarg)) - res = QAOAProblem.run(self, new_qarg, depth, mes_kwargs, max_iter) + res = QAOAProblem.run(self, new_qarg, depth, mes_kwargs, max_iter) return res From b139c15c60d3c084eac197214eb1748d543cf657 Mon Sep 17 00:00:00 2001 From: Rene Zander Date: Wed, 16 Oct 2024 22:59:17 +0200 Subject: [PATCH 2/8] Update QIRO implementations MaxClique, MaxIndepSet --- .../QIROImplementations/QIROMaxClique.rst | 80 ++++---- .../QIROImplementations/QIROMaxIndep.rst | 85 ++++----- src/qrisp/algorithms/qiro/qiro_problem.py | 3 +- .../algorithms/qiro/qiroproblems/__init__.py | 4 +- ...oMaxCliqueInfrastr.py => qiroMaxClique.py} | 94 +++++----- .../qiro/qiroproblems/qiroMaxIndepSet.py | 174 ++++++++++++++++++ .../qiroproblems/qiroMaxIndepSetInfrastr.py | 160 ---------------- 7 files changed, 303 insertions(+), 297 deletions(-) rename src/qrisp/algorithms/qiro/qiroproblems/{qiroMaxCliqueInfrastr.py => qiroMaxClique.py} (53%) create mode 100644 src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py delete mode 100644 src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSetInfrastr.py diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst index 8153e1a6..d65af6b7 100644 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst +++ b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst @@ -3,7 +3,7 @@ QIRO MaxClique ============== -.. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxCliqueInfrastr +.. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxClique Problem description @@ -15,17 +15,16 @@ Given a Graph :math:`G = (V,E)` maximize the size of a clique, i.e. a subset :m Replacement routine ------------------- -In this instance the replacements can be rather drastic. In-depth investigation may show relaxations to be better performant on larger instances. -Based on the **maximum absolute entry** of the correlation matrix M and its sign, one of the following replacements is employed: +In this instance the replacements can be rather significant. In-depth investigation may show relaxations to be perform better on larger instances. +Based on the **maximum absolute entry** of the correlation matrix $M$ and its sign, one of the following replacements is employed: -* If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th vertex is set to be in the clique set. In turn, we can remove all vertices that **do not share an edge with this vertex can be removed from the graph**, since including them in the solution would violate the problem constraints. +* If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th vertex is set to be in the clique set. In turn, all vertices that **do not share an edge with this vertex can be removed from the graph**, since including them in the solution would violate the problem constraints. -* If :math:`\text{M}_{ii} < 0` is the maximum absolute value we remove :math:`i`-th vertex from the graph +* If :math:`\text{M}_{ii} < 0` is the maximum absolute value, we remove :math:`i`-th vertex from the graph. -* If :math:`\text{M}_{ij} > 0, (i, j) ∈ E` was selected, we keep both nodes as part of the solution and remove all non-bordering nodes. The likelyhood of this behaviour failing is low, but remains to be investigated. - -* If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we remove all nodes that **do not** share an edge with either vertice :math:`i` or :math:`j`. Since one of the vertices :math:`i` and :math:`j` will be part of the final solution (but not both), any vertex that is **not** connected to either :math:`i` or :math:`j` (or both) is guaranteed to violate the problem constraints, and can be removed from the graph. +* If :math:`\text{M}_{ij} > 0, (i, j) ∈ E` was selected, we keep both vertices as part of the solution and remove all non-neighboring vertices. The likelihood of this rule failing is low, but remains to be investigated. +* If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we remove all vertices that **do not** share an edge with either vertex :math:`i` or :math:`j`. Since one of the vertices :math:`i` and :math:`j` will be part of the final solution (but not both), any vertex that is **not** connected to either :math:`i` or :math:`j` (or both) is guaranteed to violate the problem constraints, and can be removed from the graph. .. autofunction:: create_maxClique_replacement_routine @@ -37,58 +36,55 @@ QIRO Cost operator .. autofunction:: create_maxClique_cost_operator_reduced -Full Example implementation: ----------------------------- -:: +Example implementation +---------------------- + +:: - # imports from qrisp import QuantumVariable + from qrisp.qiro import QIROProblem, create_maxClique_replacement_routine, create_maxClique_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qaoa import max_clique_problem, create_max_clique_cl_cost_function import matplotlib.pyplot as plt import networkx as nx - from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxClique_cost_operator_reduced, create_maxClique_replacement_routine - from qrisp.qaoa import QAOAProblem, RX_mixer, maxCliqueCostfct, maxCliqueCostOp - - - # First we define a graph via the number of nodes and the QuantumVariable arguments + # Define a random graph via the number of nodes and the QuantumVariable arguments num_nodes = 15 - G = nx.erdos_renyi_graph(num_nodes,0.7, seed = 99) - Gtwo = nx.erdos_renyi_graph(num_nodes,0.7, seed = 99) + G = nx.erdos_renyi_graph(num_nodes, 0.7, seed=99) qarg = QuantumVariable(G.number_of_nodes()) - qarg2 = QuantumVariable(Gtwo.number_of_nodes()) - #assign cost_function and maxclique_instance, normal QAOA - testCostFun = maxCliqueCostfct(Gtwo) - maxclique_instance = QAOAProblem(maxCliqueCostOp(G), RX_mixer, maxCliqueCostfct(G)) + cl_cost = create_max_clique_cl_cost_function(G) - # assign the correct new update functions for qiro from above imports - qiro_instance = QIROProblem(problem = Gtwo, - replacement_routine = create_maxClique_replacement_routine, + # QIRO + qiro_instance = QIROProblem(problem = G, + replacement_routine = create_maxClique_replacement_routine, cost_operator = create_maxClique_cost_operator_reduced, mixer = qiro_RXMixer, - cl_cost_function = maxCliqueCostfct, + cl_cost_function = create_max_clique_cl_cost_function, init_function = qiro_init_function ) + res_qiro = qiro_instance.run_qiro(qarg=qarg, depth=3, n_recursions=2) + # The final graph that has been adjusted + final_graph = qiro_instance.problem - # We run the qiro instance and get the results! - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2, - #mes_kwargs = mes_kwargs - ) - # and also the final graph, that has been adjusted - final_Graph = qiro_instance.problem - - # get the normal QAOA results for a comparison - #res_qaoa = maxclique_instance.run( qarg = qarg2, depth = 3) + print("5 most likely QIRO solutions") + ax_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] + for res in max_five_qiro: + print([index for index, value in enumerate(res) if value == '1']) + print(cl_cost({res : 1})) - # or compare it with the networkx result of the max_clique algorithm... print("Networkx solution") - print(nx.approximation.max_clique(Gtwo)) + print(nx.approximation.max_clique(G)) - # and finally, we draw the final graph and the original graphs to compare them! + # Draw the final graph and the original graph for comparison plt.figure(1) - nx.draw(final_Graph, with_labels = True) + nx.draw(final_graph, with_labels = True, node_color='#ADD8E6', edge_color='#D3D3D3') + plt.title('Final QIRO graph') plt.figure(2) - nx.draw(G, with_labels = True) - plt.show() + most_likely = [index for index, value in enumerate(max_five_qiro[0]) if value == '1'] + nx.draw(G, with_labels = True, + node_color=['#FFCCCB' if node in most_likely else '#ADD8E6' for node in G.nodes()], + edge_color=['#FFCCCB' if edge[0] in most_likely and edge[1] in most_likely else '#D3D3D3' for edge in G.edges()]) + plt.title('Original graph with most likely QIRO solution') + plt.show() \ No newline at end of file diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst index b0c16969..cb72bdee 100644 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst +++ b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst @@ -1,32 +1,30 @@ .. _maxIndependentSetQIRO: -QIRO MaxIndependentSet -====================== +QIRO MaxIndepSet +================ -.. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxIndepSetInfrastr +.. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxIndepSet Problem description ------------------- -Given a Graph :math:`G = (V,E)` maximize the size of a clique, i.e. a subset :math:`V' \subset V` in which all pairs of vertices are mutually non-adjacent. - - +Given a Graph :math:`G = (V,E)` maximize the size of an independent set, i.e. a subset :math:`V' \subset V` in which all pairs of vertices are mutually non-adjacent. Replacement routine ------------------- -Based on the **maximum absolute entry** of the correlation matrix M and its sign, one of the following replacements is employed: +Based on the **maximum absolute entry** of the correlation matrix $M$ and its sign, one of the following replacements is employed: -* If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th vertex is set to be in the independent set (IS). In turn, we can remove all vertices that share an edge with this vertex can be removed from the graph, since including them in the solution would violate the problem constraints +* If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th vertex is set to be in the independent set (IS). In turn, all vertices that share an edge with this vertex can be removed from the graph, since including them in the solution would violate the problem constraints. -* If :math:`\text{M}_{ii} < 0` is the maximum absolute value we remove :math:`i`-th vertex from the graph +* If :math:`\text{M}_{ii} < 0` is the maximum absolute value, we remove :math:`i`-th vertex from the graph. -* If :math:`\text{M}_{ij} > 0, (i, j) ∈ E` was selected, we remove both nodes from the graph with the argument, that, since both of them would be in the same state in the final solution, including both as part of the solution would violate the constraint, as they share an edge. In turn, they can be removed from the graph. +* If :math:`\text{M}_{ij} > 0, (i, j) ∈ E` was selected, we remove both vertices from the graph with the argument, that, since both of them would be in the same state in the final solution, including both as part of the solution would violate the constraint, as they share an edge. In turn, they can be removed from the graph. -* If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we remove all nodes that share an edge with both vertices :math:`i` and :math:`j`. Since one of the vertices :math:`i` and :math:`j` will be part of the final solution (but not both), any vertex that is connected to both :math:`i` and :math:`j` is guaranteed to violate the problem constraints, and can be removed from the graph. In this case it may be possible, that no vertex is found to be as a canditate for removing. We will then simple chose second biggest absolute value of **M** for the replacement routine. +* If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we remove all vertices that share an edge with both vertices :math:`i` and :math:`j`. Since one of the vertices :math:`i` and :math:`j` will be part of the final solution (but not both), any vertex that is connected to both :math:`i` and :math:`j` is guaranteed to violate the problem constraints, and can be removed from the graph. In this case, it may be possible that no vertex is found to be a canditate for being removed. We will then simply choose the second biggest absolute value of **M** for the replacement routine. .. autofunction:: create_maxIndep_replacement_routine @@ -38,49 +36,54 @@ QIRO Cost operator .. autofunction:: create_maxIndep_cost_operator_reduced -Full Example implementation: ----------------------------- +Example implementation +---------------------- :: - # imports - from qrisp.qaoa.problems.maxIndepSetInfrastr import maxIndepSetclCostfct, maxIndepSetCostOp - from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced from qrisp import QuantumVariable + from qrisp.qiro import QIROProblem, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qaoa import create_max_indep_set_cl_cost_function + import matplotlib.pyplot as plt import networkx as nx - - - # First we define a graph via the number of nodes and the QuantumVariable arguments + # Define a random graph via the number of nodes and the QuantumVariable arguments num_nodes = 13 - G = nx.erdos_renyi_graph(num_nodes, 0.4, seed = 107) + G = nx.erdos_renyi_graph(num_nodes, 0.4, seed = 107) qarg = QuantumVariable(G.number_of_nodes()) + cl_cost = create_max_indep_set_cl_cost_function(G) - # assign the correct new update functions for qiro from above imports - qiro_instance = QIROProblem(G, - replacement_routine=create_maxIndep_replacement_routine, + # QIRO + qiro_instance = QIROProblem(G, + replacement_routine=create_maxIndep_replacement_routine, cost_operator= create_maxIndep_cost_operator_reduced, mixer= qiro_RXMixer, - cl_cost_function= maxIndepSetclCostfct, + cl_cost_function= create_max_indep_set_cl_cost_function, init_function= qiro_init_function ) + res_qiro = qiro_instance.run_qiro(qarg=qarg, depth=3, n_recursions=2) + + # The final graph that has been adjusted + final_graph = qiro_instance.problem + + print("5 most likely QIRO solutions") + max_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] + for res in max_five_qiro: + print([index for index, value in enumerate(res) if value == '1']) + print(cl_cost({res : 1})) - # We run the qiro instance and get the results! - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2, mes_kwargs = mes_kwargs) - # and also the final graph, that has been adjusted - final_Graph = qiro_instance.problem - - # Lets see what the 5 best results are - print("QIRO 5 best results") - maxfive = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] - costFunc = maxIndepSetclCostfct(G) - for key, val in res_qiro.items(): - if key in maxfive: - - print(key) - print(costFunc({key:1})) - - - # and compare them with the networkx result of the max_clique algorithm, where we might just see a better result than the heuristical NX algorithm! print("Networkx solution") print(nx.approximation.maximum_independent_set(G)) + + # Draw the final graph and the original graph for comparison + plt.figure(1) + nx.draw(final_graph, with_labels = True, node_color='#ADD8E6', edge_color='#D3D3D3') + plt.title('Final QIRO graph') + + plt.figure(2) + most_likely = [index for index, value in enumerate(max_five_qiro[0]) if value == '1'] + nx.draw(G, with_labels = True, + node_color=['#FFCCCB' if node in most_likely else '#ADD8E6' for node in G.nodes()], + edge_color='#D3D3D3') + plt.title('Original graph with most likely QIRO solution') + plt.show() \ No newline at end of file diff --git a/src/qrisp/algorithms/qiro/qiro_problem.py b/src/qrisp/algorithms/qiro/qiro_problem.py index 059ac78e..8c4ad04d 100644 --- a/src/qrisp/algorithms/qiro/qiro_problem.py +++ b/src/qrisp/algorithms/qiro/qiro_problem.py @@ -23,7 +23,7 @@ class QIROProblem(QAOAProblem): r""" - Structure to run QIRO algorithms. The idea is based on the paper by J. Finzgar et al. (https://arxiv.org/pdf/2308.13607.pdf). + Structure to run QIRO algorithms. The idea is based on the paper by `J. Finzgar et al. `_. This class encapsulates the replacement routine, cost operator, mixer operator, classical cost function and initial state preparation function for a specific QIRO problem instance. @@ -34,7 +34,6 @@ class QIROProblem(QAOAProblem): from qrisp import QuantumVariable from qrisp.qiro import QIROProblem, create_maxClique_replacement_routine, create_maxClique_cost_operator_reduced, qiro_RXMixer, qiro_init_function - from qrisp.qiro.qiro_problem import QIROProblem from qrisp.qaoa import max_clique_problem, create_max_clique_cl_cost_function import matplotlib.pyplot as plt import networkx as nx diff --git a/src/qrisp/algorithms/qiro/qiroproblems/__init__.py b/src/qrisp/algorithms/qiro/qiroproblems/__init__.py index 6437d028..59b04409 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/__init__.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/__init__.py @@ -17,8 +17,8 @@ """ -from qrisp.algorithms.qiro.qiroproblems.qiroMaxCliqueInfrastr import * -from qrisp.algorithms.qiro.qiroproblems.qiroMaxIndepSetInfrastr import * +from qrisp.algorithms.qiro.qiroproblems.qiroMaxClique import * +from qrisp.algorithms.qiro.qiroproblems.qiroMaxIndepSet import * from qrisp.algorithms.qiro.qiroproblems.qiroMaxSatInfrastr import * from qrisp.algorithms.qiro.qiroproblems.qiroMaxSetPackInfrastr import * diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxCliqueInfrastr.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py similarity index 53% rename from src/qrisp/algorithms/qiro/qiroproblems/qiroMaxCliqueInfrastr.py rename to src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py index 416ea4cc..d7d324d9 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxCliqueInfrastr.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py @@ -16,7 +16,6 @@ ********************************************************************************/ """ - from qrisp import rz, rzz, x import numpy as np import copy @@ -24,119 +23,114 @@ from qrisp.algorithms.qiro.qiroproblems.qiro_utils import * - - -def create_maxClique_replacement_routine( res, Graph, solutions, exclusions): +def create_maxClique_replacement_routine(res, graph, solutions, exclusions): """ - Creates a replacement routine for the problem structure, i.e. defines the replacement rules. - See the original paper for description of the update rules + Creates a replacement routine for the problem structure, i.e., defines the replacement rules. + See the `original paper `_ for a description of the update rules. Parameters ---------- res : dict Result dictionary of initial QAOA optimization procedure. - Graph : nx.Graph - The Graph defining the problem instance. - solutions : List - Qubits which have been found to be positive correlated, i.e. part of the problem solution. - exclusions : List - Qubits which have been found to be negative correlated, i.e. not part of the problem solution, or contradict solution qubits in accordance to the update rules. + graph : nx.Graph + The graph defining the problem instance. + solutions : list + Qubits which were found to be positively correlated, i.e., part of the problem solution. + exclusions : list + Qubits which were found to be negatively correlated, i.e., not part of the problem solution, or contradict solution qubits in accordance with the update rules. Returns ------- - newGraph : nx.Graph - Updated graph of the problem instance. - solutions : List + newgraph : nx.Graph + Updated graph for the problem instance. + solutions : list Updated set of solutions to the problem. - sign : Int + sign : int The sign of the correlation. - exclusions : List - Updated set of exclusions to the problem. + exclusions : list + Updated set of exclusions for the problem. """ - orig_edges = [list(item) for item in Graph.edges()] - orig_nodes = list(Graph.nodes()) + orig_edges = [list(item) for item in graph.edges()] + orig_nodes = list(graph.nodes()) #get the max_edge and eval the sum and sign max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) - newGraph = copy.deepcopy(Graph) + newgraph = copy.deepcopy(graph) # we just directly remove vertices from the graph if isinstance(max_item, int): if sign > 0: - border = list(Graph.adj[max_item].keys()) + border = list(graph.adj[max_item].keys()) border.append(max_item) - to_remove = [int(item) for item in Graph.nodes() if item not in border] - newGraph.remove_nodes_from( [item for item in Graph.nodes() if item not in border]) + to_remove = [int(item) for item in graph.nodes() if item not in border] + newgraph.remove_nodes_from( [item for item in graph.nodes() if item not in border]) solutions.append(max_item) exclusions += to_remove elif sign < 0: #remove item - newGraph.remove_node(max_item) + newgraph.remove_node(max_item) exclusions.append(max_item) else: if sign > 0: #keep the two items in solution and remove all that are not adjacent to both - intersect = list(set(list(Graph.adj[max_item[0]].keys())) & set(list(Graph.adj[max_item[0]].keys()))) + intersect = list(set(list(graph.adj[max_item[0]].keys())) & set(list(graph.adj[max_item[0]].keys()))) intersect.append(max_item[0]) intersect.append(max_item[1]) - to_remove = [int(item) for item in Graph.nodes() if item not in intersect] - newGraph.remove_nodes_from([item for item in Graph.nodes() if item not in intersect]) + to_remove = [int(item) for item in graph.nodes() if item not in intersect] + newgraph.remove_nodes_from([item for item in graph.nodes() if item not in intersect]) solutions.append(max_item[0]) solutions.append(max_item[1]) exclusions += to_remove elif sign < 0: #remove all that do not border on either! node - union = list(Graph.adj[max_item[0]].keys()) - union += list(Graph.adj[max_item[1]].keys()) + union = list(graph.adj[max_item[0]].keys()) + union += list(graph.adj[max_item[1]].keys()) union.append(max_item[0]) union.append(max_item[1]) - to_remove = [int(item) for item in Graph.nodes() if item not in union] - #to_delete = [item for item in Graph.nodes() if item not in union] - newGraph.remove_nodes_from([item for item in Graph.nodes() if item not in union]) + to_remove = [int(item) for item in graph.nodes() if item not in union] + #to_delete = [item for item in graph.nodes() if item not in union] + newgraph.remove_nodes_from([item for item in graph.nodes() if item not in union]) exclusions += to_remove - return newGraph, solutions, sign, exclusions + return newgraph, solutions, sign, exclusions - -def create_maxClique_cost_operator_reduced(Graph, solutions = []): - """ - | Based on PennyLane unconstrained mixer implementation. - | Initial state in :math:`(|0>+|1>)^{\otimes n}` . - | This operator is then adjusted to consider qubits that have been found to be a part of the problem solution. +def create_maxClique_cost_operator_reduced(graph, solutions=[]): + r""" + Creates the ``cost_operator`` for the problem instance. + This operator is adjusted to consider qubits that were found to be a part of the problem solution. Parameters ---------- G : nx.Graph - Graph of the problem instance - solutions : List - Qubits which have been found to be positive correlated, i.e. part of the problem solution. + The graph for the problem instance. + solutions : list + Qubits which were found to be positively correlated, i.e., part of the problem solution. Returns ------- - partialCostMixer : function - The Operator to be applied to the problem ``QuantumVariable`` + cost_operator : function + A function receiving a :ref:`QuantumVariable` and a real parameter $\gamma$. This function performs the application of the cost operator. """ - G_compl = nx.complement(Graph) - def partialcostMixer(qv, gamma): + G_compl = nx.complement(graph) + def cost_operator(qv, gamma): for pair in list(G_compl.edges()): rzz(3*gamma, qv[pair[0]], qv[pair[1]]) rz(-gamma, qv[pair[0]]) rz(-gamma, qv[pair[1]]) - for i in Graph.nodes(): + for i in graph.nodes(): if not i in solutions: rz(gamma, qv[i]) - - return partialcostMixer + return cost_operator diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py new file mode 100644 index 00000000..08c964a3 --- /dev/null +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py @@ -0,0 +1,174 @@ +""" +\******************************************************************************** +* Copyright (c) 2023 the Qrisp authors +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* This Source Code may also be made available under the following Secondary +* Licenses when the conditions for such availability set forth in the Eclipse +* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 +* with the GNU Classpath Exception which is +* available at https://www.gnu.org/software/classpath/license.html. +* +* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +********************************************************************************/ +""" + +from qrisp import rz, rzz, x +import numpy as np +import copy +from qrisp.algorithms.qiro.qiroproblems.qiro_utils import * + + +def create_maxIndep_replacement_routine(res, graph, solutions=[], exclusions=[]): + """ + Creates a replacement routine for the problem structure, i.e., defines the replacement rules. + See the `original paper `_ for a description of the update rules. + + Parameters + ---------- + res : dict + Result dictionary of initial QAOA optimization procedure. + graph : nx.Graph + The graph defining the problem instance. + solutions : list + Qubits which were found to be positively correlated, i.e., part of the problem solution. + exclusions : list + Qubits which were found to be negatively correlated, i.e., not part of the problem solution, or contradict solution qubits in accordance with the update rules. + + Returns + ------- + newgraph : nx.Graph + Updated graph for the problem instance. + solutions : list + Updated set of solutions to the problem. + sign : int + The sign of the correlation. + exclusions : list + Updated set of exclusions for the problem. + + """ + + # For multi qubit correlations + orig_edges = [list(item) for item in graph.edges()] + + # FOR SINGLE QUBIT CORRELATIONS + orig_nodes = list(graph.nodes()) + + max_item = [] + max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) + newgraph = copy.deepcopy(graph) + + # we just directly remove vertices from the graph + if isinstance(max_item, int): + if sign > 0: + to_remove = graph.adj[max_item] + newgraph.remove_nodes_from(to_remove) + solutions.append(max_item) + exclusions += to_remove + + elif sign < 0: + newgraph.remove_node(max_item) + exclusions.append(max_item) + + else: + if sign > 0: + newgraph.remove_nodes_from(max_item) + exclusions += list(max_item) + + elif sign < 0: + #remove + intersect = list(set( list(graph.adj[max_item[0]].keys()) ) & set( list(graph.adj[max_item[0]].keys()) )) + newgraph.remove_nodes_from(intersect) + exclusions += intersect + + return newgraph, solutions, sign, exclusions + + +def create_maxIndep_cost_operator_reduced(graph, solutions=[]): + r""" + Creates the ``cost_operator`` for the problem instance. + This operator is adjusted to consider qubits that were found to be a part of the problem solution. + + Parameters + ---------- + G : nx.Graph + The graph for the problem instance. + solutions : list + Qubits which were found to be positively correlated, i.e., part of the problem solution. + + Returns + ------- + cost_operator : function + A function receiving a :ref:`QuantumVariable` and a real parameter $\gamma$. This function performs the application of the cost operator. + + """ + def cost_operator(qv, gamma): + for pair in list(graph.edges()): + #cx(qv[pair[0]], qv[pair[1]]) + rzz(3*gamma, qv[pair[0]], qv[pair[1]]) + rz(-gamma, qv[pair[0]]) + rz(-gamma, qv[pair[1]]) + for i in graph.nodes(): + if not i in solutions: + rz(gamma, qv[i]) + + return cost_operator + + + +""" def create_maxIndep_mixer_reduced(graph, solutions): + + def RX_mixer(qv, beta): + + from qrisp import rx + for i in graph.nodes(): + if not i in solutions: + rx(2 * beta, qv[i]) + return RX_mixer + + +def init_function_reduced(graph, solutions): + + def init_state(qv): + from qrisp import h + for i in graph.nodes(): + if not i in solutions: + h(qv[i]) + for i in solutions: + x(qv[i]) + return init_state + + + +#TODO: +def create_maxIndep_cl_cost_function_reduced(graph): + + #btw alternative formulation: for edge: check if string[edge[0]] != string[edge[1]] + def aClcostFct(res_dic): + tot_energy = 0.001 + tot_counts = 0 + for state in res_dic.keys(): + # we assume solution is right + temp = True + energy = 0 + for edge in graph.edges(): + if not state[edge[0]] != state[edge[1]]: + temp = False + + # else we just add the number of marked as |1> nodes + if temp: + intlist = [s for s in range(len(list(state))) if list(state)[s] == "1"] + energy = -len(intlist) + + tot_energy += energy * res_dic[state] + tot_counts += res_dic[state] + + #print(tot_energy/tot_counts) + + return tot_energy/tot_counts + + return aClcostFct +""" \ No newline at end of file diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSetInfrastr.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSetInfrastr.py deleted file mode 100644 index 718b976d..00000000 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSetInfrastr.py +++ /dev/null @@ -1,160 +0,0 @@ -from qrisp import rz, rzz, x -import numpy as np -import copy -from qrisp.algorithms.qiro.qiroproblems.qiro_utils import * - -def create_maxIndep_replacement_routine( res, Graph, solutions= [], exclusions= []): - """ - Creates a replacement routine for the problem structure, i.e. defines the replacement rules. - See the original paper for description of the update rules - - Parameters - ---------- - res : dict - Result dictionary of initial QAOA optimization procedure. - Graph : nx.Graph - The Graph defining the problem instance. - solutions : List - Qubits which have been found to be positive correlated, i.e. part of the problem solution. - exclusions : List - Qubits which have been found to be negative correlated, i.e. not part of the problem solution, or contradict solution qubits in accordance to the update rules. - - Returns - ------- - newGraph : nx.Graph - Updated graph of the problem instance. - solutions : List - Updated set of solutions to the problem. - sign : Int - The sign of the correlation. - exclusions : List - Updated set of exclusions to the problem. - - - - - - """ - # For multi qubit correlations - orig_edges = [list(item) for item in Graph.edges()] - - # FOR SINGLE QUBIT CORRELATIONS - orig_nodes = list(Graph.nodes()) - - max_item = [] - max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) - newGraph = copy.deepcopy(Graph) - - # we just directly remove vertices from the graph - if isinstance(max_item, int): - if sign > 0: - to_remove = Graph.adj[max_item] - newGraph.remove_nodes_from(to_remove) - solutions.append(max_item) - exclusions += to_remove - - elif sign < 0: - newGraph.remove_node(max_item) - exclusions.append(max_item) - - else: - if sign > 0: - newGraph.remove_nodes_from(max_item) - exclusions += list(max_item) - - elif sign < 0: - #remove - intersect = list(set( list(Graph.adj[max_item[0]].keys()) ) & set( list(Graph.adj[max_item[0]].keys()) )) - newGraph.remove_nodes_from(intersect) - exclusions += intersect - - return newGraph, solutions, sign, exclusions - - -def create_maxIndep_cost_operator_reduced(Graph, solutions= []): - """ - | Based on PennyLane unconstrained mixer implementation. - | Initial state in :math:`(|0>+|1>)^{\otimes n}` . - | This operator is then adjusted to consider qubits that have been found to be a part of the problem solution. - - Parameters - ---------- - G : nx.Graph - Graph of the problem instance - solutions : List - Qubits which have been found to be positive correlated, i.e. part of the problem solution. - - Returns - ------- - partialCostMixer : function - The Operator to be applied to the problem ``QuantumVariable`` - - """ - def partialcostMixer(qv, gamma): - for pair in list(Graph.edges()): - #cx(qv[pair[0]], qv[pair[1]]) - rzz(3*gamma, qv[pair[0]], qv[pair[1]]) - rz(-gamma, qv[pair[0]]) - rz(-gamma, qv[pair[1]]) - for i in Graph.nodes(): - if not i in solutions: - rz(gamma, qv[i]) - #return qv - - return partialcostMixer - - - -""" def create_maxIndep_mixer_reduced(Graph, solutions): - - def RX_mixer(qv, beta): - - from qrisp import rx - for i in Graph.nodes(): - if not i in solutions: - rx(2 * beta, qv[i]) - return RX_mixer - - -def init_function_reduced(Graph, solutions): - - def init_state(qv): - from qrisp import h - for i in Graph.nodes(): - if not i in solutions: - h(qv[i]) - for i in solutions: - x(qv[i]) - return init_state - - - -#TODO: -def create_maxIndep_cl_cost_function_reduced(Graph): - - #btw alternative formulation: for edge: check if string[edge[0]] != string[edge[1]] - def aClcostFct(res_dic): - tot_energy = 0.001 - tot_counts = 0 - for state in res_dic.keys(): - # we assume solution is right - temp = True - energy = 0 - for edge in Graph.edges(): - if not state[edge[0]] != state[edge[1]]: - temp = False - - # else we just add the number of marked as |1> nodes - if temp: - intlist = [s for s in range(len(list(state))) if list(state)[s] == "1"] - energy = -len(intlist) - - tot_energy += energy * res_dic[state] - tot_counts += res_dic[state] - - #print(tot_energy/tot_counts) - - return tot_energy/tot_counts - - return aClcostFct -""" \ No newline at end of file From abd4944e8a49aa5be9d26e1d4541b29af0108f63 Mon Sep 17 00:00:00 2001 From: Rene Zander Date: Thu, 17 Oct 2024 14:26:03 +0200 Subject: [PATCH 3/8] Update QIRO MaxSetPacking, QAOA MaxSAt,E3Lin2 removed import init_function --- .../source/general/tutorial/QIROtutorial.rst | 166 +++++++++--------- .../qaoaProblems/qaoa.problems.eThrLinTwo.rst | 2 +- .../qaoaProblems/qaoa.problems.maxSat.rst | 2 +- .../QIROImplementations/QIROMaxClique.rst | 30 ++-- .../QIROImplementations/QIROMaxIndep.rst | 31 ++-- .../QIROImplementations/QIROMaxSetPack.rst | 86 ++++----- src/qrisp/algorithms/qiro/qiro_mixers.py | 2 +- src/qrisp/algorithms/qiro/qiro_problem.py | 5 +- .../algorithms/qiro/qiroproblems/__init__.py | 2 +- .../qiro/qiroproblems/qiroMaxClique.py | 2 +- .../qiro/qiroproblems/qiroMaxIndepSet.py | 16 +- .../qiro/qiroproblems/qiroMaxSatInfrastr.py | 18 ++ ...axSetPackInfrastr.py => qiroMaxSetPack.py} | 49 +++--- .../qiro/qiroproblems/qiro_utils.py | 2 +- 14 files changed, 221 insertions(+), 192 deletions(-) rename src/qrisp/algorithms/qiro/qiroproblems/{qiroMaxSetPackInfrastr.py => qiroMaxSetPack.py} (81%) diff --git a/documentation/source/general/tutorial/QIROtutorial.rst b/documentation/source/general/tutorial/QIROtutorial.rst index f5329181..6316a014 100644 --- a/documentation/source/general/tutorial/QIROtutorial.rst +++ b/documentation/source/general/tutorial/QIROtutorial.rst @@ -5,13 +5,12 @@ Quantum-Informed Recursive Optimization This tutorial will give you an overview of problem specific implementations for solving optimization problems with the :ref:`Quantum Approximate Optimization Algorithm (QAOA) `. -The Quantum Alternating Operator Algorithm (QAOA) is used to solve combinatorical optimization instances of NP-hard instances. For further information see our :ref:`tutorial on QAOA `! - -The paper `Quantum-Informed Recursive Optimization Algorithms (2023) `_ by J. Finzgar et. al. establishes a blueprint for developing alogrithms that update the problem instance recursively, based on the correlations in results obtained by QAOA. +The Quantum Approximate Optimization Algorithm (QAOA), is used to solve combinatorical optimization problem instances of NP-hard problems. For further information see our :ref:`tutorial on QAOA `! +The paper `Quantum-Informed Recursive Optimization Algorithms (2023) `_ by J. Finzgar et. al. establishes a blueprint for developing alogrithms that update the problem instance recursively, based on the correlations in results obtained by QAOA. While QAOA cost functions are designed with specific problem instances in mind, this research has shown promising improvements to this to approach by further leveraging the problem structure. -We have implemented this approach for different problem instances, namely :ref:`MaxClique `, :ref:`MaxIndependentSet `, :ref:`MaxSat ` and :ref:`MaxSetPacking `. The explanation below tackles the `Maximum Indepent Set (MIS) `_ instance, in analogy to the original paper by Finzgar et. al. +We have implemented this approach for different problem instances, namely :ref:`MaxClique `, :ref:`MaxIndependentSet `, :ref:`MaxSat ` and :ref:`MaxSetPacking `. The explanation below tackles the `Maximum Indepent Set (MIS) `_ problem, in analogy to the original paper by Finzgar et. al. Starting point of the algorithm ------------------------------- @@ -21,7 +20,7 @@ The algorithm evaluates the result of a :ref:`QAOA ` optimization procedur The MIS is defined via a graph with a set of vertices :math:`V` and a set of edges :math:`E` . To solve this optimization problem, one has to find the maximum number of vertices, where none of the vertices share a common edge, i.e. we want to find .. math:: - \max \left( |V'| , V' \subset V \right) , \, \, \text{where} \sum_{ i, j \in |V'| } \mathbb{1}_{(i,j) \in E} = 0 + \max \left( |V'| , V' \subset V \right), \text{ where } \sum_{ i, j \in V' } \mathbb{1}_{(i,j) \in E} = 0 The QAOA cost operator has the form @@ -33,24 +32,24 @@ and the mixer operator is .. math:: H_M = - \sum_{i \in V} Z_i . -Executing the optimization loop of our QAOA implementation of depth :math:`p` will result in the state +Executing the optimization loop of our QAOA implementation of depth :math:`p` will result in the state .. math:: - \ket{\psi} = e^{i \gamma^{*}_{p-1} H_C} e^{i \beta^{*}_{p-1} H_M} ... e^{i \gamma^{*}_0 H_C} e^{i \beta^{*}_0 H_M} ( \ket{0} + \ket{1} )^{\otimes n}, + \ket{\psi} = e^{i \beta^{*}_{p-1} H_M} e^{i \gamma^{*}_{p-1} H_C} \dotsb e^{i \beta^{*}_0 H_M} e^{i \gamma^{*}_0 H_C} ( \ket{0} + \ket{1} )^{\otimes n}, -with :math:`n` being the number of vertices in the graph. The :math:`\{\gamma^{*}_{p-1}, \beta^{*}_{p-1}, ... \gamma^{*}_0 , \beta^{*}_0 \}` represent the optimized QAOA angles. +with :math:`n` being the number of vertices in the graph. The parameters :math:`\{\gamma^{*}_{p-1}, \beta^{*}_{p-1}, \dotsc, \gamma^{*}_0 , \beta^{*}_0 \}` represent the optimized QAOA angles. This state :math:`\ket{\psi}` is the starting point of the **Quantum-Informed Recursive Optimization** considerations. Establishing correlations ------------------------- -We quickly want to motivate the search correlations with the arguments made in the original paper. For further detail we again refer to the original paper by `J. Finzgar et. al. `_ +We quickly want to motivate the search for correlations with the arguments made in the original paper. For further detail we again refer to the original paper by `J. Finzgar et. al. `_ -Firstly, QAOA is a local algorithm, which was shown to severely limit its performance. In turn, applying non-local updates classically has been proposed, i.e. in recursive QAOA (RQAOA) implementations. Values of variables are iteratively frozen as given by the correlations in the measurements of -quantum state prepared in a QAOA optimization routine. In QIRO, this information is used to recursively decrease the size of the problem instance by introducing problem-specific classical optimization routines. With this, new connections between previously unconnected nodes non-local effects are introduced, counterbalancing the locality inherent to QAOA. +Firstly, QAOA is a local algorithm, which was shown to severely limit its performance. In turn, applying non-local updates classically has been proposed, i.e., in recursive QAOA (RQAOA) implementations. Values of variables are iteratively frozen as given by the correlations in the measurements of +the quantum state prepared in a QAOA optimization routine. In QIRO, this information is used to recursively decrease the size of the problem instance by introducing problem-specific classical optimization routines. With this, new connections between previously unconnected nodes non-local effects are introduced, counterbalancing the locality inherent to QAOA. -For any problem specific QIRO implementation the next step is to evaluate the expected correlations in the solution space, i.e. computing the values of the matrix :math:`\text{M}`, where +For any problem specific QIRO implementation the next step is to evaluate the expected correlations in the solution space, i.e., computing the values of the matrix :math:`\text{M}`, where :math:`\text{M}_{ii} = \bra{\psi} Z_i \ket{\psi}` and :math:`\text{M}_{ij} = \bra{\psi} Z_i Z_j \ket{\psi}`. We then need to find the **maximum absolute value** of :math:`\text{M}`. @@ -58,27 +57,28 @@ We then need to find the **maximum absolute value** of :math:`\text{M}`. Reducing the problem -------------------- -Based on the **maximum absolute entry** of M and its sign, one of the following replacements is employed: +Based on the **maximum absolute entry** of the correlation matrix M and its sign, one of the following replacements is employed: -* If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th vertex is set to be in the independent set (IS). In turn, we can remove all vertices that share an edge with this vertex can be removed from the graph, since including them in the solution would violate the problem constraints +* If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th vertex is set to be in the independent set (IS). In turn, all vertices that share an edge with this vertex can be removed from the graph, since including them in the solution would violate the problem constraints. -* If :math:`\text{M}_{ii} < 0` is the maximum absolute value we remove :math:`i`-th vertex from the graph +* If :math:`\text{M}_{ii} < 0` is the maximum absolute value, we remove :math:`i`-th vertex from the graph. -* If :math:`\text{M}_{ij} > 0, (i, j) ∈ E` was selected, we remove both nodes from the graph with the argument, that, since both of them would be in the same state in the final solution, including both as part of the solution would violate the constraint, as they share an edge. In turn, they can be removed from the graph. +* If :math:`\text{M}_{ij} > 0, (i, j) ∈ E` was selected, we remove both vertices from the graph with the argument, that, since both of them would be in the same state in the final solution, including both as part of the solution would violate the constraint, as they share an edge. In turn, they can be removed from the graph. -* If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we remove all nodes that share an edge with both vertices :math:`i` and :math:`j`. Since one of the vertices :math:`i` and :math:`j` will be part of the final solution (but not both), any vertex that is connected to both :math:`i` and :math:`j` is guaranteed to violate the problem constraints, and can be removed from the graph. In this case it may be possible, that no vertex is found to be as a canditate for removing. We will then simple chose second biggest absolute value of **M** for the replacement routine. +* If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we remove all vertices that share an edge with both vertices :math:`i` and :math:`j`. Since one of the vertices :math:`i` and :math:`j` will be part of the final solution (but not both), any vertex that is connected to both :math:`i` and :math:`j` is guaranteed to violate the problem constraints, and can be removed from the graph. In this case, it may be possible that no vertex is found to be a canditate for being removed. We will then simply choose the second biggest absolute value of **M** for the replacement routine. -These operations are undertaken directly on the ``networkx`` graph that has been fed to instance of the ``QIROProblem`` class, see the code example below. -We then hand over the reduced problem graph to a new ``QAOAProblem`` instance, optimize the parameter, and reduce the problem again with the same subroutine as above. +These operations are performed on a copy of the ``networkx`` graph that was provided to the instance of the :ref:`QIROProblem` class, see the code example below. +Based on the reduced problem graph, the ``cost_operator``, ``mixer``, and ``init_function`` are updated. +Subsequently, we optimize the parameters for the simplified ``QAOAProblem`` instance, and reduce the problem again with the same subroutine as described above. The final solution --------------------- +------------------ The after a specific number of recursions the final solution is returned as the result of a ``QAOAProblem`` optimization routine, where we consider the excluded and included vertices from the above steps in the ``cost_operator``, ``mixer`` and ``init_function`` of the ``QAOAProblem``. -The final result is therefore a the classic ``dictionary`` return from the ``QAOAProblem`` class and poses an optimized solution to the initial full problem instance. +The final result is therefore a dictionary of measurement results from the :meth:`.run ` method of the :ref:`QAOAProblem` class and poses an optimized solution to the initial full problem instance. Try it out yourself with the example code below! @@ -89,30 +89,29 @@ QIRO implementation The QIRO class -------------- -Upon instanciation, the :ref:`QIROProblem` class requires five arguments: +Upon instantiation, the :ref:`QIROProblem` class requires five arguments: * The ``problem`` to be solved, which not necessarly a graph, since QIRO is also implemented for :ref:`MaxSat `. * The ``replacement_routine``, which has the job of performing the aforementioned specific reductions to the ``problem`` object. -* The ``cost_operator``, ``mixer``, ``init_function`` and ``cl_cost_function`` in analogy to :ref:`QAOAProblem` instanciation. +* The ``cost_operator``, ``mixer``, ``cl_cost_function`` and ``init_function`` in analogy to :ref:`QAOAProblem` instantiation. -Why the ``cost_operator``, ``mixer``, and ``init_function`` undergo some slight adjustements, will be made clear in the code example below, aswell as the necessity -for directly assigning a ``cost_operator``, a ``mixer``, and and ``init_function``. +Why the ``cost_operator``, ``mixer``, and ``init_function`` undergo some slight adjustements, will be made clear in the code example below, as well as the necessity +for directly assigning a ``cost_operator``, a ``mixer``, and an ``init_function``. -To run the instance and solve the optimization problem we use the ``.run_qiro`` function, which takes the following arguments: -qarg, depth, n_recursions, mes_kwargs = {}, max_iter = 50 +To run the instance and solve the optimization problem we use the :meth:`.run ` method, which takes the following arguments: +``qarg``, ``depth``, ``n_recursions``, ``mes_kwargs = {}``, and ``max_iter = 50``. -* The :ref:`QuantumVariable` ``qarg``, which is the quantum argument the algorithm is evaluated on, in analogy to the QAOA module +* The :ref:`QuantumVariable` ``qarg``, which is the quantum argument the algorithm is evaluated on. * The integer ``depth``, which is the depth of QAOA optimization circuit. * The integer ``n_recursions``, representing the number of QIRO update steps. -* The dictionary ``mes_kwargs = {}``, empty by default, to define further specifications of the measurements. +* The dictionary ``mes_kwargs = {}``, an empty dictionary by default, to define further specifications of the measurements. * The integer ``max_iter = 50``, set to 50 by default, which defines the maximum number of the classical optimization loop with the ``COBYLA`` optimizer as part of the QAOA optimization routine - Maximum independent set example =============================== -We now investigate a code example for the Maximum independent set problem instance. +We now investigate a code example for the maximum independent set problem. Preliminaries ------------- @@ -125,45 +124,49 @@ All in all, the function remains straight forward. We employ a ``find_max`` subr def create_maxIndep_replacement_routine( res, Graph, solutions= [], exclusions= []): - # For multi qubit correlations - orig_edges = [list(item) for item in Graph.edges()] + # for multi qubit correlations + orig_edges = [list(item) for item in graph.edges()] - # For single qubit correlations - orig_nodes = list(Graph.nodes()) - - # find the max_item + # for single qubit correlations + orig_nodes = list(graph.nodes()) + + max_item = [] max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) - # create a copy of the graph to prevent unwanted side effects - newGraph = copy.deepcopy(Graph) + # create a copy of the graph + newgraph = copy.deepcopy(graph) - # we just directly remove vertices from the graph, as suggested by the replacement rules - # if the item is an int, its a single node, else its an edge + # we remove nodes from the graph, as suggested by the replacement rules + # if the item is an int, it is a single node, else it is an edge if isinstance(max_item, int): if sign > 0: - # remove all adjacent nodes - newGraph.remove_nodes_from(Graph.adj[max_item]) + # remove all adjacent nodes + to_remove = graph.adj[max_item] + newgraph.remove_nodes_from(to_remove) solutions.append(max_item) - exclusions.append(max_item) + exclusions += to_remove + elif sign < 0: - # remove the nodes - newGraph.remove_node(max_item) + # remove the node + newgraph.remove_node(max_item) exclusions.append(max_item) + else: if sign > 0: # remove both nodes - newGraph.remove_nodes_from(max_item) - exclusions += max_item + newgraph.remove_nodes_from(max_item) + exclusions += list(max_item) + elif sign < 0: - # remove all nodes connected to both nodes - intersect = list(set( list(Graph.adj[max_item[0]].keys()) ) & set( list(Graph.adj[max_item[0]].keys()) )) - newGraph.remove_nodes_from(intersect) + # remove all nodes connected to both nodes + intersect = list(set( list(graph.adj[max_item[0]].keys()) ) & set( list(graph.adj[max_item[0]].keys()) )) + newgraph.remove_nodes_from(intersect) exclusions += intersect - return newGraph, solutions, sign, exclusions + return newgraph, solutions, sign, exclusions -As you might gave noticed in the code above, we add the nodes that are included into (respective excluded from) the solution to a list ``solutions`` (``exclusions``). -This allows us to directly :ref:`recycle the QAOA code ` for the ``cost_operator``, ``mixer`` and ``init_function`` of the original QAOA implementation with minor adjustments. +As you might have noticed in the code above, we add the nodes that are included into (respective excluded from) the solution to a list ``solutions`` (``exclusions``). +This allows us to directly :ref:`recycle the QAOA code ` for the ``cost_operator``, ``mixer`` and ``init_function`` of the original QAOA implementation with minor adjustments. Since we have to consider nodes that are already asigned to be in the solution set, or exluded from the algorithm, we do not want to apply these functions to said nodes. We therefore include some simple lines of code into the functions, for example in the ``qiro_RXMixer``: @@ -185,60 +188,57 @@ With the preliminaries out of the way, let us jump right into the code example: Code execution -------------- -We start off by importing all the relevant code and defining the graph of our problem, aswell as the :ref:`QuantumVariable` to run our instance on: +We start off by defining the graph of our problem, as well as the :ref:`QuantumVariable` to run our instance on: :: - # imports - from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced - from qrisp.qaoa.problems.maxIndepSetInfrastr import maxIndepSetclCostfct from qrisp import QuantumVariable + from qrisp.qiro import QIROProblem, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qaoa import create_max_indep_set_cl_cost_function + import matplotlib.pyplot as plt import networkx as nx - #Define a graph via the number of nodes, and the QuantumVariable arguments + # define a random graph via the number of nodes and the QuantumVariable arguments num_nodes = 13 - G = nx.erdos_renyi_graph(num_nodes, 0.4, seed = 107) + G = nx.erdos_renyi_graph(num_nodes, 0.4, seed = 107) qarg = QuantumVariable(G.number_of_nodes()) - -With this, we can directly throw everything thats relevant at the :ref:`QIROProblem` class and create an instance. +With this, we can directly throw everything that is relevant at the :ref:`QIROProblem` class and create an instance. :: - # assign the correct new update functions for qiro from above imports - qiro_instance = QIROProblem(G, - replacement_routine=create_maxIndep_replacement_routine, - cost_operator= create_maxIndep_cost_operator_reduced, - mixer= qiro_RXMixer, - cl_cost_function= maxIndepSetclCostfct, - init_function= qiro_init_function + # assign the correct new update functions for qiro + qiro_instance = QIROProblem(G, + replacement_routine=create_maxIndep_replacement_routine, + cost_operator=create_maxIndep_cost_operator_reduced, + mixer=qiro_RXMixer, + cl_cost_function=create_max_indep_set_cl_cost_function, + init_function=qiro_init_function ) We think of arguments for the ``run_qiro`` function, run the algorithm, et violà! :: - # We run the qiro instance and get the results! - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2) + res_qiro = qiro_instance.run_qiro(qarg=qarg, depth=3, n_recursions=2) -All done! We have solved the NP-hard MIS problem using Quantum-Informed Recursive Optimization! +All done! We have solved the MIS problem using Quantum-Informed Recursive Optimization! Results ------- -But of course we also want to investigate our results, so lets find out about the five most likely solutions the algorithm came up with: +But of course, we also want to investigate our results, so let's find out about the five most likely solutions the algorithm came up with: :: - print("QIRO 5 best results") - maxfive = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] - costFunc = maxIndepSetclCostfct(G) - for key, val in res_qiro.items(): - if key in maxfive: - # print the result bitstring and value of the costfunction - print(key) - print(costFunc({key:1})) + cl_cost = create_max_indep_set_cl_cost_function(G) + + print("5 most likely QIRO solutions") + max_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] + for res in max_five_qiro: + print([index for index, value in enumerate(res) if value == '1']) + print(cl_cost({res : 1})) We do not put the example output here, since the algorithm is not deterministic, and the output you receive may differ from what an example might show. Just go ahead and try it yourself! @@ -259,7 +259,7 @@ We can also compare these results with the standard QAOA implementation. from qrisp.qaoa.problems.maxIndepSetInfrastr import maxIndepSetCostOp from qrisp.qaoa.mixers import RX_mixer - Gtwo = nx.erdos_renyi_graph(num_nodes, 0.4, seed = 107) + Gtwo = nx.erdos_renyi_graph(num_nodes, 0.4, seed=107) qarg2 = QuantumVariable(Gtwo.number_of_nodes()) maxindep_instance = QAOAProblem(maxIndepSetCostOp(G), RX_mixer, maxIndepSetclCostfct(G)) res_qaoa = maxindep_instance.run( qarg = qarg2, depth = 3) @@ -277,7 +277,7 @@ As a final caveat, we can look at the graph we are left with after all reduction :: - final_Graph = qiro_instance.problem + final_graph = qiro_instance.problem Congratulations, you have reached the end of the tutorial and are now capable of solving the MIS problem in Qrisp! Should your appetite not be satisfied, we advise you to check out our other QIRO implementations: diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.eThrLinTwo.rst b/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.eThrLinTwo.rst index ccda278d..21b7b9be 100644 --- a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.eThrLinTwo.rst +++ b/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.eThrLinTwo.rst @@ -45,7 +45,7 @@ Example implementation :: from qrisp import QuantumVariable - from qrisp.qaoa import QAOAProblem, RX_mixer, create_e3lin2_cl_cost_function, create_e3lin2_cost_operator, e3lin2_init_function + from qrisp.qaoa import QAOAProblem, RX_mixer, create_e3lin2_cl_cost_function, create_e3lin2_cost_operator clauses = [[0,1,2,1],[1,2,3,0],[0,1,4,0],[0,2,4,1],[2,4,5,1],[1,3,5,1],[2,3,4,0]] num_variables = 6 diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSat.rst b/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSat.rst index b6035e1c..4f3a89bf 100644 --- a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSat.rst +++ b/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSat.rst @@ -66,7 +66,7 @@ Example implementation :: from qrisp import QuantumVariable - from qrisp.qaoa import QAOAProblem, RX_mixer, create_maxsat_cl_cost_function, create_maxsat_cost_operator, maxsat_init_function + from qrisp.qaoa import QAOAProblem, RX_mixer, create_maxsat_cl_cost_function, create_maxsat_cost_operator clauses = [[1,2,-3],[1,4,-6],[4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6]] num_variables = 6 diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst index d65af6b7..e5a9fff4 100644 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst +++ b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst @@ -16,7 +16,7 @@ Replacement routine ------------------- In this instance the replacements can be rather significant. In-depth investigation may show relaxations to be perform better on larger instances. -Based on the **maximum absolute entry** of the correlation matrix $M$ and its sign, one of the following replacements is employed: +Based on the **maximum absolute entry** of the correlation matrix M and its sign, one of the following replacements is employed: * If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th vertex is set to be in the clique set. In turn, all vertices that **do not share an edge with this vertex can be removed from the graph**, since including them in the solution would violate the problem constraints. @@ -52,21 +52,25 @@ Example implementation G = nx.erdos_renyi_graph(num_nodes, 0.7, seed=99) qarg = QuantumVariable(G.number_of_nodes()) - cl_cost = create_max_clique_cl_cost_function(G) - # QIRO - qiro_instance = QIROProblem(problem = G, - replacement_routine = create_maxClique_replacement_routine, - cost_operator = create_maxClique_cost_operator_reduced, - mixer = qiro_RXMixer, - cl_cost_function = create_max_clique_cl_cost_function, - init_function = qiro_init_function + qiro_instance = QIROProblem(problem=G, + replacement_routine= reate_maxClique_replacement_routine, + cost_operator=create_maxClique_cost_operator_reduced, + mixer=qiro_RXMixer, + cl_cost_function=create_max_clique_cl_cost_function, + init_function=qiro_init_function ) res_qiro = qiro_instance.run_qiro(qarg=qarg, depth=3, n_recursions=2) # The final graph that has been adjusted final_graph = qiro_instance.problem +That’s it! In the following, we print the 5 most likely solutions together with their cost values, and compare to the NetworkX solution + +:: + + cl_cost = create_max_clique_cl_cost_function(G) + print("5 most likely QIRO solutions") ax_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] for res in max_five_qiro: @@ -76,14 +80,18 @@ Example implementation print("Networkx solution") print(nx.approximation.max_clique(G)) +Finally, we visualize the final QIRO graph and the most likely solution. + +:: + # Draw the final graph and the original graph for comparison plt.figure(1) - nx.draw(final_graph, with_labels = True, node_color='#ADD8E6', edge_color='#D3D3D3') + nx.draw(final_graph, with_labels=True, node_color='#ADD8E6', edge_color='#D3D3D3') plt.title('Final QIRO graph') plt.figure(2) most_likely = [index for index, value in enumerate(max_five_qiro[0]) if value == '1'] - nx.draw(G, with_labels = True, + nx.draw(G, with_labels=True, node_color=['#FFCCCB' if node in most_likely else '#ADD8E6' for node in G.nodes()], edge_color=['#FFCCCB' if edge[0] in most_likely and edge[1] in most_likely else '#D3D3D3' for edge in G.edges()]) plt.title('Original graph with most likely QIRO solution') diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst index cb72bdee..3a32ae90 100644 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst +++ b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst @@ -16,7 +16,7 @@ Given a Graph :math:`G = (V,E)` maximize the size of an independent set, i.e. a Replacement routine ------------------- -Based on the **maximum absolute entry** of the correlation matrix $M$ and its sign, one of the following replacements is employed: +Based on the **maximum absolute entry** of the correlation matrix M and its sign, one of the following replacements is employed: * If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th vertex is set to be in the independent set (IS). In turn, all vertices that share an edge with this vertex can be removed from the graph, since including them in the solution would violate the problem constraints. @@ -51,20 +51,20 @@ Example implementation G = nx.erdos_renyi_graph(num_nodes, 0.4, seed = 107) qarg = QuantumVariable(G.number_of_nodes()) - cl_cost = create_max_indep_set_cl_cost_function(G) - - # QIRO qiro_instance = QIROProblem(G, replacement_routine=create_maxIndep_replacement_routine, - cost_operator= create_maxIndep_cost_operator_reduced, - mixer= qiro_RXMixer, - cl_cost_function= create_max_indep_set_cl_cost_function, - init_function= qiro_init_function + cost_operator=create_maxIndep_cost_operator_reduced, + mixer=qiro_RXMixer, + cl_cost_function=create_max_indep_set_cl_cost_function, + init_function=qiro_init_function ) res_qiro = qiro_instance.run_qiro(qarg=qarg, depth=3, n_recursions=2) - # The final graph that has been adjusted - final_graph = qiro_instance.problem +That’s it! In the following, we print the 5 most likely solutions together with their cost values, and compare to the NetworkX solution. + +:: + + cl_cost = create_max_indep_set_cl_cost_function(G) print("5 most likely QIRO solutions") max_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] @@ -75,14 +75,21 @@ Example implementation print("Networkx solution") print(nx.approximation.maximum_independent_set(G)) +Finally, we visualize the final QIRO graph and the most likely solution. + +:: + + # The final graph that has been adjusted + final_graph = qiro_instance.problem + # Draw the final graph and the original graph for comparison plt.figure(1) - nx.draw(final_graph, with_labels = True, node_color='#ADD8E6', edge_color='#D3D3D3') + nx.draw(final_graph, with_labels=True, node_color='#ADD8E6', edge_color='#D3D3D3') plt.title('Final QIRO graph') plt.figure(2) most_likely = [index for index, value in enumerate(max_five_qiro[0]) if value == '1'] - nx.draw(G, with_labels = True, + nx.draw(G, with_labels=True, node_color=['#FFCCCB' if node in most_likely else '#ADD8E6' for node in G.nodes()], edge_color='#D3D3D3') plt.title('Original graph with most likely QIRO solution') diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSetPack.rst b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSetPack.rst index ff477c8d..acd1a90f 100644 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSetPack.rst +++ b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSetPack.rst @@ -3,79 +3,61 @@ QIRO MaxSetPacking ================== -.. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxSetPackInfrastr +.. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxSetPack Problem description ------------------- -This implementation utilizes a Transformation to a MIS formulation. - -We therefore only need the Transformation function, and then use the MIS QIRO implementation. - -Given a universe :math:`[n]` and :math:`m` subsets :math:`S = (S_j)^m_{j=1}` , :math:`S_j \subset [n]` find the maximum -cardinality subcollection :math:`S' \subset S` of pairwise disjoint subsets. +Given a universe :math:`[n]` and :math:`m` subsets :math:`\mathcal S = (S_j)^m_{j=1}` , :math:`S_j \subset [n]`, find the maximum +cardinality subcollection :math:`\mathcal S' \subset \mathcal S` of pairwise disjoint subsets. +Following the work of `Hadfield et al. `_, the MaxSetPacking problem is solved by solving the :ref:`MaxIndepSet ` problem on the constraint graph $G=(V,E)$ +where vertices correspond to subsets in $\mathcal S$ and edges correspond to pairs of intersecting subsets. Transformation to MIS --------------------- -.. autofunction:: trafo_maxPackToMIS +.. autofunction:: transform_max_set_pack_to_mis -Full Example implementation: ----------------------------- +Example implementation +---------------------- :: - from qrisp.qaoa.problems.maxIndepSetInfrastr import maxIndepSetclCostfct, maxIndepSetCostOp - from qrisp.qiro import QIROProblem, trafo_maxPackToMIS, qiro_init_function, qiro_RXMixer, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced from qrisp import QuantumVariable + from qrisp.qiro import QIROProblem, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qaoa import create_max_indep_set_cl_cost_function + import matplotlib.pyplot as plt import networkx as nx + # sets are given as list of sets + sets = [{0,7,1},{6,5},{2,3},{5,4},{8,7,0},{2,4,7},{1,3,4},{7,9},{1,9},{1,3,8},{4,9},{0,7,9},{0,4,8},{1,4,8}] + + G = transform_max_set_pack_to_mis(sets) + qarg = QuantumVariable(G.number_of_nodes()) + + qiro_instance = QIROProblem(G, + replacement_routine=create_maxIndep_replacement_routine, + cost_operator=create_maxIndep_cost_operator_reduced, + mixer=qiro_RXMixer, + cl_cost_function=create_max_indep_set_cl_cost_function, + init_function=qiro_init_function + ) + res_qiro = qiro_instance.run_qiro(qarg=qarg, depth=3, n_recursions=2) +That’s it! In the following, we print the 5 most likely solutions together with their cost values, and compare to the NetworkX solution. - import random - random.seed(105) - # sets are given as list of lists - sets = [[0,7,1],[6,5],[2,3],[5,4],[8,7,0], [2,4,7],[1,3,4],[7,9],[1,9],[1,3,8],[4,9],[0,7,9],[0,4,8],[1,4,8]] - # full universe is given as a number of nodes - 1 - sol = 12 +:: - G = trafo_maxPackToMIS(problem=problem) - qarg = QuantumVariable(G.number_of_nodes()) + cl_cost = create_max_indep_set_cl_cost_function(G) - # set simulator shots - mes_kwargs = { - #below should be 5k - "shots" : 5000 - } - - # assign the correct new update functions for qiro from above imports - qiro_instance = QIROProblem(G, - replacement_routine=create_maxIndep_replacement_routine, - cost_operator= create_maxIndep_cost_operator_reduced, - mixer= qiro_RXMixer, - cl_cost_function= maxIndepSetclCostfct, - init_function= qiro_init_function - ) + print("5 most likely QIRO solutions") + max_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] + for res in max_five_qiro: + print([sets[index] for index, value in enumerate(res) if value == '1']) + print(cl_cost({res : 1})) - # We run the qiro instance and get the results! - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2, mes_kwargs = mes_kwargs) - # and also the final graph, that has been adjusted - final_Graph = qiro_instance.problem - - # Lets see what the 5 best results are - print("QIRO 5 best results") - maxfive = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] - costFunc = maxIndepSetclCostfct(G) - for key, val in res_qiro.items(): - if key in maxfive: - - print(key) - print(costFunc({key:1})) - - - # and compare them with the networkx result of the max_clique algorithm, where we might just see a better result than the heuristical NX algorithm! print("Networkx solution") - print(nx.approximation.maximum_independent_set(G)) + print([sets[index] for index in nx.approximation.maximum_independent_set(G)]) diff --git a/src/qrisp/algorithms/qiro/qiro_mixers.py b/src/qrisp/algorithms/qiro/qiro_mixers.py index abfccf62..ab45af0a 100644 --- a/src/qrisp/algorithms/qiro/qiro_mixers.py +++ b/src/qrisp/algorithms/qiro/qiro_mixers.py @@ -1,6 +1,6 @@ """ \******************************************************************************** -* Copyright (c) 2023 the Qrisp authors +* Copyright (c) 2024 the Qrisp authors * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at diff --git a/src/qrisp/algorithms/qiro/qiro_problem.py b/src/qrisp/algorithms/qiro/qiro_problem.py index 8c4ad04d..767fb282 100644 --- a/src/qrisp/algorithms/qiro/qiro_problem.py +++ b/src/qrisp/algorithms/qiro/qiro_problem.py @@ -1,6 +1,6 @@ """ \******************************************************************************** -* Copyright (c) 2023 the Qrisp authors +* Copyright (c) 2024 the Qrisp authors * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -23,7 +23,8 @@ class QIROProblem(QAOAProblem): r""" - Structure to run QIRO algorithms. The idea is based on the paper by `J. Finzgar et al. `_. + Central structure to run QIRO algorithms. A subcalss of the :ref:`QAOAProblem` class. + The idea is based on the paper by `J. Finzgar et al. `_. This class encapsulates the replacement routine, cost operator, mixer operator, classical cost function and initial state preparation function for a specific QIRO problem instance. diff --git a/src/qrisp/algorithms/qiro/qiroproblems/__init__.py b/src/qrisp/algorithms/qiro/qiroproblems/__init__.py index 59b04409..3ad1f8ae 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/__init__.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/__init__.py @@ -20,5 +20,5 @@ from qrisp.algorithms.qiro.qiroproblems.qiroMaxClique import * from qrisp.algorithms.qiro.qiroproblems.qiroMaxIndepSet import * from qrisp.algorithms.qiro.qiroproblems.qiroMaxSatInfrastr import * -from qrisp.algorithms.qiro.qiroproblems.qiroMaxSetPackInfrastr import * +from qrisp.algorithms.qiro.qiroproblems.qiroMaxSetPack import * diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py index d7d324d9..c1658aaf 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py @@ -1,6 +1,6 @@ """ \******************************************************************************** -* Copyright (c) 2023 the Qrisp authors +* Copyright (c) 2024 the Qrisp authors * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py index 08c964a3..72cba148 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py @@ -1,6 +1,6 @@ """ \******************************************************************************** -* Copyright (c) 2023 the Qrisp authors +* Copyright (c) 2024 the Qrisp authors * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -51,35 +51,41 @@ def create_maxIndep_replacement_routine(res, graph, solutions=[], exclusions=[]) """ - # For multi qubit correlations + # for multi qubit correlations orig_edges = [list(item) for item in graph.edges()] - # FOR SINGLE QUBIT CORRELATIONS + # for single qubit correlations orig_nodes = list(graph.nodes()) max_item = [] max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) + + # create a copy of the graph newgraph = copy.deepcopy(graph) - # we just directly remove vertices from the graph + # we remove nodes from the graph, as suggested by the replacement rules + # if the item is an int, it is a single node, else it is an edge if isinstance(max_item, int): if sign > 0: + # remove all adjacent nodes to_remove = graph.adj[max_item] newgraph.remove_nodes_from(to_remove) solutions.append(max_item) exclusions += to_remove elif sign < 0: + # remove the node newgraph.remove_node(max_item) exclusions.append(max_item) else: if sign > 0: + # remove both nodes newgraph.remove_nodes_from(max_item) exclusions += list(max_item) elif sign < 0: - #remove + # remove all nodes connected to both nodes intersect = list(set( list(graph.adj[max_item[0]].keys()) ) & set( list(graph.adj[max_item[0]].keys()) )) newgraph.remove_nodes_from(intersect) exclusions += intersect diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSatInfrastr.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSatInfrastr.py index 30c558ef..1ba846d7 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSatInfrastr.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSatInfrastr.py @@ -1,3 +1,21 @@ +""" +\******************************************************************************** +* Copyright (c) 2024 the Qrisp authors +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* This Source Code may also be made available under the following Secondary +* Licenses when the conditions for such availability set forth in the Eclipse +* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 +* with the GNU Classpath Exception which is +* available at https://www.gnu.org/software/classpath/license.html. +* +* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +********************************************************************************/ +""" + # just write a replacement_routine and throw out not relevant sets from qrisp import rz, x, cx diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPackInfrastr.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPack.py similarity index 81% rename from src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPackInfrastr.py rename to src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPack.py index b661f859..6fb51505 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPackInfrastr.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPack.py @@ -1,20 +1,32 @@ +""" +\******************************************************************************** +* Copyright (c) 2024 the Qrisp authors +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* This Source Code may also be made available under the following Secondary +* Licenses when the conditions for such availability set forth in the Eclipse +* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 +* with the GNU Classpath Exception which is +* available at https://www.gnu.org/software/classpath/license.html. +* +* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +********************************************************************************/ +""" -from qrisp import rz, rzz,rx, mcx, x, QuantumVariable, control -import numpy as np -import copy -from qrisp.algorithms.qiro.qiroproblems.qiro_utils import * -from qrisp.algorithms.qaoa.problems.maxSetPackInfrastr import get_neighbourhood_relations +from itertools import combinations import networkx as nx - -def trafo_maxPackToMIS(problem): +def transform_max_set_pack_to_mis(problem): """ - Function to transform a Maximum Set Packing problem into a Maximum independent set problem. + Transforms a Maximum Set Packing problem instance into a Maximum Independent Set problem instance. Parameters ---------- - problem : List - The problem definition in analogy to the QAOA maxSetPacking problem + problem : list[set] + A list of sets specifying the problem. Returns ------- @@ -23,18 +35,13 @@ def trafo_maxPackToMIS(problem): """ - # the MIS solution to G is equivalent to the solution of the maxSetPackingProblem + def non_empty_intersection(problem): + return [(i, j) for (i, s1), (j, s2) in combinations(enumerate(problem), 2) if s1.intersection(s2)] + + # create constraint graph G = nx.Graph() - G.add_nodes_from(list(range(len(problem[1])))) - n_dict = get_neighbourhood_relations(problem) - - for val in n_dict.values(): - - if len(val)<2: - continue - for index1 in range(len(val)-1): - for index2 in range(index1+1, len(val)): - G.add_edge(val[index1],val[index2]) + G.add_nodes_from(range(len(problem))) + G.add_edges_from(non_empty_intersection(problem)) return G diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiro_utils.py b/src/qrisp/algorithms/qiro/qiroproblems/qiro_utils.py index f4a2fbc0..3538f42b 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiro_utils.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiro_utils.py @@ -1,6 +1,6 @@ """ \******************************************************************************** -* Copyright (c) 2023 the Qrisp authors +* Copyright (c) 2024 the Qrisp authors * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at From 7ce01347f2ab4f193238cd6f19182695137f5c1e Mon Sep 17 00:00:00 2001 From: Rene Zander Date: Thu, 17 Oct 2024 18:55:00 +0200 Subject: [PATCH 4/8] Updates QIRO MaxSat, tutorial, documentation. Minor updates QAOA implementation. --- .../source/general/tutorial/QIROtutorial.rst | 110 ++++++++---------- .../source/reference/Algorithms/index.rst | 4 +- .../Algorithms/qaoa/{index.rst => QAOA.rst} | 0 .../Algorithms/qaoa/QAOAImplementations.rst | 18 +-- .../QUBO.rst} | 0 .../eThrLinTwo.rst} | 0 .../mKCS.png | Bin .../maxClique.png | Bin .../maxClique.rst} | 0 .../maxCut.png | Bin .../maxCut.rst} | 0 .../maxIndepSet.png | Bin .../maxIndepSet.rst} | 0 .../maxKColorableSubgraph.rst} | 2 +- .../maxSat.rst} | 13 ++- .../maxSetPack.rst} | 0 .../minSetCover.rst} | 0 .../reference/Algorithms/{ => qiro}/QIRO.rst | 19 ++- .../Algorithms/qiro/QIROImplementations.rst | 30 +++++ .../{qiroProblems => qiro}/QIROProblem.rst | 3 +- .../qrisp.qiro.QIROProblem.run_qiro.rst | 0 .../implementations}/QIROMaxClique.rst | 0 .../implementations}/QIROMaxIndep.rst | 1 - .../qiro/implementations/QIROMaxSat.rst | 100 ++++++++++++++++ .../implementations}/QIROMaxSetPack.rst | 0 .../Algorithms/qiroProblems/QIROImplemens.rst | 30 ----- .../QIROImplementations/QIROMaxSat.rst | 92 --------------- .../algorithms/qaoa/problems/eThrLinTwo.py | 9 +- .../algorithms/qaoa/problems/maxClique.py | 2 +- src/qrisp/algorithms/qaoa/problems/maxCut.py | 4 +- .../algorithms/qaoa/problems/maxIndepSet.py | 8 +- .../qaoa/problems/maxKColorableSubgraph.py | 4 +- src/qrisp/algorithms/qaoa/problems/maxSat.py | 46 ++++---- .../algorithms/qaoa/problems/minSetCover.py | 2 +- src/qrisp/algorithms/qiro/qiro_mixers.py | 2 +- .../algorithms/qiro/qiroproblems/__init__.py | 2 +- .../qiro/qiroproblems/qiroMaxClique.py | 16 +-- .../qiro/qiroproblems/qiroMaxIndepSet.py | 16 +-- .../{qiroMaxSatInfrastr.py => qiroMaxSat.py} | 64 ++++------ .../qiro/qiroproblems/qiro_utils.py | 16 +-- tests/test_QAOAmaxSat.py | 16 +-- 41 files changed, 301 insertions(+), 328 deletions(-) rename documentation/source/reference/Algorithms/qaoa/{index.rst => QAOA.rst} (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.QUBO.rst => implementations/QUBO.rst} (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.eThrLinTwo.rst => implementations/eThrLinTwo.rst} (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems => implementations}/mKCS.png (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems => implementations}/maxClique.png (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.maxClique.rst => implementations/maxClique.rst} (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems => implementations}/maxCut.png (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.maxCut.rst => implementations/maxCut.rst} (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems => implementations}/maxIndepSet.png (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.maxIndepSet.rst => implementations/maxIndepSet.rst} (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.maxKColorableSubgraph.rst => implementations/maxKColorableSubgraph.rst} (95%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.maxSat.rst => implementations/maxSat.rst} (91%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.maxSetPack.rst => implementations/maxSetPack.rst} (100%) rename documentation/source/reference/Algorithms/qaoa/{qaoaProblems/qaoa.problems.minSetCover.rst => implementations/minSetCover.rst} (100%) rename documentation/source/reference/Algorithms/{ => qiro}/QIRO.rst (65%) create mode 100644 documentation/source/reference/Algorithms/qiro/QIROImplementations.rst rename documentation/source/reference/Algorithms/{qiroProblems => qiro}/QIROProblem.rst (86%) rename documentation/source/reference/Algorithms/{qiroProblems => qiro}/generated/qrisp.qiro.QIROProblem.run_qiro.rst (100%) rename documentation/source/reference/Algorithms/{qiroProblems/QIROImplementations => qiro/implementations}/QIROMaxClique.rst (100%) rename documentation/source/reference/Algorithms/{qiroProblems/QIROImplementations => qiro/implementations}/QIROMaxIndep.rst (99%) create mode 100644 documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSat.rst rename documentation/source/reference/Algorithms/{qiroProblems/QIROImplementations => qiro/implementations}/QIROMaxSetPack.rst (100%) delete mode 100644 documentation/source/reference/Algorithms/qiroProblems/QIROImplemens.rst delete mode 100644 documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSat.rst rename src/qrisp/algorithms/qiro/qiroproblems/{qiroMaxSatInfrastr.py => qiroMaxSat.py} (76%) diff --git a/documentation/source/general/tutorial/QIROtutorial.rst b/documentation/source/general/tutorial/QIROtutorial.rst index 6316a014..bfed77fe 100644 --- a/documentation/source/general/tutorial/QIROtutorial.rst +++ b/documentation/source/general/tutorial/QIROtutorial.rst @@ -124,46 +124,46 @@ All in all, the function remains straight forward. We employ a ``find_max`` subr def create_maxIndep_replacement_routine( res, Graph, solutions= [], exclusions= []): - # for multi qubit correlations - orig_edges = [list(item) for item in graph.edges()] + # for multi qubit correlations + orig_edges = [list(item) for item in graph.edges()] - # for single qubit correlations - orig_nodes = list(graph.nodes()) + # for single qubit correlations + orig_nodes = list(graph.nodes()) - max_item = [] - max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) - - # create a copy of the graph - newgraph = copy.deepcopy(graph) - - # we remove nodes from the graph, as suggested by the replacement rules - # if the item is an int, it is a single node, else it is an edge - if isinstance(max_item, int): - if sign > 0: - # remove all adjacent nodes - to_remove = graph.adj[max_item] - newgraph.remove_nodes_from(to_remove) - solutions.append(max_item) - exclusions += to_remove - - elif sign < 0: - # remove the node - newgraph.remove_node(max_item) - exclusions.append(max_item) - - else: - if sign > 0: - # remove both nodes - newgraph.remove_nodes_from(max_item) - exclusions += list(max_item) - - elif sign < 0: - # remove all nodes connected to both nodes - intersect = list(set( list(graph.adj[max_item[0]].keys()) ) & set( list(graph.adj[max_item[0]].keys()) )) - newgraph.remove_nodes_from(intersect) - exclusions += intersect - - return newgraph, solutions, sign, exclusions + max_item = [] + max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) + + # create a copy of the graph + new_graph = copy.deepcopy(graph) + + # we remove nodes from the graph, as suggested by the replacement rules + # if the item is an int, it is a single node, else it is an edge + if isinstance(max_item, int): + if sign > 0: + # remove all adjacent nodes + to_remove = graph.adj[max_item] + new_graph.remove_nodes_from(to_remove) + solutions.append(max_item) + exclusions += to_remove + + elif sign < 0: + # remove the node + new_graph.remove_node(max_item) + exclusions.append(max_item) + + else: + if sign > 0: + # remove both nodes + new_graph.remove_nodes_from(max_item) + exclusions += list(max_item) + + elif sign < 0: + # remove all nodes connected to both nodes + intersect = list(set( list(graph.adj[max_item[0]].keys()) ) & set( list(graph.adj[max_item[0]].keys()) )) + new_graph.remove_nodes_from(intersect) + exclusions += intersect + + return new_graph, solutions, sign, exclusions As you might have noticed in the code above, we add the nodes that are included into (respective excluded from) the solution to a list ``solutions`` (``exclusions``). This allows us to directly :ref:`recycle the QAOA code ` for the ``cost_operator``, ``mixer`` and ``init_function`` of the original QAOA implementation with minor adjustments. @@ -217,7 +217,7 @@ With this, we can directly throw everything that is relevant at the :ref:`QIROPr init_function=qiro_init_function ) -We think of arguments for the ``run_qiro`` function, run the algorithm, et violà! +We think of arguments for the :meth:`.run_qiro ` method, run the algorithm, et violà! :: @@ -251,39 +251,21 @@ We can further compare our results to the `NetworkX MIS algorithm `, -* :ref:`MaxSat ` and +* :ref:`MaxClique ` +* :ref:`MaxSat ` * :ref:`MaxSetPacking ` and of course all the other material in the tutorial section! diff --git a/documentation/source/reference/Algorithms/index.rst b/documentation/source/reference/Algorithms/index.rst index 7dbc9b96..c043a2b4 100644 --- a/documentation/source/reference/Algorithms/index.rst +++ b/documentation/source/reference/Algorithms/index.rst @@ -31,8 +31,8 @@ We encourage you to explore these algorithms, delve into their documentation, an :maxdepth: 2 :hidden: - qaoa/index - QIRO + qaoa/QAOA + qiro/QIRO Shor Grover QuantumBacktrackingTree diff --git a/documentation/source/reference/Algorithms/qaoa/index.rst b/documentation/source/reference/Algorithms/qaoa/QAOA.rst similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/index.rst rename to documentation/source/reference/Algorithms/qaoa/QAOA.rst diff --git a/documentation/source/reference/Algorithms/qaoa/QAOAImplementations.rst b/documentation/source/reference/Algorithms/qaoa/QAOAImplementations.rst index 7275ece9..b0dabd9b 100644 --- a/documentation/source/reference/Algorithms/qaoa/QAOAImplementations.rst +++ b/documentation/source/reference/Algorithms/qaoa/QAOAImplementations.rst @@ -8,15 +8,15 @@ Implementations .. toctree:: :hidden: - qaoaProblems/qaoa.problems.maxCut - qaoaProblems/qaoa.problems.maxSat - qaoaProblems/qaoa.problems.eThrLinTwo - qaoaProblems/qaoa.problems.QUBO - qaoaProblems/qaoa.problems.maxIndepSet - qaoaProblems/qaoa.problems.maxClique - qaoaProblems/qaoa.problems.maxSetPack - qaoaProblems/qaoa.problems.minSetCover - qaoaProblems/qaoa.problems.maxKColorableSubgraph + implementations/maxCut + implementations/maxSat + implementations/eThrLinTwo + implementations/QUBO + implementations/maxIndepSet + implementations/maxClique + implementations/maxSetPack + implementations/minSetCover + implementations/maxKColorableSubgraph Our voyage into :ref:`MaxCut ` and :ref:`Max-$\\kappa$-Colorable Subgraph ` problems is detailed in the :ref:`tutorial ` section. diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.QUBO.rst b/documentation/source/reference/Algorithms/qaoa/implementations/QUBO.rst similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.QUBO.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/QUBO.rst diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.eThrLinTwo.rst b/documentation/source/reference/Algorithms/qaoa/implementations/eThrLinTwo.rst similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.eThrLinTwo.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/eThrLinTwo.rst diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/mKCS.png b/documentation/source/reference/Algorithms/qaoa/implementations/mKCS.png similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/mKCS.png rename to documentation/source/reference/Algorithms/qaoa/implementations/mKCS.png diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/maxClique.png b/documentation/source/reference/Algorithms/qaoa/implementations/maxClique.png similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/maxClique.png rename to documentation/source/reference/Algorithms/qaoa/implementations/maxClique.png diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxClique.rst b/documentation/source/reference/Algorithms/qaoa/implementations/maxClique.rst similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxClique.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/maxClique.rst diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/maxCut.png b/documentation/source/reference/Algorithms/qaoa/implementations/maxCut.png similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/maxCut.png rename to documentation/source/reference/Algorithms/qaoa/implementations/maxCut.png diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxCut.rst b/documentation/source/reference/Algorithms/qaoa/implementations/maxCut.rst similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxCut.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/maxCut.rst diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/maxIndepSet.png b/documentation/source/reference/Algorithms/qaoa/implementations/maxIndepSet.png similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/maxIndepSet.png rename to documentation/source/reference/Algorithms/qaoa/implementations/maxIndepSet.png diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxIndepSet.rst b/documentation/source/reference/Algorithms/qaoa/implementations/maxIndepSet.rst similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxIndepSet.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/maxIndepSet.rst diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxKColorableSubgraph.rst b/documentation/source/reference/Algorithms/qaoa/implementations/maxKColorableSubgraph.rst similarity index 95% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxKColorableSubgraph.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/maxKColorableSubgraph.rst index b5d309a4..b3f17be9 100644 --- a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxKColorableSubgraph.rst +++ b/documentation/source/reference/Algorithms/qaoa/implementations/maxKColorableSubgraph.rst @@ -15,7 +15,7 @@ Here, we provide a condensed implementation of QAOA for M$\kappa$CS using all of Problem description ------------------- -Given a Graph :math:`G = (V,E)` find and $\kappa$ colors, maximize the size (number of edges) of a properly colored subgraph. +Given a Graph :math:`G = (V,E)` and $\kappa$ colors, maximize the size (number of edges) of a properly colored subgraph. Cost operator ------------- diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSat.rst b/documentation/source/reference/Algorithms/qaoa/implementations/maxSat.rst similarity index 91% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSat.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/maxSat.rst index 4f3a89bf..616b4b4b 100644 --- a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSat.rst +++ b/documentation/source/reference/Algorithms/qaoa/implementations/maxSat.rst @@ -10,7 +10,7 @@ QAOA MaxSat Problem description ------------------- -Given :math:`m` disjunctive clauses over :math:`n` Boolean variables :math:`x`, where each clause +Given :math:`m` disjunctive clauses over :math:`n` Boolean variables :math:`x_1,\dotsc,x_n`, where each clause contains at most :math:`l \geq 2` literals, find a variable assignment that maximizes the number of satisfied clauses. @@ -69,12 +69,13 @@ Example implementation from qrisp.qaoa import QAOAProblem, RX_mixer, create_maxsat_cl_cost_function, create_maxsat_cost_operator clauses = [[1,2,-3],[1,4,-6],[4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6]] - num_variables = 6 - qarg = QuantumVariable(num_variables) + num_vars = 6 + problem = (num_vars, clauses) + qarg = QuantumVariable(num_vars) - qaoa_max_indep_set = QAOAProblem(cost_operator=create_maxsat_cost_operator(clauses), + qaoa_max_indep_set = QAOAProblem(cost_operator=create_maxsat_cost_operator(problem), mixer=RX_mixer, - cl_cost_function=create_maxsat_cl_cost_function(clauses)) + cl_cost_function=create_maxsat_cl_cost_function(problem)) results = qaoa_max_indep_set.run(qarg=qarg, depth=5) That's it! Feel free to experiment with the ``init_type='tqa'`` option in the :meth:`.run ` method for improved performance. @@ -82,7 +83,7 @@ In the following, we print the 5 most likely solutions together with their cost :: - cl_cost = create_maxsat_cl_cost_function(clauses) + cl_cost = create_maxsat_cl_cost_function(problem) print("5 most likely solutions") max_five = sorted(results.items(), key=lambda item: item[1], reverse=True)[:5] diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSetPack.rst b/documentation/source/reference/Algorithms/qaoa/implementations/maxSetPack.rst similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.maxSetPack.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/maxSetPack.rst diff --git a/documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.minSetCover.rst b/documentation/source/reference/Algorithms/qaoa/implementations/minSetCover.rst similarity index 100% rename from documentation/source/reference/Algorithms/qaoa/qaoaProblems/qaoa.problems.minSetCover.rst rename to documentation/source/reference/Algorithms/qaoa/implementations/minSetCover.rst diff --git a/documentation/source/reference/Algorithms/QIRO.rst b/documentation/source/reference/Algorithms/qiro/QIRO.rst similarity index 65% rename from documentation/source/reference/Algorithms/QIRO.rst rename to documentation/source/reference/Algorithms/qiro/QIRO.rst index 2a835567..36714d90 100644 --- a/documentation/source/reference/Algorithms/QIRO.rst +++ b/documentation/source/reference/Algorithms/qiro/QIRO.rst @@ -9,11 +9,11 @@ Quantum Informed Recursive Optimization .. toctree:: :hidden: - qiroProblems/QIROProblem - qiroProblems/QIROImplemens + QIROProblem + QIROImplementations -An algorithm to facilitate the functionality of Quantum Informed Recursive Optimizations, as developed by J. Finzgar et. al. in `Quantum-Informed Recursive Optimization Algorithms (2023) `_ . +An algorithm to facilitate the functionality of Quantum Informed Recursive Optimizations, as developed by J. Finzgar et. al. in `Quantum-Informed Recursive Optimization Algorithms (2023) `_ . It is based on updating the problem instance based on correlations, that are in turn established with a QAOA protocol. For further info have a look at our :ref:`tutorial on QIRO! ` @@ -24,16 +24,15 @@ The central data structure of the QIRO module is the :ref:`QIROProblem` class. The :ref:`QIROProblem` encapsulates the required prerequesites to run the algorithm: -* The ``problem`` to be solved, which is not necessarly a graph, -* the ``replacement_routine``, which has the job of performing the aforementioned specific reductions to the ``problem`` object, -* The ``cost_operator``, ``mixer``, ``init_function`` and ``cl_cost_function`` in analogy to :ref:`QAOAProblem` instanciation. +* The ``problem`` to be solved, which is not necessarly a graph. +* The ``replacement_routine``, which has the job of performing the aforementioned specific reductions to the ``problem`` object. +* The ``cost_operator``, ``mixer``, ``init_function`` and ``cl_cost_function`` in analogy to :ref:`QAOAProblem` instantiation. .. _QIROMiXers: -Collection of mixers and suplementary material ----------------------------------------------- -Qrisp comes with a variety of predefined mixers to tackle various types of problem instances: +Collection of mixers and auxiliary functions +-------------------------------------------- .. autosummary:: @@ -45,7 +44,7 @@ Qrisp comes with a variety of predefined mixers to tackle various types of probl QIRO implementations of problem instances ----------------------------------------- -For implemented problem instances see :ref:`the QIRO implementations page ` +For implemented problem instances see :ref:`the QIRO implementations page ` diff --git a/documentation/source/reference/Algorithms/qiro/QIROImplementations.rst b/documentation/source/reference/Algorithms/qiro/QIROImplementations.rst new file mode 100644 index 00000000..ab675d2c --- /dev/null +++ b/documentation/source/reference/Algorithms/qiro/QIROImplementations.rst @@ -0,0 +1,30 @@ + +.. _QIROImplementations: + +.. currentmodule:: qrisp.qiro + +Implementations +=============== + +.. toctree:: + :hidden: + + implementations/QIROMaxClique + implementations/QIROMaxIndep + implementations/QIROMaxSat + implementations/QIROMaxSetPack + + +Here, you can find the QIRO implementations for specific problem instances, namely: + + +* :ref:`MaxIndependentSet ` +* :ref:`MaxClique ` +* :ref:`MaxSat ` +* :ref:`MaxSetPacking ` + + + + + + diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROProblem.rst b/documentation/source/reference/Algorithms/qiro/QIROProblem.rst similarity index 86% rename from documentation/source/reference/Algorithms/qiroProblems/QIROProblem.rst rename to documentation/source/reference/Algorithms/qiro/QIROProblem.rst index 9dc2130c..927c37cf 100644 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROProblem.rst +++ b/documentation/source/reference/Algorithms/qiro/QIROProblem.rst @@ -2,12 +2,13 @@ .. currentmodule:: qrisp.qiro + QIROProblem =========== -.. currentmodule:: qrisp.qiro .. autoclass:: QIROProblem + Methods ======= diff --git a/documentation/source/reference/Algorithms/qiroProblems/generated/qrisp.qiro.QIROProblem.run_qiro.rst b/documentation/source/reference/Algorithms/qiro/generated/qrisp.qiro.QIROProblem.run_qiro.rst similarity index 100% rename from documentation/source/reference/Algorithms/qiroProblems/generated/qrisp.qiro.QIROProblem.run_qiro.rst rename to documentation/source/reference/Algorithms/qiro/generated/qrisp.qiro.QIROProblem.run_qiro.rst diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst similarity index 100% rename from documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxClique.rst rename to documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxIndep.rst similarity index 99% rename from documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst rename to documentation/source/reference/Algorithms/qiro/implementations/QIROMaxIndep.rst index 3a32ae90..29e211e3 100644 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxIndep.rst +++ b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxIndep.rst @@ -6,7 +6,6 @@ QIRO MaxIndepSet .. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxIndepSet - Problem description ------------------- diff --git a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSat.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSat.rst new file mode 100644 index 00000000..997db168 --- /dev/null +++ b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSat.rst @@ -0,0 +1,100 @@ +.. _maxSatQIRO: + +QIRO MaxSat +=========== + +.. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxSat + + +Problem description +------------------- + +Given :math:`m` disjunctive clauses over :math:`n` Boolean variables :math:`x_1,\dotsc,x_n`, where each clause +contains at most :math:`l \geq 2` literals, find a variable assignment that maximizes the number of +satisfied clauses. + +For example, for $l=3$ and $n=5$ Boolean variables $x_1,\dotsc,x_5$ consider the clauses + +$$(x_1\\lor x_2\\lor x_3), (\\bar x_1\\lor x_2\\lor x_4), (\\bar x_2\\lor x_3\\lor \\bar x_5), (\\bar x_3\\lor x_4\\lor x_5)$$ + +where $\bar x_i$ denotes the negation of $x_i$. What is sought now is an assignment of the variables with 0 (False) or 1 (True) +that maximizes the number of satisfied clauses. +In this case, the assignment $x=x_1x_2x_3x_4x_5=01111$ satisfies all clauses. + +Given $n$ variables $x_1,\dotsc,x_n$ we represent each clause as a list of integers such that the absolute values correspond to the indices of the variables and a minus sign corresponds to negation of the variable. +For example, the above clauses correspond to + +$$[1,2,3],\\quad [-1,2,4],\\quad [-2,3,-5],\\quad [-3,4,5]$$ + +The cost function for the maximum satisfyability problem is given by + +$$C(x)=\\sum_{\\alpha=1}^mC_{\\alpha}(x)$$ + +where for each clause $C_{\alpha}(x)=1$ if the corresponding clause is satisfied and $C_{\alpha}(x)=0$ otherwise. Here, + +$$C_{\\alpha}(x)=1-\\prod_{j>0}(1-x_j)\\prod_{j<0}x_{-j}$$ + + +Replacement routine +------------------- + +The replacements work based on the problem encoding. We discard clauses, based on the truth value assignments as those are what is described by the qubits. +Based on the **maximum absolute entry** of the correlation matrix M and its sign, one of the following replacements is employed: + +* If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th value is set to be true. In turn, we can remove all clauses that negate that value. + +* If :math:`\text{M}_{ii} < 0` is the maximum absolute value we remove :math:`i`-th value is set to be false and we will exclude clause that include that value. + +* If :math:`\text{M}_{ij} > 0, (i, j) ∈ E` was selected, we transfer this correlation to the clauses, by replacing the first item with the second one. + +* If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we transfer this correlation to the clauses, by replacing the first item with the **negation** of the second one. + + + +.. autofunction:: create_maxSat_replacement_routine + + +QIRO Cost operator +------------------ + +.. autofunction:: create_maxSat_cost_operator_reduced + + +Example implementation +---------------------- + +:: + + from qrisp import QuantumVariable + from qrisp.qaoa import create_maxsat_cl_cost_function + from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxSat_replacement_routine, create_maxSat_cost_operator_reduced + + clauses = [[1,2,-3],[1,4,-6],[4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6],[1,7,8],[3,-7,-8],[3,4,8],[4,5,8],[1,2,7]] + num_vars = 8 + problem = (num_vars, clauses) + qarg = QuantumVariable(num_vars) + + qiro_instance = QIROProblem(problem = problem, + replacement_routine = create_maxSat_replacement_routine, + cost_operator = create_maxSat_cost_operator_reduced, + mixer = qiro_RXMixer, + cl_cost_function = create_maxsat_cl_cost_function, + init_function = qiro_init_function + ) + res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2) + +That’s it! In the following, we print the 5 most likely solutions together with their cost values, and the final clauses. + +:: + + cl_cost = create_maxsat_cl_cost_function(problem) + + print("5 most likely QIRO solutions") + max_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] + for res in max_five_qiro: + print(res) + print(cl_cost({res : 1})) + + final_clauses = qiro_instance.problem + print("final clauses") + print(final_clauses) \ No newline at end of file diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSetPack.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSetPack.rst similarity index 100% rename from documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSetPack.rst rename to documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSetPack.rst diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplemens.rst b/documentation/source/reference/Algorithms/qiroProblems/QIROImplemens.rst deleted file mode 100644 index cf2a63ba..00000000 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROImplemens.rst +++ /dev/null @@ -1,30 +0,0 @@ - -.. _QIROImplemens: - -.. currentmodule:: qrisp.qiro - -Implementations -=============== - -.. toctree:: - :hidden: - - QIROImplementations/QIROMaxClique - QIROImplementations/QIROMaxIndep - QIROImplementations/QIROMaxSat - QIROImplementations/QIROMaxSetPack - - -Here you can see the QIRO implementations for specific problem instances, namely: - - -* :ref:`MaxIndependentSet `, -* :ref:`MaxClique `, -* :ref:`MaxSat ` and -* :ref:`MaxSetPacking ` - - - - - - diff --git a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSat.rst b/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSat.rst deleted file mode 100644 index d29a5603..00000000 --- a/documentation/source/reference/Algorithms/qiroProblems/QIROImplementations/QIROMaxSat.rst +++ /dev/null @@ -1,92 +0,0 @@ -.. _maxSatQIRO: - -QIRO MaxSat -=========== - -.. currentmodule:: qrisp.qiro.qiroproblems.qiroMaxSatInfrastr - - -Problem description -------------------- - -Given :math:`m` disjunctive clauses over :math:`n` Boolean variables :math:`x` , where each clause -contains at most :math:`l \geq 2` literals, find a variable assignment that maximizes the number of -satisfied clauses. - - - -Replacement routine -------------------- - -The replacements work based on the problem encoding. We look throw out clauses, based on the truth value assigments as those are what is described by the qubits. -Based on the **maximum absolute entry** of the correlation matrix M and its sign, one of the following replacements is employed: - -* If :math:`\text{M}_{ii} \geq 0` is the maximum absolute value, then the :math:`i`-th value is set to be true. In turn, we can remove all clauses that negate that value. - -* If :math:`\text{M}_{ii} < 0` is the maximum absolute value we remove :math:`i`-th value is set to be false and we will exclude clause that include that value. - -* If :math:`\text{M}_{ij} > 0, (i, j) ∈ E` was selected, we transfer this correlation to the clauses, by replacing the first item with the second one. - -* If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we transfer this correlation to the clauses, by replacing the first item with the **negation** of the second one. - - - -.. autofunction:: create_maxSat_replacement_routine - - -QIRO Cost operator ------------------- - -.. autofunction:: create_maxSat_cost_operator_reduced - - -Full Example implementation: ----------------------------- -:: - - # imports - from qrisp.qaoa.problems.maxSatInfrastr import maxSatclCostfct, clausesdecoder - from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxSat_replacement_routine, create_maxSat_cost_operator_reduced - from qrisp import QuantumVariable - import matplotlib.pyplot as plt - import networkx as nx - - # define the problem according to maxSat encoding - problem = [8 , [[1,2,-3],[1,4,-6], [4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6], [1,7,8], [3,-7,-8], [3,4,8],[4,5,8], [1,2,7]]] - decodedClauses = clausesdecoder( problem) - - qarg = QuantumVariable(problem[0]) - - - # assign cost_function and maxclique_instance, normal QAOA - testCostFun = maxSatclCostfct(problem) - - # assign the correct new update functions for qiro from above imports - qiro_instance = QIROProblem(problem = problem, - replacement_routine = create_maxSat_replacement_routine, - cost_operator = create_maxSat_cost_operator_reduced, - mixer = qiro_RXMixer, - cl_cost_function = maxSatclCostfct, - init_function = qiro_init_function - ) - - - # We run the qiro instance and get the results! - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2, - #mes_kwargs = mes_kwargs - ) - - print("QIRO 5 best results") - maxfive = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] - for key, val in res_qiro.items(): - if key in maxfive: - - print(key) - print(testCostFun({key:1})) - - # or compare it with the networkx result of the max_clique algorithm... - # or a write brute force solution, this is up to you - - # and finally, we print the final clauses! - final_clauses = qiro_instance.problem - print(final_clauses) diff --git a/src/qrisp/algorithms/qaoa/problems/eThrLinTwo.py b/src/qrisp/algorithms/qaoa/problems/eThrLinTwo.py index 96ef343b..ef7d65a4 100644 --- a/src/qrisp/algorithms/qaoa/problems/eThrLinTwo.py +++ b/src/qrisp/algorithms/qaoa/problems/eThrLinTwo.py @@ -16,7 +16,7 @@ ********************************************************************************/ """ -from qrisp import h, cx, rz, conjugate +from qrisp import cx, rz, conjugate def parity(qarg, indices): @@ -36,7 +36,7 @@ def create_e3lin2_cost_operator(clauses): Parameters ---------- - clasues : list[list[int]] + clauses : list[list[int]] The clasues defining the problem. Returns @@ -61,7 +61,7 @@ def create_e3lin2_cl_cost_function(clauses): Parameters ---------- - clasues : list[list[int]] + clauses : list[list[int]] The clasues defining the problem. Returns @@ -103,6 +103,5 @@ def e3lin2_problem(clauses): return QAOAProblem(cost_operator=create_e3lin2_cost_operator(clauses), mixer=RX_mixer, - cl_cost_function=create_e3lin2_cl_cost_function(clauses), - init_function=e3lin2_init_function) + cl_cost_function=create_e3lin2_cl_cost_function(clauses)) diff --git a/src/qrisp/algorithms/qaoa/problems/maxClique.py b/src/qrisp/algorithms/qaoa/problems/maxClique.py index 40434a4b..0c749246 100644 --- a/src/qrisp/algorithms/qaoa/problems/maxClique.py +++ b/src/qrisp/algorithms/qaoa/problems/maxClique.py @@ -24,7 +24,7 @@ def create_max_clique_cl_cost_function(G): """ - Creates the classical cost function for an instance of the maximum clique problem for a given graph $G$. + Creates the classical cost function for an instance of the maximum clique problem for a given graph ``G``. Parameters ---------- diff --git a/src/qrisp/algorithms/qaoa/problems/maxCut.py b/src/qrisp/algorithms/qaoa/problems/maxCut.py index bb857367..4f3e8107 100644 --- a/src/qrisp/algorithms/qaoa/problems/maxCut.py +++ b/src/qrisp/algorithms/qaoa/problems/maxCut.py @@ -49,7 +49,7 @@ def maxcut_energy(outcome_array, count_array, edge_list): def create_maxcut_cl_cost_function(G): """ - Creates the classical cost function for an instance of the maximum cut problem for a given graph $G$. + Creates the classical cost function for an instance of the maximum cut problem for a given graph ``G``. Parameters ---------- @@ -89,7 +89,7 @@ def cl_cost_function(counts): def create_maxcut_cost_operator(G): r""" - Creates the cost operator for an instance of the maximum cut problem for a given graph $G$. + Creates the cost operator for an instance of the maximum cut problem for a given graph ``G``. Parameters ---------- diff --git a/src/qrisp/algorithms/qaoa/problems/maxIndepSet.py b/src/qrisp/algorithms/qaoa/problems/maxIndepSet.py index 716c0d0e..5aa2a10f 100644 --- a/src/qrisp/algorithms/qaoa/problems/maxIndepSet.py +++ b/src/qrisp/algorithms/qaoa/problems/maxIndepSet.py @@ -23,7 +23,7 @@ def create_max_indep_set_mixer(G): r""" - Creates the ``controlled_RX_mixer`` for an instance of the maximal independet set problem for a given graph $G$ following `Hadfield et al. `_ + Creates the ``controlled_RX_mixer`` for an instance of the maximal independet set problem for a given graph ``G`` following `Hadfield et al. `_ The belonging ``predicate`` function indicates if a set can be swapped into the solution. @@ -35,8 +35,8 @@ def create_max_indep_set_mixer(G): Returns ------- function - A Python function receiving a ``QuantumVariable`` and real parameter $\beta$. - This function performs the application of the mixer associated to the graph $G$. + A Python function receiving a :ref:`QuantumVariable` and real parameter $\beta$. + This function performs the application of the mixer associated to the graph ``G``. """ @@ -57,7 +57,7 @@ def predicate(qv,i): def create_max_indep_set_cl_cost_function(G): """ - Creates the classical cost function for an instance of the maximal independet set problem for a given graph $G$. + Creates the classical cost function for an instance of the maximal independet set problem for a given graph ``G``. Parameters ---------- diff --git a/src/qrisp/algorithms/qaoa/problems/maxKColorableSubgraph.py b/src/qrisp/algorithms/qaoa/problems/maxKColorableSubgraph.py index 9aa4c017..0ac38f84 100644 --- a/src/qrisp/algorithms/qaoa/problems/maxKColorableSubgraph.py +++ b/src/qrisp/algorithms/qaoa/problems/maxKColorableSubgraph.py @@ -188,7 +188,7 @@ def apply_phase_if_eq(qcolor_0, qcolor_1, gamma): def create_coloring_operator(G): r""" - Generates the cost operator for an instance of the coloring problem for a given graph $G$. + Generates the cost operator for an instance of the coloring problem for a given graph ``G``. The coloring operator and applies a phase if two neighboring nodes in the graph have the same color. @@ -227,7 +227,7 @@ def mkcs_obj(quantumcolor_array, G): def create_coloring_cl_cost_function(G): """ - Creates the classical cost function for an instance of the coloring problem for a given graph $G$. + Creates the classical cost function for an instance of the coloring problem for a given graph ``G``. Parameters ---------- diff --git a/src/qrisp/algorithms/qaoa/problems/maxSat.py b/src/qrisp/algorithms/qaoa/problems/maxSat.py index 1079bf6a..31dfc387 100644 --- a/src/qrisp/algorithms/qaoa/problems/maxSat.py +++ b/src/qrisp/algorithms/qaoa/problems/maxSat.py @@ -16,19 +16,19 @@ ********************************************************************************/ """ -from qrisp import h, app_sb_phase_polynomial +from qrisp import app_sb_phase_polynomial import sympy as sp import math -def create_maxsat_cost_polynomials(clauses): +def create_maxsat_cost_polynomials(problem): """ Creates a list of polynomials representing the cost function for each clause, and a list of symbols. Parameters ---------- - clauses : list[list[int]] - The clauses of the maximum satisfiability problem instance. + problem : tuple(int, list[list[int]]) + The number of variables, and the clauses of the maximum satisfiability problem instance. Returns ------- @@ -39,8 +39,8 @@ def create_maxsat_cost_polynomials(clauses): """ - all_indices = list(set(abs(index) for clause in clauses for index in clause)) - symbols = [sp.Symbol(f"x{i}") for i in range(1,len(all_indices)+1)] + clauses = problem[1] + symbols = [sp.Symbol(f"x{i}") for i in range(1,problem[0]+1)] cost_polynomials = [] for clause in clauses: C = 1 - sp.prod((1-symbols[index-1]) if index>0 else symbols[-index-1] for index in clause) @@ -49,14 +49,14 @@ def create_maxsat_cost_polynomials(clauses): return cost_polynomials, symbols -def create_maxsat_cl_cost_function(clauses): +def create_maxsat_cl_cost_function(problem): """ Creates the classical cost function for an instance of the maximum satisfiability problem. Parameters ---------- - clauses : list[list[int]] - The clauses of the maximum satisfiability problem instance. + problem : tuple(int, list[list[int]]) + The number of variables, and the clauses of the maximum satisfiability problem instance. Returns ------- @@ -65,6 +65,7 @@ def create_maxsat_cl_cost_function(clauses): """ + clauses = problem[1] def cl_cost_function(res_dic): cost = 0 for state, prob in res_dic.items(): @@ -76,7 +77,7 @@ def cl_cost_function(res_dic): return cl_cost_function -def create_maxsat_cost_operator(clauses): +def create_maxsat_cost_operator(problem): r""" Creates the cost operator for an instance of the maximum satisfiability problem. For a given cost function @@ -89,45 +90,44 @@ def create_maxsat_cost_operator(clauses): Parameters ---------- - clauses : list[list[int]] - The clauses of the maximum satisfiability problem instance. + problem : tuple(int, list[list[int]]) + The number of variables, and the clauses of the maximum satisfiability problem instance. Returns ------- cost_operator : function - A function receiving a :ref:`QuantumVariable` and a real parameter $\beta$. + A function receiving a :ref:`QuantumVariable` and a real parameter $\gamma$. This function performs the application of the cost operator. """ - cost_polynomials, symbols = create_maxsat_cost_polynomials(clauses) + cost_polynomials, symbols = create_maxsat_cost_polynomials(problem) - def cost_operator(qv, beta): + def cost_operator(qv, gamma): for P in cost_polynomials: - app_sb_phase_polynomial([qv], -P, symbols, t=beta) + app_sb_phase_polynomial([qv], -P, symbols, t=gamma) return cost_operator -def maxsat_problem(clauses): +def maxsat_problem(problem): """ Creates a QAOA problem instance with appropriate phase separator, mixer, and classical cost function. Parameters ---------- - clauses : list[list[int]] - The clauses of the maximum satisfiability problem instance. + problem : tuple(int, list[list[int]]) + The number of variables, and the clauses of the maximum satisfiability problem instance. Returns ------- :ref:`QAOAProblem` - A QAOA problem instance for MaxSat for given ``clauses``. + A QAOA problem instance for MaxSat for given a ``problem``. """ from qrisp.qaoa import QAOAProblem, RX_mixer - return QAOAProblem(cost_operator=create_maxsat_cost_operator(clauses), + return QAOAProblem(cost_operator=create_maxsat_cost_operator(problem), mixer=RX_mixer, - cl_cost_function=create_maxsat_cl_cost_function(clauses), - init_function=maxsat_init_function) \ No newline at end of file + cl_cost_function=create_maxsat_cl_cost_function(problem)) \ No newline at end of file diff --git a/src/qrisp/algorithms/qaoa/problems/minSetCover.py b/src/qrisp/algorithms/qaoa/problems/minSetCover.py index 3b96e42a..11247980 100644 --- a/src/qrisp/algorithms/qaoa/problems/minSetCover.py +++ b/src/qrisp/algorithms/qaoa/problems/minSetCover.py @@ -36,7 +36,7 @@ def create_min_set_cover_mixer(sets, universe): Returns ------- function - A Python function receiving a ``QuantumVariable`` and real parameter $\beta$. + A Python function receiving a :ref:`QuantumVariable` and real parameter $\beta$. This function performs the application of the mixer associated to the problem instance. """ diff --git a/src/qrisp/algorithms/qiro/qiro_mixers.py b/src/qrisp/algorithms/qiro/qiro_mixers.py index ab45af0a..d41a020a 100644 --- a/src/qrisp/algorithms/qiro/qiro_mixers.py +++ b/src/qrisp/algorithms/qiro/qiro_mixers.py @@ -47,7 +47,7 @@ def RX_mixer(qv, beta): def qiro_init_function(solutions = [], exclusions = []): """ - State initiation function for QIRO algorithm. Works analogously to the normal initiation function, but respects solutions and exclusions that have been found in the QIRO reduction steps. + State initialization function for QIRO algorithm. Works analogously to the normal initialization function, but respects solutions and exclusions that have been found in the QIRO reduction steps. Parameters ---------- diff --git a/src/qrisp/algorithms/qiro/qiroproblems/__init__.py b/src/qrisp/algorithms/qiro/qiroproblems/__init__.py index 3ad1f8ae..38183506 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/__init__.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/__init__.py @@ -19,6 +19,6 @@ from qrisp.algorithms.qiro.qiroproblems.qiroMaxClique import * from qrisp.algorithms.qiro.qiroproblems.qiroMaxIndepSet import * -from qrisp.algorithms.qiro.qiroproblems.qiroMaxSatInfrastr import * +from qrisp.algorithms.qiro.qiroproblems.qiroMaxSat import * from qrisp.algorithms.qiro.qiroproblems.qiroMaxSetPack import * diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py index c1658aaf..02a8a99f 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py @@ -31,7 +31,7 @@ def create_maxClique_replacement_routine(res, graph, solutions, exclusions): Parameters ---------- res : dict - Result dictionary of initial QAOA optimization procedure. + Result dictionary of QAOA optimization procedure. graph : nx.Graph The graph defining the problem instance. solutions : list @@ -41,7 +41,7 @@ def create_maxClique_replacement_routine(res, graph, solutions, exclusions): Returns ------- - newgraph : nx.Graph + new_graph : nx.Graph Updated graph for the problem instance. solutions : list Updated set of solutions to the problem. @@ -58,7 +58,7 @@ def create_maxClique_replacement_routine(res, graph, solutions, exclusions): #get the max_edge and eval the sum and sign max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) - newgraph = copy.deepcopy(graph) + new_graph = copy.deepcopy(graph) # we just directly remove vertices from the graph if isinstance(max_item, int): @@ -66,13 +66,13 @@ def create_maxClique_replacement_routine(res, graph, solutions, exclusions): border = list(graph.adj[max_item].keys()) border.append(max_item) to_remove = [int(item) for item in graph.nodes() if item not in border] - newgraph.remove_nodes_from( [item for item in graph.nodes() if item not in border]) + new_graph.remove_nodes_from( [item for item in graph.nodes() if item not in border]) solutions.append(max_item) exclusions += to_remove elif sign < 0: #remove item - newgraph.remove_node(max_item) + new_graph.remove_node(max_item) exclusions.append(max_item) else: @@ -83,7 +83,7 @@ def create_maxClique_replacement_routine(res, graph, solutions, exclusions): intersect.append(max_item[0]) intersect.append(max_item[1]) to_remove = [int(item) for item in graph.nodes() if item not in intersect] - newgraph.remove_nodes_from([item for item in graph.nodes() if item not in intersect]) + new_graph.remove_nodes_from([item for item in graph.nodes() if item not in intersect]) solutions.append(max_item[0]) solutions.append(max_item[1]) exclusions += to_remove @@ -96,10 +96,10 @@ def create_maxClique_replacement_routine(res, graph, solutions, exclusions): union.append(max_item[1]) to_remove = [int(item) for item in graph.nodes() if item not in union] #to_delete = [item for item in graph.nodes() if item not in union] - newgraph.remove_nodes_from([item for item in graph.nodes() if item not in union]) + new_graph.remove_nodes_from([item for item in graph.nodes() if item not in union]) exclusions += to_remove - return newgraph, solutions, sign, exclusions + return new_graph, solutions, sign, exclusions def create_maxClique_cost_operator_reduced(graph, solutions=[]): diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py index 72cba148..07a4527d 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py @@ -30,7 +30,7 @@ def create_maxIndep_replacement_routine(res, graph, solutions=[], exclusions=[]) Parameters ---------- res : dict - Result dictionary of initial QAOA optimization procedure. + Result dictionary of QAOA optimization procedure. graph : nx.Graph The graph defining the problem instance. solutions : list @@ -40,7 +40,7 @@ def create_maxIndep_replacement_routine(res, graph, solutions=[], exclusions=[]) Returns ------- - newgraph : nx.Graph + new_graph : nx.Graph Updated graph for the problem instance. solutions : list Updated set of solutions to the problem. @@ -61,7 +61,7 @@ def create_maxIndep_replacement_routine(res, graph, solutions=[], exclusions=[]) max_item, sign = find_max(orig_nodes, orig_edges , res, solutions) # create a copy of the graph - newgraph = copy.deepcopy(graph) + new_graph = copy.deepcopy(graph) # we remove nodes from the graph, as suggested by the replacement rules # if the item is an int, it is a single node, else it is an edge @@ -69,28 +69,28 @@ def create_maxIndep_replacement_routine(res, graph, solutions=[], exclusions=[]) if sign > 0: # remove all adjacent nodes to_remove = graph.adj[max_item] - newgraph.remove_nodes_from(to_remove) + new_graph.remove_nodes_from(to_remove) solutions.append(max_item) exclusions += to_remove elif sign < 0: # remove the node - newgraph.remove_node(max_item) + new_graph.remove_node(max_item) exclusions.append(max_item) else: if sign > 0: # remove both nodes - newgraph.remove_nodes_from(max_item) + new_graph.remove_nodes_from(max_item) exclusions += list(max_item) elif sign < 0: # remove all nodes connected to both nodes intersect = list(set( list(graph.adj[max_item[0]].keys()) ) & set( list(graph.adj[max_item[0]].keys()) )) - newgraph.remove_nodes_from(intersect) + new_graph.remove_nodes_from(intersect) exclusions += intersect - return newgraph, solutions, sign, exclusions + return new_graph, solutions, sign, exclusions def create_maxIndep_cost_operator_reduced(graph, solutions=[]): diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSatInfrastr.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSat.py similarity index 76% rename from src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSatInfrastr.py rename to src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSat.py index 1ba846d7..4f8b58eb 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSatInfrastr.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSat.py @@ -20,44 +20,42 @@ from qrisp import rz, x, cx import numpy as np +import math import copy from qrisp.algorithms.qiro.qiroproblems.qiro_utils import * -def create_maxSat_replacement_routine( res, oldproblem, solutions, exclusions): - + +def create_maxSat_replacement_routine(res, problem, solutions, exclusions): """ - Creates a replacement routine for the problem structure, i.e. defines the replacement rules. - See the original paper for description of the update rules + Creates a replacement routine for the problem structure, i.e., defines the replacement rules. + See the `original paper `_ for a description of the update rules. Parameters ---------- res : dict - Result dictionary of initial QAOA optimization procedure. - oldproblem : List - The clauses defining the problem instance. - solutions : List - Qubits which have been found to be positive correlated, i.e. part of the problem solution. - exclusions : List - Qubits which have been found to be negative correlated, i.e. not part of the problem solution, or contradict solution qubits in accordance to the update rules. + Result dictionary of QAOA optimization procedure. + problem : tuple(int, list[list[int]]) + The number of variables and a list of clauses for the MaxSat instance. + solutions : list + Qubits which were found to be positively correlated, i.e., part of the problem solution. + exclusions : list + Qubits which were found to be negatively correlated, i.e., not part of the problem solution, or contradict solution qubits in accordance with the update rules. Returns ------- new_problem: list Updated clauses of the problem instance. - solutions : List + solutions : list Updated set of solutions to the problem. - sign : Int + sign : int The sign of the correlation. - exclusions : List + exclusions : list Updated set of exclusions to the problem. - - - """ - numVars = oldproblem[0] - clauses = copy.deepcopy(oldproblem[1]) + numVars = problem[0] + clauses = copy.deepcopy(problem[1]) # FOR SINGLE QUBIT CORRELATIONS # make -1 here for consistency in the find max @@ -125,34 +123,20 @@ def create_maxSat_replacement_routine( res, oldproblem, solutions, exclusions): def create_maxSat_cost_operator_reduced(problem, solutions=[]): """ - | Implementation for MaxSat Cost-Operator, in accordance to the original QAOA-paper. - | Adjusted for QIRO to also respect found solutions to the problem + Creates the ``cost_operator`` for the problem instance. + This operator is adjusted to consider qubits that were found to be a part of the problem solution. Parameters ---------- - clauses : List(Lists) - Clauses to be considered for MaxSat. Should be given as a list of lists and 1-indexed instead 0-indexed, see example - solutions : List + problem : tuple(int, list[list[int]]) + The number of variables and a list of clauses for the MaxSat instance. + solutions : list Variables that were found to be a part of the solution. + Returns ------- cost_operator : function - the Operator applied to the circuit-QuantumVariable - - Examples - -------- - - >>> clauses11 = (6, [[1,2,-3], [1,4,-6], [4,5,6], [1,3,-4], [2,4,5], [1,3,5], [-2,-3,6]]) - - | Explanation: - | First entry of tuple is the number of variables, second is the clauses - | Clause [1, 2, -4] is fulfilled by the QuantumStates "1100", "1110" - - - * if the sign is positive : the index has of the QuantumVariable to be "1", if negative sign the index has to be "0". - * indices not mentioned can be "0" or "1" (the third integer in this case). - * start with 1 in your clauses! because ``int 0`` has no sign and this is problematic. - * we want to keep the mathematical formulation, so no handing over strings! + A function receiving a :ref:`QuantumVariable` and a real parameter $\gamma$. This function performs the application of the cost operator. """ diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiro_utils.py b/src/qrisp/algorithms/qiro/qiroproblems/qiro_utils.py index 3538f42b..2ab02370 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiro_utils.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiro_utils.py @@ -20,22 +20,22 @@ def find_max(single_cor, double_cor, res, solutions): """ - General subroutine for finding the values with maximal correlation in the QIRO algorithm + Subroutine for finding the values with maximal correlation in the QIRO algorithm. Parameters ---------- - single_cor : List - Single qubits correlations to check. - double_cor : List[Tuple] - Multi qubits correlations to check. + single_cor : list + Single qubit correlations to check. + double_cor : list[tuple] + Multi qubit correlations to check. res : dict - Result dictionary of initial QAOA optimization procedure + Result dictionary of QAOA optimization procedure. solutions : List - Qubits which have been found to be positive correlated, i.e. part of the problem solution + Qubits which were found to be positivel correlated, i.e., part of the problem solution. Returns ------- - max_item , sign + max_item, sign The item with maximal correlation and the sign of the correlation. """ diff --git a/tests/test_QAOAmaxSat.py b/tests/test_QAOAmaxSat.py index f114559c..d3bd46ee 100644 --- a/tests/test_QAOAmaxSat.py +++ b/tests/test_QAOAmaxSat.py @@ -25,18 +25,19 @@ def test_QAOAmaxSat(): clauses = [[1,2,-3],[1,4,-6],[4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6]] - num_variables = 6 - qarg = QuantumVariable(num_variables) + num_vars = 6 + problem = (num_vars, clauses) + qarg = QuantumVariable(num_vars) - qaoa_max_indep_set = QAOAProblem(cost_operator=create_maxsat_cost_operator(clauses), + qaoa_max_indep_set = QAOAProblem(cost_operator=create_maxsat_cost_operator(problem), mixer=RX_mixer, - cl_cost_function=create_maxsat_cl_cost_function(clauses)) + cl_cost_function=create_maxsat_cl_cost_function(problem)) results = qaoa_max_indep_set.run(qarg=qarg, depth=5) - cl_cost = create_maxsat_cl_cost_function(clauses) + cl_cost = create_maxsat_cl_cost_function(problem) # find optimal solution by brute force - temp_binStrings = list(itertools.product([1,0], repeat=num_variables)) + temp_binStrings = list(itertools.product([1,0], repeat=num_vars)) binStrings = ["".join(map(str, item)) for item in temp_binStrings] min = 0 @@ -50,5 +51,4 @@ def test_QAOAmaxSat(): optimal_sol = binStrings[min_index] # approximation ratio test - assert approximation_ratio(results, optimal_sol, cl_cost)>=0.5 - + assert approximation_ratio(results, optimal_sol, cl_cost)>=0.5 \ No newline at end of file From c893cb84fffad9a2ef28874564a80c09a87b7926 Mon Sep 17 00:00:00 2001 From: Rene Zander Date: Thu, 17 Oct 2024 19:16:00 +0200 Subject: [PATCH 5/8] Updated QIRO test cases --- .../qiro/implementations/QIROMaxClique.rst | 2 +- tests/test_QAOAmaxSat.py | 1 - tests/test_QIRO.py | 59 ---------- tests/test_QIROMaxSat.py | 108 ++++++++---------- tests/test_QIROmaxClique.py | 78 ++++++------- 5 files changed, 81 insertions(+), 167 deletions(-) delete mode 100644 tests/test_QIRO.py diff --git a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst index e5a9fff4..99f0cf07 100644 --- a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst +++ b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst @@ -72,7 +72,7 @@ That’s it! In the following, we print the 5 most likely solutions together wit cl_cost = create_max_clique_cl_cost_function(G) print("5 most likely QIRO solutions") - ax_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] + max_five_qiro = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] for res in max_five_qiro: print([index for index, value in enumerate(res) if value == '1']) print(cl_cost({res : 1})) diff --git a/tests/test_QAOAmaxSat.py b/tests/test_QAOAmaxSat.py index d3bd46ee..abc2332e 100644 --- a/tests/test_QAOAmaxSat.py +++ b/tests/test_QAOAmaxSat.py @@ -16,7 +16,6 @@ ********************************************************************************/ """ - from qrisp import QuantumVariable from qrisp.qaoa import QAOAProblem, RX_mixer, approximation_ratio, create_maxsat_cl_cost_function, create_maxsat_cost_operator import itertools diff --git a/tests/test_QIRO.py b/tests/test_QIRO.py deleted file mode 100644 index 8fef657a..00000000 --- a/tests/test_QIRO.py +++ /dev/null @@ -1,59 +0,0 @@ -# imports -from qrisp.qiro.qiro_problem import QIROProblem -from qrisp.qaoa.qaoa_problem import QAOAProblem -from qrisp.qaoa.problems.maxCliqueInfrastr import maxCliqueCostfct, maxCliqueCostOp -from qrisp.qiro.qiroproblems.qiroMaxCliqueInfrastr import * -from qrisp.qaoa.mixers import RX_mixer -from qrisp.qiro.qiro_mixers import qiro_init_function, qiro_RXMixer -from qrisp import QuantumVariable -import matplotlib.pyplot as plt -import networkx as nx - -def test_QIRO(): - - # First we define a graph via the number of nodes and the QuantumVariable arguments - num_nodes = 15 - G = nx.erdos_renyi_graph(num_nodes,0.7, seed = 99) - Gtwo = nx.erdos_renyi_graph(num_nodes,0.7, seed = 99) - qarg = QuantumVariable(G.number_of_nodes()) - # set simulator shots - mes_kwargs = { - #below should be 5k - "shots" : 5000 - } - - #assign cost_function and maxclique_instance, normal QAOA - testCostFun = maxCliqueCostfct(Gtwo) - maxclique_instance = QAOAProblem(maxCliqueCostOp(G), RX_mixer, maxCliqueCostfct(G)) - - # assign the correct new update functions for qiro from above imports - qiro_instance = QIROProblem(problem = Gtwo, - replacement_routine = create_maxClique_replacement_routine, - cost_operator = create_maxClique_cost_operator_reduced, - mixer = qiro_RXMixer, - cl_cost_function = maxCliqueCostfct, - init_function = qiro_init_function - ) - - - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2, - #mes_kwargs = mes_kwargs - ) - - #QIRO 5 best results - maxfive = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] - - import itertools - for key, val in res_qiro.items(): - if key in maxfive: - cost = testCostFun({key:1}) - temp = True - if cost < 0: - intlist = [s for s in range(len(list(key))) if list(key)[s] == "1"] - combinations = list(itertools.combinations(intlist, 2)) - # if any combination is found in the list of G.edges(), the solution is wrong, and energy == 0 - for combination in combinations: - if combination not in G.edges(): - temp = False - break - assert temp diff --git a/tests/test_QIROMaxSat.py b/tests/test_QIROMaxSat.py index 0e05474f..416c0ada 100644 --- a/tests/test_QIROMaxSat.py +++ b/tests/test_QIROMaxSat.py @@ -1,69 +1,57 @@ -# imports -from qrisp.qiro.qiro_problem import QIROProblem -from qrisp.qaoa.problems.maxSatInfrastr import maxSatclCostfct, clausesdecoder -from qrisp.qiro.qiroproblems.qiroMaxSatInfrastr import * -from qrisp.qiro.qiro_mixers import qiro_init_function, qiro_RXMixer -from qrisp import QuantumVariable -import matplotlib.pyplot as plt -import networkx as nx - -def test_QIROmaxSat(): - # define the problem according to maxSat encoding - problem = [8 , [[1,2,-3],[1,4,-6], [4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6], [1,7,8], [3,-7,-8], [3,4,8],[4,5,8], [1,2,7]]] - decodedClauses = clausesdecoder( problem) +""" +\******************************************************************************** +* Copyright (c) 2024 the Qrisp authors +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* This Source Code may also be made available under the following Secondary +* Licenses when the conditions for such availability set forth in the Eclipse +* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 +* with the GNU Classpath Exception which is +* available at https://www.gnu.org/software/classpath/license.html. +* +* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +********************************************************************************/ +""" - qarg = QuantumVariable(problem[0]) +from qrisp import QuantumVariable +from qrisp.qaoa import create_maxsat_cl_cost_function, approximation_ratio +from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxSat_replacement_routine, create_maxSat_cost_operator_reduced +import itertools - # set simulator shots - mes_kwargs = { - #below should be 5k - "shots" : 5000 - } +def test_qiro_maxsat(): + clauses = [[1,2,-3],[1,4,-6],[4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6],[1,7,8],[3,-7,-8],[3,4,8],[4,5,8],[1,2,7]] + num_vars = 8 + problem = (num_vars, clauses) + qarg = QuantumVariable(num_vars) - # assign cost_function and maxclique_instance, normal QAOA - testCostFun = maxSatclCostfct(problem) + cl_cost = create_maxsat_cl_cost_function(problem) - # assign the correct new update functions for qiro from above imports - qiro_instance = QIROProblem(problem = problem, - replacement_routine = create_maxSat_replacement_routine, + qiro_instance = QIROProblem(problem = problem, + replacement_routine = create_maxSat_replacement_routine, cost_operator = create_maxSat_cost_operator_reduced, mixer = qiro_RXMixer, - cl_cost_function = maxSatclCostfct, + cl_cost_function = create_maxsat_cl_cost_function, init_function = qiro_init_function ) - - - # We run the qiro instance and get the results! - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2, - #mes_kwargs = mes_kwargs - ) - - - - import itertools - def testCostFun(state): - obj = 0 - #literally just do a minus 1 op if state is equiv to a given condition - for index in range(len(decodedClauses)): - if state in decodedClauses[index]: - obj -= 1 - - return obj - - - #print 5 best sols - # assert that sol is in decoded clauses - maxfive = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] - # assert that sol is in decoded clauses - for name in res_qiro.keys(): # for name, age in dictionary.iteritems(): (for Python 2.x) - if name in maxfive: - print(name) - print(testCostFun(name)) - if testCostFun(name) < 0: - temp = False - for index in range(len(decodedClauses)): - if name in decodedClauses[index]: - temp = True - assert temp - + res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2) + + # find optimal solution by brute force + temp_binStrings = list(itertools.product([1,0], repeat=num_vars)) + binStrings = ["".join(map(str, item)) for item in temp_binStrings] + + min = 0 + min_index = 0 + for index in range(len(binStrings)): + val = cl_cost({binStrings[index] : 1}) + if val < min: + min = val + min_index = index + + optimal_sol = binStrings[min_index] + + # approximation ratio test + assert approximation_ratio(res_qiro, optimal_sol, cl_cost)>=0.5 diff --git a/tests/test_QIROmaxClique.py b/tests/test_QIROmaxClique.py index 59bf9837..f3b87ea3 100644 --- a/tests/test_QIROmaxClique.py +++ b/tests/test_QIROmaxClique.py @@ -1,60 +1,46 @@ -# imports -from qrisp.qiro.qiro_problem import QIROProblem -from qrisp.qaoa.qaoa_problem import QAOAProblem -from qrisp.qaoa.problems.maxCliqueInfrastr import maxCliqueCostfct, maxCliqueCostOp -from qrisp.qiro.qiroproblems.qiroMaxCliqueInfrastr import * -from qrisp.qaoa.mixers import RX_mixer -from qrisp.qiro.qiro_mixers import qiro_init_function, qiro_RXMixer +""" +\******************************************************************************** +* Copyright (c) 2024 the Qrisp authors +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* This Source Code may also be made available under the following Secondary +* Licenses when the conditions for such availability set forth in the Eclipse +* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 +* with the GNU Classpath Exception which is +* available at https://www.gnu.org/software/classpath/license.html. +* +* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +********************************************************************************/ +""" + from qrisp import QuantumVariable -import matplotlib.pyplot as plt +from qrisp.qiro import QIROProblem, create_maxClique_replacement_routine, create_maxClique_cost_operator_reduced, qiro_RXMixer, qiro_init_function +from qrisp.qaoa import max_clique_problem, create_max_clique_cl_cost_function import networkx as nx -def test_QIRO(): +def test_qiro_max_clique(): - # First we define a graph via the number of nodes and the QuantumVariable arguments + # Define a random graph via the number of nodes and the QuantumVariable arguments num_nodes = 15 - G = nx.erdos_renyi_graph(num_nodes,0.7, seed = 99) - Gtwo = nx.erdos_renyi_graph(num_nodes,0.7, seed = 99) + G = nx.erdos_renyi_graph(num_nodes, 0.7, seed=99) qarg = QuantumVariable(G.number_of_nodes()) - # set simulator shots - mes_kwargs = { - #below should be 5k - "shots" : 5000 - } - #assign cost_function and maxclique_instance, normal QAOA - testCostFun = maxCliqueCostfct(Gtwo) - maxclique_instance = QAOAProblem(maxCliqueCostOp(G), RX_mixer, maxCliqueCostfct(G)) + cl_cost = create_max_clique_cl_cost_function(G) - # assign the correct new update functions for qiro from above imports - qiro_instance = QIROProblem(problem = Gtwo, - replacement_routine = create_maxClique_replacement_routine, + # QIRO + qiro_instance = QIROProblem(problem = G, + replacement_routine = create_maxClique_replacement_routine, cost_operator = create_maxClique_cost_operator_reduced, mixer = qiro_RXMixer, - cl_cost_function = maxCliqueCostfct, + cl_cost_function = create_max_clique_cl_cost_function, init_function = qiro_init_function ) + res_qiro = qiro_instance.run_qiro(qarg=qarg, depth=3, n_recursions=2) - - res_qiro = qiro_instance.run_qiro(qarg=qarg, depth = 3, n_recursions = 2, - #mes_kwargs = mes_kwargs - ) - - #QIRO 5 best results - maxfive = sorted(res_qiro, key=res_qiro.get, reverse=True)[:5] - - import itertools - for key, val in res_qiro.items(): - if key in maxfive: - cost = testCostFun({key:1}) - temp = True - if cost < 0: - intlist = [s for s in range(len(list(key))) if list(key)[s] == "1"] - combinations = list(itertools.combinations(intlist, 2)) - # if any combination is found in the list of G.edges(), the solution is wrong, and energy == 0 - for combination in combinations: - if combination not in G.edges(): - temp = False - break - assert temp + # QIRO most likely result + most_likely = sorted(res_qiro, key=res_qiro.get, reverse=True)[0] + assert cl_cost({most_likely:1})<0 From d77873dabe653d6cc4aea8f35be08850ddf71d2e Mon Sep 17 00:00:00 2001 From: Rene Zander Date: Thu, 17 Oct 2024 21:42:03 +0200 Subject: [PATCH 6/8] Minor updates QIRO implementations --- .../source/general/tutorial/QIROtutorial.rst | 6 +- .../qiro/implementations/QIROMaxClique.rst | 12 +- .../qiro/implementations/QIROMaxIndep.rst | 10 +- .../qiro/implementations/QIROMaxSat.rst | 10 +- .../qiro/implementations/QIROMaxSetPack.rst | 6 +- src/qrisp/algorithms/qiro/__init__.py | 1 - src/qrisp/algorithms/qiro/qiro_problem.py | 13 +- .../qiro/qiroproblems/qiroMaxClique.py | 20 +-- .../qiro/qiroproblems/qiroMaxIndepSet.py | 4 +- .../qiro/qiroproblems/qiroMaxSat.py | 4 +- .../qiro/qiroproblems/qiroMaxSetPack.py | 167 +----------------- tests/test_QIROMaxSat.py | 6 +- tests/test_QIROmaxClique.py | 6 +- 13 files changed, 43 insertions(+), 222 deletions(-) diff --git a/documentation/source/general/tutorial/QIROtutorial.rst b/documentation/source/general/tutorial/QIROtutorial.rst index bfed77fe..7bbb1b38 100644 --- a/documentation/source/general/tutorial/QIROtutorial.rst +++ b/documentation/source/general/tutorial/QIROtutorial.rst @@ -193,7 +193,7 @@ We start off by defining the graph of our problem, as well as the :ref:`QuantumV :: from qrisp import QuantumVariable - from qrisp.qiro import QIROProblem, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qiro import QIROProblem, create_max_indep_replacement_routine, create_max_indep_cost_operator_reduced, qiro_RXMixer, qiro_init_function from qrisp.qaoa import create_max_indep_set_cl_cost_function import matplotlib.pyplot as plt import networkx as nx @@ -210,8 +210,8 @@ With this, we can directly throw everything that is relevant at the :ref:`QIROPr # assign the correct new update functions for qiro qiro_instance = QIROProblem(G, - replacement_routine=create_maxIndep_replacement_routine, - cost_operator=create_maxIndep_cost_operator_reduced, + replacement_routine=create_max_indep_replacement_routine, + cost_operator=create_max_indep_cost_operator_reduced, mixer=qiro_RXMixer, cl_cost_function=create_max_indep_set_cl_cost_function, init_function=qiro_init_function diff --git a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst index 99f0cf07..00ab75a4 100644 --- a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst +++ b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxClique.rst @@ -27,13 +27,13 @@ Based on the **maximum absolute entry** of the correlation matrix M and its sign * If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we remove all vertices that **do not** share an edge with either vertex :math:`i` or :math:`j`. Since one of the vertices :math:`i` and :math:`j` will be part of the final solution (but not both), any vertex that is **not** connected to either :math:`i` or :math:`j` (or both) is guaranteed to violate the problem constraints, and can be removed from the graph. -.. autofunction:: create_maxClique_replacement_routine +.. autofunction:: create_max_clique_replacement_routine QIRO Cost operator ------------------ -.. autofunction:: create_maxClique_cost_operator_reduced +.. autofunction:: create_max_clique_cost_operator_reduced Example implementation @@ -42,7 +42,7 @@ Example implementation :: from qrisp import QuantumVariable - from qrisp.qiro import QIROProblem, create_maxClique_replacement_routine, create_maxClique_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qiro import QIROProblem, create_max_clique_replacement_routine, create_max_clique_cost_operator_reduced, qiro_RXMixer, qiro_init_function from qrisp.qaoa import max_clique_problem, create_max_clique_cl_cost_function import matplotlib.pyplot as plt import networkx as nx @@ -54,8 +54,8 @@ Example implementation # QIRO qiro_instance = QIROProblem(problem=G, - replacement_routine= reate_maxClique_replacement_routine, - cost_operator=create_maxClique_cost_operator_reduced, + replacement_routine= reate_max_clique_replacement_routine, + cost_operator=create_max_clique_cost_operator_reduced, mixer=qiro_RXMixer, cl_cost_function=create_max_clique_cl_cost_function, init_function=qiro_init_function @@ -65,7 +65,7 @@ Example implementation # The final graph that has been adjusted final_graph = qiro_instance.problem -That’s it! In the following, we print the 5 most likely solutions together with their cost values, and compare to the NetworkX solution +That’s it! In the following, we print the 5 most likely solutions together with their cost values, and compare to the NetworkX solution. :: diff --git a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxIndep.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxIndep.rst index 29e211e3..ff3c1a76 100644 --- a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxIndep.rst +++ b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxIndep.rst @@ -26,13 +26,13 @@ Based on the **maximum absolute entry** of the correlation matrix M and its sign * If :math:`\text{M}_{ij} < 0, (i, j) ∈ E` was selected, we remove all vertices that share an edge with both vertices :math:`i` and :math:`j`. Since one of the vertices :math:`i` and :math:`j` will be part of the final solution (but not both), any vertex that is connected to both :math:`i` and :math:`j` is guaranteed to violate the problem constraints, and can be removed from the graph. In this case, it may be possible that no vertex is found to be a canditate for being removed. We will then simply choose the second biggest absolute value of **M** for the replacement routine. -.. autofunction:: create_maxIndep_replacement_routine +.. autofunction:: create_max_indep_replacement_routine QIRO Cost operator ------------------ -.. autofunction:: create_maxIndep_cost_operator_reduced +.. autofunction:: create_max_indep_cost_operator_reduced Example implementation @@ -40,7 +40,7 @@ Example implementation :: from qrisp import QuantumVariable - from qrisp.qiro import QIROProblem, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qiro import QIROProblem, create_max_indep_replacement_routine, create_max_indep_cost_operator_reduced, qiro_RXMixer, qiro_init_function from qrisp.qaoa import create_max_indep_set_cl_cost_function import matplotlib.pyplot as plt import networkx as nx @@ -51,8 +51,8 @@ Example implementation qarg = QuantumVariable(G.number_of_nodes()) qiro_instance = QIROProblem(G, - replacement_routine=create_maxIndep_replacement_routine, - cost_operator=create_maxIndep_cost_operator_reduced, + replacement_routine=create_max_indep_replacement_routine, + cost_operator=create_max_indep_cost_operator_reduced, mixer=qiro_RXMixer, cl_cost_function=create_max_indep_set_cl_cost_function, init_function=qiro_init_function diff --git a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSat.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSat.rst index 997db168..9cc8e719 100644 --- a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSat.rst +++ b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSat.rst @@ -51,13 +51,13 @@ Based on the **maximum absolute entry** of the correlation matrix M and its sign -.. autofunction:: create_maxSat_replacement_routine +.. autofunction:: create_maxsat_replacement_routine QIRO Cost operator ------------------ -.. autofunction:: create_maxSat_cost_operator_reduced +.. autofunction:: create_maxsat_cost_operator_reduced Example implementation @@ -67,7 +67,7 @@ Example implementation from qrisp import QuantumVariable from qrisp.qaoa import create_maxsat_cl_cost_function - from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxSat_replacement_routine, create_maxSat_cost_operator_reduced + from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxsat_replacement_routine, create_maxsat_cost_operator_reduced clauses = [[1,2,-3],[1,4,-6],[4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6],[1,7,8],[3,-7,-8],[3,4,8],[4,5,8],[1,2,7]] num_vars = 8 @@ -75,8 +75,8 @@ Example implementation qarg = QuantumVariable(num_vars) qiro_instance = QIROProblem(problem = problem, - replacement_routine = create_maxSat_replacement_routine, - cost_operator = create_maxSat_cost_operator_reduced, + replacement_routine = create_maxsat_replacement_routine, + cost_operator = create_maxsat_cost_operator_reduced, mixer = qiro_RXMixer, cl_cost_function = create_maxsat_cl_cost_function, init_function = qiro_init_function diff --git a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSetPack.rst b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSetPack.rst index acd1a90f..5fc5d8ea 100644 --- a/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSetPack.rst +++ b/documentation/source/reference/Algorithms/qiro/implementations/QIROMaxSetPack.rst @@ -27,7 +27,7 @@ Example implementation :: from qrisp import QuantumVariable - from qrisp.qiro import QIROProblem, create_maxIndep_replacement_routine, create_maxIndep_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qiro import QIROProblem, create_max_indep_replacement_routine, create_max_indep_cost_operator_reduced, qiro_RXMixer, qiro_init_function from qrisp.qaoa import create_max_indep_set_cl_cost_function import matplotlib.pyplot as plt import networkx as nx @@ -39,8 +39,8 @@ Example implementation qarg = QuantumVariable(G.number_of_nodes()) qiro_instance = QIROProblem(G, - replacement_routine=create_maxIndep_replacement_routine, - cost_operator=create_maxIndep_cost_operator_reduced, + replacement_routine=create_max_indep_replacement_routine, + cost_operator=create_max_indep_cost_operator_reduced, mixer=qiro_RXMixer, cl_cost_function=create_max_indep_set_cl_cost_function, init_function=qiro_init_function diff --git a/src/qrisp/algorithms/qiro/__init__.py b/src/qrisp/algorithms/qiro/__init__.py index f048f16b..bcaf3cfb 100644 --- a/src/qrisp/algorithms/qiro/__init__.py +++ b/src/qrisp/algorithms/qiro/__init__.py @@ -23,4 +23,3 @@ from qrisp.algorithms.qaoa.problems import * from qrisp.algorithms.qiro.qiroproblems import * from qrisp.algorithms.qiro.qiro_mixers import * -from qrisp.algorithms.qiro.qiro_problem import * diff --git a/src/qrisp/algorithms/qiro/qiro_problem.py b/src/qrisp/algorithms/qiro/qiro_problem.py index 767fb282..430c8a1f 100644 --- a/src/qrisp/algorithms/qiro/qiro_problem.py +++ b/src/qrisp/algorithms/qiro/qiro_problem.py @@ -34,7 +34,7 @@ class QIROProblem(QAOAProblem): :: from qrisp import QuantumVariable - from qrisp.qiro import QIROProblem, create_maxClique_replacement_routine, create_maxClique_cost_operator_reduced, qiro_RXMixer, qiro_init_function + from qrisp.qiro import QIROProblem, create_max_clique_replacement_routine, create_max_clique_cost_operator_reduced, qiro_RXMixer, qiro_init_function from qrisp.qaoa import max_clique_problem, create_max_clique_cl_cost_function import matplotlib.pyplot as plt import networkx as nx @@ -53,8 +53,8 @@ class QIROProblem(QAOAProblem): # QIRO qiro_instance = QIROProblem(problem = Gtwo, - replacement_routine = create_maxClique_replacement_routine, - cost_operator = create_maxClique_cost_operator_reduced, + replacement_routine = create_max_clique_replacement_routine, + cost_operator = create_max_clique_cost_operator_reduced, mixer = qiro_RXMixer, cl_cost_function = create_max_clique_cl_cost_function, init_function = qiro_init_function @@ -87,8 +87,11 @@ class QIROProblem(QAOAProblem): plt.title('final QIRO graph') plt.figure(2) - nx.draw(G, with_labels = True, node_color='#ADD8E6', edge_color='#D3D3D3') - plt.title('Original graph') + most_likely = [index for index, value in enumerate(max_five_qiro[0]) if value == '1'] + nx.draw(G, with_labels=True, + node_color=['#FFCCCB' if node in most_likely else '#ADD8E6' for node in G.nodes()], + edge_color=['#FFCCCB' if edge[0] in most_likely and edge[1] in most_likely else '#D3D3D3' for edge in G.edges()]) + plt.title('Original graph with most likely QIRO solution') plt.show() diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py index 02a8a99f..4fc16bf7 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxClique.py @@ -23,7 +23,7 @@ from qrisp.algorithms.qiro.qiroproblems.qiro_utils import * -def create_maxClique_replacement_routine(res, graph, solutions, exclusions): +def create_max_clique_replacement_routine(res, graph, solutions, exclusions): """ Creates a replacement routine for the problem structure, i.e., defines the replacement rules. See the `original paper `_ for a description of the update rules. @@ -102,7 +102,7 @@ def create_maxClique_replacement_routine(res, graph, solutions, exclusions): return new_graph, solutions, sign, exclusions -def create_maxClique_cost_operator_reduced(graph, solutions=[]): +def create_max_clique_cost_operator_reduced(graph, solutions=[]): r""" Creates the ``cost_operator`` for the problem instance. This operator is adjusted to consider qubits that were found to be a part of the problem solution. @@ -132,19 +132,3 @@ def cost_operator(qv, gamma): return cost_operator - - - - - - - - -""" -BM: 18 nodes, seed 99 -0.5933101570400762 -normal -0.08909071112167886 - - -""" \ No newline at end of file diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py index 07a4527d..858809ba 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxIndepSet.py @@ -22,7 +22,7 @@ from qrisp.algorithms.qiro.qiroproblems.qiro_utils import * -def create_maxIndep_replacement_routine(res, graph, solutions=[], exclusions=[]): +def create_max_indep_replacement_routine(res, graph, solutions=[], exclusions=[]): """ Creates a replacement routine for the problem structure, i.e., defines the replacement rules. See the `original paper `_ for a description of the update rules. @@ -93,7 +93,7 @@ def create_maxIndep_replacement_routine(res, graph, solutions=[], exclusions=[]) return new_graph, solutions, sign, exclusions -def create_maxIndep_cost_operator_reduced(graph, solutions=[]): +def create_max_indep_cost_operator_reduced(graph, solutions=[]): r""" Creates the ``cost_operator`` for the problem instance. This operator is adjusted to consider qubits that were found to be a part of the problem solution. diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSat.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSat.py index 4f8b58eb..64a5f74c 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSat.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSat.py @@ -25,7 +25,7 @@ from qrisp.algorithms.qiro.qiroproblems.qiro_utils import * -def create_maxSat_replacement_routine(res, problem, solutions, exclusions): +def create_maxsat_replacement_routine(res, problem, solutions, exclusions): """ Creates a replacement routine for the problem structure, i.e., defines the replacement rules. See the `original paper `_ for a description of the update rules. @@ -120,7 +120,7 @@ def create_maxSat_replacement_routine(res, problem, solutions, exclusions): return [numVars, clauses], solutions, sign, exclusions -def create_maxSat_cost_operator_reduced(problem, solutions=[]): +def create_maxsat_cost_operator_reduced(problem, solutions=[]): """ Creates the ``cost_operator`` for the problem instance. diff --git a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPack.py b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPack.py index 6fb51505..9e5fcef9 100644 --- a/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPack.py +++ b/src/qrisp/algorithms/qiro/qiroproblems/qiroMaxSetPack.py @@ -21,7 +21,7 @@ def transform_max_set_pack_to_mis(problem): """ - Transforms a Maximum Set Packing problem instance into a Maximum Independent Set problem instance. + Transforms a Maximum Set Packing problem instance into a Maximum Independent Set (MIS) problem instance. Parameters ---------- @@ -45,168 +45,3 @@ def non_empty_intersection(problem): return G - - - - - - - - - -""" -does all of the set popping actually work? Since the amount of sets is equivalent to len of qarg? ---> setting to empty also doesnt work, since that implies that that they are always in - - # what to do about sols and exclusions in cost_op - -where does the neighbourhood_dict fit it? - -need to write cost function? - - -""" - -""" def create_maxSetPack_replacement_routine( res, old_n_dict, problem, solutions, exclusions): - - sets ,universe = copy.deepcopy(problem[0]), problem[1] - - n_dict = copy.deepcopy(old_n_dict) - - # FOR SINGLE QUBIT CORRELATIONS - orig_nodes = list(universe) - combinations = [] - - # add check for solution nodes - for index1 in range(len(universe)): - for index2 in range(index1,len(universe)): - combinations.append((index1,index2)) - - # you are HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - max = 0 - max_item = [] - #get the max_edge and eval the sum and sign - - - max_item, sign = find_max(orig_nodes, combinations, res, solutions) - - # we just directly remove vertices from the graph - if isinstance(max_item, int): - if sign > 0: - the_set = sets[max_item] - for node in the_set: - rels = n_dict[node] - for rel in rels: - sets[rels] = [] - # add set to sol - # go through n_dict and remove all other sets that contain elements in this set - # what does remove mean in this context - solutions.append(max_item) - exclusions.append(max_item) - - elif sign < 0: - # remove set from sets? - sets[rels] = [] - exclusions.append(max_item) - - - else: - if sign > 0: - - - # both are in: all elements can be deleted for other sets - # both are out: ?? - # --> add whatever is in second set to first set, and then dont consider the second set anymore? - # keep the correlation somehow?? --> this is now in the cost_op - # or work on the neighbourhood_dict? - sets[0] += sets[1] - sets[1] = [] - solutions.append(max_item) - - pass - - elif sign < 0: - # remove all sets that contain elements of the schnittmenge, because one of the two is going to be in there - # how to fix the negative correlation?? - intersect = list(sets[max_item[0]] & sets[max_item[1]]) - for item in intersect: - rels = n_dict[item] - for rel in rels: - if not rel in max_item: - sets[rels] = [] - exclusions.append(rel) - - - - return sets, solutions, sign, exclusions - -# what about other operatores?? - - - -def maxSetPackCost_reduced(problem, solutions = []): - - | Create the cost/problem operator for this problem instance. The swapping rule is to swap a set in and out of the solution, if it is not intersecting with any other set. - | Idea - Per set: - - * Create ancillas for every element, they represent these elements - * Perform multi controlled x operations on each ancilla - * Controls are given by sets with also contain the considered element - * If all controls are "0" (see ``ctrl_state`` for ``mcx``-operation) we set this ancilla to "1" - - | Then perform mcx on the qubit describing the set as follows: - | If all ancillas are "1" this means the qubits describing the sets contain no intersections with the considered set. We can then swap the set in (or out). - | Afterwards uncompute the ancillas. - - Parameters - ---------- - sets : list(Lists) - The sets the universe is seperated into as by the problem definition - - universe: Tuple - The universe for the problem instance, i.e. all possible values (all graph vertices) - - Returns - ------- - QuantumCircuit: qrisp.QuantumCircuit - the Operator applied to the circuit-QuantumVariable - - - - universe, sets = list(range(problem[0])), problem[1] - - # get neigbhourhood relations from helper function - nbh_rel = get_neighbourhood_relations(problem) - - # what to do about sols and exclusions - def theCostOpEmbedded(qv, gamma): - #check all sets - for set_index in range(len(sets)): - # get set elements and create an ancilla for every set element - nodes = sets[set_index] - ancillas = QuantumVariable(len(nodes)) - # go through all ancillas and, equivalently set elements - for ancilla_index in range(len(ancillas)): - # if the element is only in one set, we can set this ancilla to 1 - if len(nbh_rel[nodes[ancilla_index]])<2: - x(ancillas[ancilla_index]) - continue - # else save the sets with also contain the considered element - nbh_sets_list = [ item for item in nbh_rel[nodes[ancilla_index]] if item != set_index] - # perform mcx on ancilla, control given by the relevant set - mcx([qv[nbh_sets_index] for nbh_sets_index in nbh_sets_list], ancillas[ancilla_index], ctrl_state= "0" * len(nbh_sets_list)) - # perform mcrx gate on the qubit describing the considered set - with control(ancillas): - rx(gamma, qv[set_index]) - - for item in solutions: - if not isinstance(item, int): - cx(item[0],item[1]) - #mcrx_gate = RXGate(gamma).control(len(ancillas)) - #qv.qs.append( mcrx_gate, [*ancillas, qv[set_index]]) - - ancillas.uncompute() - - return theCostOpEmbedded - - """ \ No newline at end of file diff --git a/tests/test_QIROMaxSat.py b/tests/test_QIROMaxSat.py index 416c0ada..594a6ade 100644 --- a/tests/test_QIROMaxSat.py +++ b/tests/test_QIROMaxSat.py @@ -18,7 +18,7 @@ from qrisp import QuantumVariable from qrisp.qaoa import create_maxsat_cl_cost_function, approximation_ratio -from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxSat_replacement_routine, create_maxSat_cost_operator_reduced +from qrisp.qiro import QIROProblem, qiro_init_function, qiro_RXMixer, create_maxsat_replacement_routine, create_maxsat_cost_operator_reduced import itertools def test_qiro_maxsat(): @@ -31,8 +31,8 @@ def test_qiro_maxsat(): cl_cost = create_maxsat_cl_cost_function(problem) qiro_instance = QIROProblem(problem = problem, - replacement_routine = create_maxSat_replacement_routine, - cost_operator = create_maxSat_cost_operator_reduced, + replacement_routine = create_maxsat_replacement_routine, + cost_operator = create_maxsat_cost_operator_reduced, mixer = qiro_RXMixer, cl_cost_function = create_maxsat_cl_cost_function, init_function = qiro_init_function diff --git a/tests/test_QIROmaxClique.py b/tests/test_QIROmaxClique.py index f3b87ea3..116017d1 100644 --- a/tests/test_QIROmaxClique.py +++ b/tests/test_QIROmaxClique.py @@ -17,7 +17,7 @@ """ from qrisp import QuantumVariable -from qrisp.qiro import QIROProblem, create_maxClique_replacement_routine, create_maxClique_cost_operator_reduced, qiro_RXMixer, qiro_init_function +from qrisp.qiro import QIROProblem, create_max_clique_replacement_routine, create_max_clique_cost_operator_reduced, qiro_RXMixer, qiro_init_function from qrisp.qaoa import max_clique_problem, create_max_clique_cl_cost_function import networkx as nx @@ -32,8 +32,8 @@ def test_qiro_max_clique(): # QIRO qiro_instance = QIROProblem(problem = G, - replacement_routine = create_maxClique_replacement_routine, - cost_operator = create_maxClique_cost_operator_reduced, + replacement_routine = create_max_clique_replacement_routine, + cost_operator = create_max_clique_cost_operator_reduced, mixer = qiro_RXMixer, cl_cost_function = create_max_clique_cl_cost_function, init_function = qiro_init_function From 61eda523aa361172a953c3a9eb714dfc7fcc4829 Mon Sep 17 00:00:00 2001 From: Rene Zander Date: Thu, 17 Oct 2024 21:51:20 +0200 Subject: [PATCH 7/8] Removed deprecated QAOA implementations --- .../algorithms/qaoa/problems/__init__.py | 9 - .../qaoa/problems/eThrTwoLinInfrastr.py | 126 ---------- .../qaoa/problems/maxCliqueInfrastr.py | 104 -------- .../qaoa/problems/maxIndepSetInfrastr.py | 86 ------- .../qaoa/problems/maxSatInfrastr.py | 233 ------------------ .../qaoa/problems/maxSetPackInfrastr.py | 202 --------------- .../qaoa/problems/minSetCoverInfrastr.py | 195 --------------- 7 files changed, 955 deletions(-) delete mode 100644 src/qrisp/algorithms/qaoa/problems/eThrTwoLinInfrastr.py delete mode 100644 src/qrisp/algorithms/qaoa/problems/maxCliqueInfrastr.py delete mode 100644 src/qrisp/algorithms/qaoa/problems/maxIndepSetInfrastr.py delete mode 100644 src/qrisp/algorithms/qaoa/problems/maxSatInfrastr.py delete mode 100644 src/qrisp/algorithms/qaoa/problems/maxSetPackInfrastr.py delete mode 100644 src/qrisp/algorithms/qaoa/problems/minSetCoverInfrastr.py diff --git a/src/qrisp/algorithms/qaoa/problems/__init__.py b/src/qrisp/algorithms/qaoa/problems/__init__.py index 49712bed..57a76514 100644 --- a/src/qrisp/algorithms/qaoa/problems/__init__.py +++ b/src/qrisp/algorithms/qaoa/problems/__init__.py @@ -28,12 +28,3 @@ from qrisp.algorithms.qaoa.problems.maxSetPacking import * from qrisp.algorithms.qaoa.problems.minSetCover import * -# -# DEPRECATED -# -from qrisp.algorithms.qaoa.problems.minSetCoverInfrastr import * -from qrisp.algorithms.qaoa.problems.maxSatInfrastr import * -from qrisp.algorithms.qaoa.problems.eThrTwoLinInfrastr import * -from qrisp.algorithms.qaoa.problems.maxCliqueInfrastr import * -from qrisp.algorithms.qaoa.problems.maxIndepSetInfrastr import * -from qrisp.algorithms.qaoa.problems.maxSetPackInfrastr import * \ No newline at end of file diff --git a/src/qrisp/algorithms/qaoa/problems/eThrTwoLinInfrastr.py b/src/qrisp/algorithms/qaoa/problems/eThrTwoLinInfrastr.py deleted file mode 100644 index ff394389..00000000 --- a/src/qrisp/algorithms/qaoa/problems/eThrTwoLinInfrastr.py +++ /dev/null @@ -1,126 +0,0 @@ -""" -\******************************************************************************** -* Copyright (c) 2023 the Qrisp authors -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* This Source Code may also be made available under the following Secondary -* Licenses when the conditions for such availability set forth in the Eclipse -* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 -* with the GNU Classpath Exception which is -* available at https://www.gnu.org/software/classpath/license.html. -* -* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 -********************************************************************************/ -""" - -from qrisp import h, rz , cx -import numpy as np -from scipy.optimize import minimize -from sympy import Symbol -import itertools - -from collections.abc import Iterable - -# rethink this --> can we call clause[3]% 2 in assignment? because otherwise his blows up the circuit size... - - -def eThrTwocostOp(clauses): - - """ - | Implementation for eThrTwoLin Cost-Operator. - | Straight forward, apply :math:`R_{ZZZ}` gates between all qubits meantioned in each clause. - - - Parameters - ---------- - clauses : List(List) - clauses to be considered for eTwoThrLin - - Returns - ------- - QuantumCircuit: qrisp.QuantumCircuit - the Operator applied to the circuit-QuantumVariable - - Examples - -------- - - first three are the indices (a_1,j ; a_2,j ; a3,j), last one is b_L - - >>> clauses = [(1,3,4,5), (2,6,9,11), (1,5,8,33), (3,4,5,77), (2,4,6,7), (1,3,4,55), (1,3,6,49), (1,3,4,57), (1,3,6,44), (2,4,7,8)] - >>> cost_operator = eThrTwocostOp(clauses=clauses) - - """ - if not isinstance(clauses, Iterable): - raise Exception("Wrong structure of problem - clauses have to be iterable!") - for clause in clauses: - if not isinstance(clause, Iterable): - raise Exception("Wrong structure of problem - each clauses has to be an array!") - for item in clause: - if not isinstance(item, int): - raise Exception("Wrong structure of problem - each literal has to an int!") - - - def eThrTwocostnoEmbed(qv , gamma): - #clauses = clauses - qc = qv - for numClause in range(len(clauses)): - #go through clauses and apply quantum gates - clause = clauses[numClause] - - cx(qc[clause[0]], qc[clause[1]]) - cx(qc[clause[1]], qc[clause[2]]) - rz(pow(-1, clause[3] % 2)* gamma, qc[clause[2]]) - cx(qc[clause[1]], qc[clause[2]]) - cx(qc[clause[0]], qc[clause[1]]) - - return qc - - return eThrTwocostnoEmbed - - - -def eTwoThrLinclCostfct(clauses): - - """ - - Parameters - ---------- - clauses : List - clauses to analyze in the problem instance - - Returns - ------- - Costfunction : function - the classical function for the problem instance, which takes a dictionary of measurement results as input - """ - - def setupaClCostfct(res_dic): - energy = 0 - total_counts = 0 - for state in list(res_dic.keys()): - obj = 0 - #ljust do a minus 1 op if state is equiv to a given condition clause - for index in range(len(clauses)): - clause = clauses[index] - sum = 0 - for index_aj in range(0,2): - sum += int(state[clause[index_aj]]) - if sum == clause[3] % 2: - obj -= 1 - energy += obj * res_dic[state] - total_counts += res_dic[state] - #print(energy/total_counts) - - return energy/total_counts - - return setupaClCostfct - - -def init_state(qv): - # all in 0 - h(qv) - return qv - diff --git a/src/qrisp/algorithms/qaoa/problems/maxCliqueInfrastr.py b/src/qrisp/algorithms/qaoa/problems/maxCliqueInfrastr.py deleted file mode 100644 index 7f0831c2..00000000 --- a/src/qrisp/algorithms/qaoa/problems/maxCliqueInfrastr.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -\******************************************************************************** -* Copyright (c) 2023 the Qrisp authors -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* This Source Code may also be made available under the following Secondary -* Licenses when the conditions for such availability set forth in the Eclipse -* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 -* with the GNU Classpath Exception which is -* available at https://www.gnu.org/software/classpath/license.html. -* -* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 -********************************************************************************/ -""" - -from qrisp import h, rz, rx , rzz -import itertools -import networkx as nx - - - - -def maxCliqueCostOp(G): - - """ - | Based on PennyLane unconstrained mixer implementation. - | Initial state in :math:`(|0>+|1>)^{\otimes n}` . - | For explanation see the PennyLane implementation. - - Parameters - ---------- - G : nx.Graph - Graph of the problem instance - - Returns - ------- - QuantumCircuit: qrisp.QuantumCircuit - the Operator applied to the circuit-QuantumVariable - - """ - G_compl = nx.complement(G) - def partialcostMixer(qv, gamma): - for pair in list(G_compl.edges()): - #cx(qv[pair[0]], qv[pair[1]]) - rzz(3*gamma, qv[pair[0]], qv[pair[1]]) - rz(-gamma, qv[pair[0]]) - rz(-gamma, qv[pair[1]]) - rz(gamma, qv) - #return qv - - return partialcostMixer - - - -def maxCliqueCostfct(G): - """ - - Parameters - ---------- - G : nx.Graph - Graph of the problem instance - - Returns - ------- - Costfunction : function - the classical function for the problem instance, which takes a dictionary of measurement results as input - - """ - - def aClcostFct(res_dic): - tot_energy = 0.001 - tot_counts = 0 - for state in res_dic.keys(): - # we assume solution is right - temp = True - energy = 0 - intlist = [s for s in range(len(list(state))) if list(state)[s] == "1"] - # get all combinations of vertices in graph that are marked as |1> by the solution - combinations = list(itertools.combinations(intlist, 2)) - # if any combination is found in the list of G.edges(), the solution is wrong, and energy == 0 - for combination in combinations: - if combination not in G.edges(): - temp = False - break - # else we just add the number of marked as |1> nodes - if temp: - energy = -len(intlist) - tot_energy += energy * res_dic[state] - tot_counts += res_dic[state] - - #print(tot_energy/tot_counts) - - return tot_energy/tot_counts - - return aClcostFct - - - -def init_state(qv): - h(qv) - return qv \ No newline at end of file diff --git a/src/qrisp/algorithms/qaoa/problems/maxIndepSetInfrastr.py b/src/qrisp/algorithms/qaoa/problems/maxIndepSetInfrastr.py deleted file mode 100644 index 6213021f..00000000 --- a/src/qrisp/algorithms/qaoa/problems/maxIndepSetInfrastr.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -\******************************************************************************** -* Copyright (c) 2023 the Qrisp authors -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* This Source Code may also be made available under the following Secondary -* Licenses when the conditions for such availability set forth in the Eclipse -* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 -* with the GNU Classpath Exception which is -* available at https://www.gnu.org/software/classpath/license.html. -* -* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 -********************************************************************************/ -""" - -from qrisp import h, rz, rx ,rzz -import itertools - - -def maxIndepSetCostOp(G): - """ - - Parameters - ---------- - G : nx.Graph - The graph for the problem instance. - - Returns - ------- - function - A Python function receiving a ``QuantumVariable`` and real parameter $\gamma$. - This function performs the application of the cost operator associated to the graph $G$. - - """ - - def partialcostMixer(qv, gamma): - for pair in list(G.edges()): - #cx(qv[pair[0]], qv[pair[1]]) - rzz(3*gamma, qv[pair[0]], qv[pair[1]]) - rz(-gamma, qv[pair[0]]) - rz(-gamma, qv[pair[1]]) - rz(gamma, qv) - - return partialcostMixer - - -def maxIndepSetclCostfct(G): - """ - - Parameters - ---------- - G : nx.Graph - The Graph for the problem instance. - - Returns - ------- - cl_cost_function : function - The classical function for the problem instance, which takes a dictionary of measurement results as input. - - """ - - def cl_cost_function(res_dic): - tot_energy = 0.001 - for state, prob in res_dic.items(): - temp = True - energy = 0 - indices = [index for index, value in enumerate(state) if value == '1'] - # get all combinations of vertices in graph that are marked as |1> by the solution - combinations = list(itertools.combinations(indices, 2)) - # if any combination is found in the list of G.edges(), the solution is invalid, and energy == 0 - for combination in combinations: - if combination in G.edges(): - temp = False - break - # else we subtract the number of vertices marked as |1> - if temp: - energy = -len(indices) - tot_energy += energy*prob - - return tot_energy - - return cl_cost_function - diff --git a/src/qrisp/algorithms/qaoa/problems/maxSatInfrastr.py b/src/qrisp/algorithms/qaoa/problems/maxSatInfrastr.py deleted file mode 100644 index 13ef3d29..00000000 --- a/src/qrisp/algorithms/qaoa/problems/maxSatInfrastr.py +++ /dev/null @@ -1,233 +0,0 @@ -""" -\******************************************************************************** -* Copyright (c) 2023 the Qrisp authors -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* This Source Code may also be made available under the following Secondary -* Licenses when the conditions for such availability set forth in the Eclipse -* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 -* with the GNU Classpath Exception which is -* available at https://www.gnu.org/software/classpath/license.html. -* -* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 -********************************************************************************/ -""" - - -from qrisp import QuantumVariable, h, barrier, rz, rx , cx, QuantumArray,x -import numpy as np -from scipy.optimize import minimize -from sympy import Symbol -import itertools -from _collections_abc import Iterable - - - - - -def maxSatCostOp(problem): - - """ - Implementation for MaxSat Cost-Operator, in accordance to the original QAOA-paper. - - For each clause :math:`C_i` apply :math:`C_i(x) = 1 - \prod_j x_j` (replace :math:`x_j` with - :math:`(1-x_j)` if :math:`x_j` is negated). The problem operator is acquired with the substitution - :math:`x_j = (I - Z_j)` , where :math:`Z_j` is the Pauli- :math:`Z` operator applied to the :math:`j` -th qubit. - - Parameters - ---------- - clauses : List(Lists) - Clauses to be considered for MaxSat. Should be given as a list of lists and 1-indexed instead 0-indexed, see example - - - Returns - ------- - QuantumCircuit: qrisp.QuantumCircuit - the Operator applied to the circuit-QuantumVariable - - Examples - -------- - >>> clauses11 = (6, [[1,2,-3], [1,4,-6], [4,5,6], [1,3,-4], [2,4,5], [1,3,5], [-2,-3,6]]) - - | Explanation: - | First entry of tuple is the number of variables, second is the clauses - | Clause [1, 2, -4] is fulfilled by the QuantumStates "1100", "1110" - - * if the sign is positive : the index has of the QuantumVariable to be "1", if negative sign the index has to be "0". - * indices not mentioned can be "0" or "1" (the third integer in this case). - * start with 1 in your clauses! because ``int 0`` has no sign and this is problematic. - * we want to keep the mathematical formulation, so no handing over strings! - - Assign the operators - - >>> cost_operator=maxSatCostOp(clauses11) - >>> mixer=RX_mixer - - """ - - - clauses = problem[1] - - if not isinstance(clauses, Iterable): - raise Exception("Wrong structure of problem - clauses have to be iterable!") - satlen = len(clauses[0]) - for index in range(len(clauses)): - if not satlen ==len(clauses[index]): - raise Exception("Wrong structure of problem - inconsistent length in clause " + str(index)) - if not isinstance(clauses[index], Iterable): - raise Exception("Wrong structure of problem - each clauses has to be an array! Problem in clause " + str(index)) - for item in clauses[index]: - if not isinstance(item, int): - raise Exception("Wrong structure of problem - each literal has to an int!") - - - - def maxSatcostEmbed(qv , gamma): - #clauses = clauses - - qc = qv - - for numClause in range(len(clauses)): - #go through clauses - clause = clauses[numClause] - - for lenofCombination in range(1,satlen+1): - # get all combinations to assign rz, rzz, rzzz, rzzzzzz.... - combinations = list(itertools.combinations(clause, lenofCombination)) - #print(combinations) - - #len == 1: just rz - if lenofCombination == 1: - for index in range(len(combinations)): - # sign for rz mixer - signu = - np.sign(combinations[index][0]) - # always have the "-1" at the end since clauses are not initiated with 0, see above - #print(abs( combinations[index][0] - 1 )) - rz(signu * gamma/8, qc[ abs( combinations[index][0]) -1 ] ) - - else: - #for all combinations of this length - for index in range(len(combinations)): - signu = 1 - - # up to final value in combination perform cx gates --> set up the rzz, rzzz, rzzzz ... gates - for index2 in range(lenofCombination-1): - # (also remember the sign) - signu *= - np.sign(combinations[index][index2]) - cx(qc[abs( combinations[index][index2] ) -1], qc[abs( combinations[index][index2+1] ) -1]) - - signu *= np.sign(combinations[index][lenofCombination-1]) - # finalize rzz, rzzz, rzzzz ... gates - rz(signu*gamma/8, qc[abs( combinations[index][lenofCombination-1] ) -1]) - - # and cnot gates back - for index2 in reversed(range(lenofCombination-1)): - cx( qc[abs(combinations[index][index2] ) -1], qc[abs(combinations[index][index2+1] ) -1 ]) - - return qc - - return maxSatcostEmbed - - - - -def clausesdecoder(problem): - """ - Decodes the clause arrays to represent binary bitstrings, that fulfill the clauses - --> used to calculate objective function, i.e. the classical cost function - - - Parameters - ---------- - clauses : List(List) - clauses to be considered for maxSat (! 1-indexed, instead of 0-indexed, see example above) - numVars : int - the total amount of Variables considered within the clauses - - Returns - ------- - decodedClauses : Iterable(tuple) - clauses as bitstrings, to be compared to the QAOA-bitstring results - - Examples - -------- - As above: - - >>> clauses11 = [[1,2,-3], [1,4,-6], [4,5,6], [1,3,-4], [2,4,5], [1,3,5], [-2,-3,6]] - >>> decodedClauses = clausesdecoder( clauses = clauses11, numVars = 6) - - Assign ``cost_function`` - - >>> cl_cost_function=maxSatclCostfct(decodedClauses) - - - """ - - numVars = problem[0] - clauses = problem[1] - - # create all bitstring possibilites - binStrings = list(itertools.product([0,1], repeat=numVars)) - decodedClauses = [] - for indexClauses in range(len(clauses)): - # create temp placeholder - tempIter = binStrings - clause = clauses[indexClauses] - for index in clause: - val = abs(index)-1 - temp = 1 if np.sign(index) == 1 else 0 - # placeholder only keeps to bitstrings that fulfill the clause-conditions - tempIter = [ thing for thing in tempIter if thing[val] == temp ] - # turn to str, since QAOA return is dict of str - tempIter2 = ["".join(map(str, item)) for item in tempIter] - decodedClauses.append(tuple(tempIter2)) - - return decodedClauses - - - -def maxSatclCostfct(problem): - - """ - - Parameters - ---------- - decodedClauses : Iterable(tuple) - clauses as bitstrings, as returned by ``clausesdecoder()`` helper function - - - Returns - ------- - Costfunction : function - the classical function for the problem instance, which takes a dictionary of measurement results as input - - """ - - - decodedClauses = clausesdecoder(problem) - - def setupaClCostfct(res_dic): - energy = 0 - total_counts = 0 - for state in list(res_dic.keys()): - obj = 0 - #literally just do a minus 1 op if state is equiv to a given condition - for index in range(len(decodedClauses)): - if state in decodedClauses[index]: - obj -= 1 - energy += obj * res_dic[state] - total_counts += res_dic[state] - #print(energy/total_counts) - - return energy/total_counts - - return setupaClCostfct - - -def init_state(qv): - h(qv) - return qv - diff --git a/src/qrisp/algorithms/qaoa/problems/maxSetPackInfrastr.py b/src/qrisp/algorithms/qaoa/problems/maxSetPackInfrastr.py deleted file mode 100644 index 09527317..00000000 --- a/src/qrisp/algorithms/qaoa/problems/maxSetPackInfrastr.py +++ /dev/null @@ -1,202 +0,0 @@ -""" -\******************************************************************************** -* Copyright (c) 2023 the Qrisp authors -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* This Source Code may also be made available under the following Secondary -* Licenses when the conditions for such availability set forth in the Eclipse -* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 -* with the GNU Classpath Exception which is -* available at https://www.gnu.org/software/classpath/license.html. -* -* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 -********************************************************************************/ -""" - -from qrisp import QuantumVariable, mcx, rz, x, rx, auto_uncompute - -from qrisp import control -from collections.abc import Iterable -# This is pretty much maxIndependent set, but with a twist -# instead of swapping out singular qubits, we swap out whole predefined sets. -# This means we apply the mixers to all elements in the sets. - -# we have a graph of 9 vertices - -####################### -## reformulate using @auto_uncompute !!! -## https://www.qrisp.eu/reference/Core/Uncomputation.html - - -def maxSetPackCostOp(problem): - - """ - | Create the cost/problem operator for this problem instance. The swapping rule is to swap a set in and out of the solution, if it is not intersecting with any other set. - | Idea - Per set: - - * Create ancillas for every element, they represent these elements - * Perform multi controlled x operations on each ancilla - * Controls are given by sets with also contain the considered element - * If all controls are "0" (see ``ctrl_state`` for ``mcx``-operation) we set this ancilla to "1" - - | Then perform mcx on the qubit describing the set as follows: - | If all ancillas are "1" this means the qubits describing the sets contain no intersections with the considered set. We can then swap the set in (or out). - | Afterwards uncompute the ancillas. - - Parameters - ---------- - sets : list(Lists) - The sets the universe is seperated into as by the problem definition - - universe: Tuple - The universe for the problem instance, i.e. all possible values (all graph vertices) - - Returns - ------- - QuantumCircuit: qrisp.QuantumCircuit - the Operator applied to the circuit-QuantumVariable - - Examples - -------- - Definition of the sets, given as list of lists. Full universe ``sol`` is given by the amount of elements (+1, since its 0-indexed) - - >>> sets = [[0,7,1],[6,5],[2,3],[5,4],[8,7,0],[1]] - >>> sol = 9 - >>> problem = [sol, sets] - - The relations between the sets, i.e. which vertice is in which other sets - - >>> print(get_neighbourhood_relations(sets, len_universe=len(sol))) - - Assign the operators - - >>> cost_fun = maxSetPackclCostfct(problem) - >>> mixerOp = RZ_mixer - >>> costOp = maxSetPackCostOp(problem) - """ - - universe = list(range(problem[0])) - sets = problem[1] - - if not isinstance(sets, Iterable): - raise Exception("Wrong structure of problem - clauses have to be iterable!") - for clause in sets: - if not isinstance(clause, Iterable): - raise Exception("Wrong structure of problem - each set has to be a tuple!") - for item in clause: - if not isinstance(item, int): - raise Exception("Wrong structure of problem - each literal has to an int!") - - # get neigbhorhood relations from helper function - nbh_rel = get_neighbourhood_relations(problem) - - @auto_uncompute - def theCostOpEmbedded(qv, gamma): - # check all sets - for set_index in range(len(sets)): - # get set elements and create an ancilla for every set element - nodes = sets[set_index] - ancillas = QuantumVariable(len(nodes)) - # go through all ancillas and, equivalently set elements - for ancilla_index in range(len(ancillas)): - # if the element is only in one set, we can set this ancilla to 1 - if len(nbh_rel[nodes[ancilla_index]])<2: - x(ancillas[ancilla_index]) - continue - # else save the sets with also contain the considered element - nbh_sets_list = [ item for item in nbh_rel[nodes[ancilla_index]] if item != set_index] - # perform mcx on ancilla, control given by the relevant set - mcx([qv[nbh_sets_index] for nbh_sets_index in nbh_sets_list], ancillas[ancilla_index], ctrl_state= "0" * len(nbh_sets_list)) - # perform mcx gate on the qubit describing the considered set - with control(ancillas): - rx(gamma, qv[set_index]) - - #ancillas.uncompute() - - return theCostOpEmbedded - - -def get_neighbourhood_relations(problem): - """ - helper function to return a dictionary describing neighbourhood relations in the sets, i.e. for each element in the universe, gives the info in which the element is contained in. - - - Parameters - ---------- - problem : List - The problem definition, as described above - - Returns - ------- - neighbourhood relation dictionary : dict - | keys: all universe elements (all graph vertices) - | values: per universe element the sets it is contained in - - """ - - sets = problem[1] - - n_dict = {} - for index_node in range(problem[0]): - adding_list = [index_set for index_set in range(len(sets)) if index_node in sets[index_set]] - #if len(adding_list)>1: - n_dict[index_node] = adding_list - return n_dict - - -def maxSetPackclCostfct(problem): - """ - create the classical cost function for the problem instance - - Parameters - ---------- - problem : list - The problem definition, as described above - - Returns - ------- - cl_cost_function : function - The classical function for the problem instance, which takes a dictionary of measurement results as input. - - """ - - universe = list(range(problem[0])) - sets = problem[1] - - def cl_cost_function(res_dic): - tot_energy = 0 - for state, prob in res_dic.items(): - list_universe = [True]*len(universe) - temp = True - energy = 0 - # get all sets marked by the solution - indices = [index for index, value in enumerate(state) if value == '1'] - sol_sets = [sets[index] for index in indices] - - for set in sol_sets: - for val in set: - if list_universe[val]: - # if the value appears in the sets, set this value to False - list_universe[val] = False - else: - # if the value is False, this element appeared in another solution set - # the sets then intersect and the solution is invalid - temp = False - break - if temp: - energy = -len(indices) - tot_energy += energy*prob - - return tot_energy - - return cl_cost_function - - -def init_state(qv): - # all in 0 - return qv - - diff --git a/src/qrisp/algorithms/qaoa/problems/minSetCoverInfrastr.py b/src/qrisp/algorithms/qaoa/problems/minSetCoverInfrastr.py deleted file mode 100644 index 8cc6493b..00000000 --- a/src/qrisp/algorithms/qaoa/problems/minSetCoverInfrastr.py +++ /dev/null @@ -1,195 +0,0 @@ -""" -\******************************************************************************** -* Copyright (c) 2023 the Qrisp authors -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* This Source Code may also be made available under the following Secondary -* Licenses when the conditions for such availability set forth in the Eclipse -* Public License, v. 2.0 are satisfied: GNU General Public License, version 2 -* with the GNU Classpath Exception which is -* available at https://www.gnu.org/software/classpath/license.html. -* -* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 -********************************************************************************/ -""" - - -from qrisp import QuantumVariable, mcx, rz, x, rx -from qrisp import control -from _collections_abc import Iterable - - - - -def minSetCoverCostOp(sets, universe): - """ - | Create the cost/problem operator for this problem instance. Swapping rule is to swap a set in and out of the solution, if all elements are covered by other sets - | Idea - Per set: - - * create ancillas for every element, they represent these elements - * ancillas are set to "1" by default - * perform multi controlled x operations on each ancilla - * controls are given by sets with also contain the considered element - * if all controls are "0" (see ``ctrl_state`` for ``mcx``-operation) we set this ancilla to "0" - - | Then perform mcx on the qubit describing the set: - | If all ancillas are "1" this means that, for all elements in the list, there is already (atleast) one set in the bitstring describing the solution sets, which already contains this element. The set can then be swapped in (or out) of the solution, since all elements are covered by other sets - | Afterwards uncompute the ancillas - - Parameters - ---------- - sets : list(Lists) - The sets the universe is seperated into as by the problem definition - - universe: Tuple - The universe for the problem instance, i.e. all possible values (all graph vertices) - - Returns - ------- - QuantumCircuit: qrisp.QuantumCircuit - the Operator applied to the circuit-QuantumVariable - - Examples - -------- - - Definition of the sets, given as list of lists. Full universe ``sol`` is given as a tuple - >>> sets = [[0,7,1],[6,5],[2,3],[5,4],[8,7,0],[1]] - >>> sol = (0,1,2,3,4,5,6,7,8) - - The relations between the sets, i.e. which vertice is in which other sets - - >>> print(get_neighbourhood_relations(sets, len_universe=len(sol))) - - Assign operators - >>> cost_fun = minSetCoverclCostfct(sets=sets,universe = sol) - >>> mixerOp = RZ_Mixer() - >>> costOp = minSetCoverCostOp(sets=sets, universe=sol) - """ - - if not isinstance(sets, Iterable): - raise Exception("Wrong structure of problem - clauses have to be iterable!") - for clause in sets: - if not isinstance(clause, Iterable): - raise Exception("Wrong structure of problem - each set has to be a tuple!") - for item in clause: - if not isinstance(item, int): - raise Exception("Wrong structure of problem - each literal has to an int!") - - # get neighbourhood relations from helper function - nbh_rel = get_neighbourhood_relations(sets, len(universe)) - - def theCostOpEmbedded(qv, gamma): - # go through all sets - for set_index in range(len(sets)): - # get set elements and create an ancilla for every set element - nodes = sets[set_index] - ancillas = QuantumVariable(len(nodes)) - # go through all ancillas and, equivalently, set elements - for ancilla_index in range(len(ancillas)): - # if the considered element is in no other set, we cannot swap this set... - if len(nbh_rel[nodes[ancilla_index]])<2: - #break ? - continue - # set ancilla to one - x(ancillas[ancilla_index]) - # get relevant neighbourhood sets for this element - nbh_sets_list = [item for item in nbh_rel[nodes[ancilla_index]] if item != set_index] - # perform mcx on the ancilla, controlled by the relevant set qubits - mcx([qv[nbh_sets_index] for nbh_sets_index in nbh_sets_list], ancillas[ancilla_index], ctrl_state= "0"*len(nbh_sets_list)) - # perform mcx on the set qubit if all elements are covered by other sets, i.e. all ancillas == "1" - with control(ancillas): - rx(gamma, qv[set_index]) - - # uncompute ancillas - ancillas.uncompute() - - return theCostOpEmbedded - - - - - - - -def get_neighbourhood_relations(sets, len_universe): - """ - helper function to return a dictionary describing neighbourhood relations in the sets, i.e. for each element in the universe, gives the info in which the element is contained in. - - - Parameters - ---------- - sets : list(Lists) - The sets the universe is seperated into as by the problem definition - - len_universe: int - The number of elements in the universe - - Returns - ------- - neighbourhood relation dictionary : dict - | keys: all universe elements (all graph vertices) - | values: per universe element the sets it is contained in - - - """ - n_dict = {} - for index_node in range(len_universe): - adding_list = [index_set for index_set in range(len(sets)) if index_node in sets[index_set]] - #if len(adding_list)>1: - n_dict[index_node] = adding_list - return n_dict - - -def minSetCoverclCostfct(sets, universe): - - """ - create the classical cost function for the problem instance - - Parameters - ---------- - sets : list(Lists) - The sets the universe is seperated into as by the problem definition - - universe: Tuple - The universe for the problem instance, i.e. all possible values (all graph vertices) - - Returns - ------- - Costfunction : function - the classical function for the problem instance, which takes a dictionary of measurement results as input - - """ - def setupaClCostfct(res_dic): - energy = 0 - total_counts = 0 - for state in list(res_dic.keys()): - obj = 0.01 - intlist = [s for s in range(len(list(state))) if list(state)[s] == "1"] - sol_sets = [sets[index] for index in intlist] - res = () - # get all elements that are contained within the solution sets - for seto in sol_sets: - res = tuple(set(res + tuple(seto))) - # this is problematic... wrong solutions are being disregarded... - if res != universe: - continue - obj += len(intlist) - energy += obj * res_dic[state] - total_counts += res_dic[state] - #print(energy/total_counts) - - return energy/total_counts - - return setupaClCostfct - - - -def init_state(qv): - x(qv) - return qv - - - From 20d6426d2e3e2ade86fed093c4ac5e90d401aec1 Mon Sep 17 00:00:00 2001 From: Rene Zander Date: Thu, 17 Oct 2024 22:41:13 +0200 Subject: [PATCH 8/8] Updated test for QAOAtrain_func --- tests/test_QAOAmaxClique.py | 14 +++--- tests/test_QAOAtrain_func.py | 87 +++++++++++------------------------- 2 files changed, 33 insertions(+), 68 deletions(-) diff --git a/tests/test_QAOAmaxClique.py b/tests/test_QAOAmaxClique.py index 6a92f124..9504543d 100644 --- a/tests/test_QAOAmaxClique.py +++ b/tests/test_QAOAmaxClique.py @@ -18,7 +18,7 @@ from qrisp import QuantumVariable -from qrisp.qaoa import QAOAProblem, RZ_mixer, approximation_ratio, create_max_indep_set_cl_cost_function, create_max_indep_set_mixer, max_indep_set_init_function +from qrisp.qaoa import QAOAProblem, RZ_mixer, create_max_indep_set_cl_cost_function, create_max_indep_set_mixer, max_indep_set_init_function, approximation_ratio import networkx as nx @@ -28,11 +28,11 @@ def test_QAOAmaxClique(): G_complement = nx.complement(G) qarg = QuantumVariable(G.number_of_nodes()) - qaoa_max_indep_set = QAOAProblem(cost_operator=RZ_mixer, - mixer=create_max_indep_set_mixer(G_complement), - cl_cost_function=create_max_indep_set_cl_cost_function(G_complement), - init_function=max_indep_set_init_function) - results = qaoa_max_indep_set.run(qarg=qarg, depth=5) + qaoa_max_clique = QAOAProblem(cost_operator=RZ_mixer, + mixer=create_max_indep_set_mixer(G_complement), + cl_cost_function=create_max_indep_set_cl_cost_function(G_complement), + init_function=max_indep_set_init_function) + results = qaoa_max_clique.run(qarg=qarg, depth=5) cl_cost = create_max_indep_set_cl_cost_function(G_complement) @@ -48,6 +48,6 @@ def test_QAOAmaxClique(): optimal_sol = "".join(["1" if index in cliques[max_index] else "0" for index in range(G.number_of_nodes())]) # approximation ratio test - assert approximation_ratio(results, optimal_sol, cl_cost)>=0.5 + assert approximation_ratio(results, optimal_sol, cl_cost)>=0.5 diff --git a/tests/test_QAOAtrain_func.py b/tests/test_QAOAtrain_func.py index 303bb6fd..23617a3a 100644 --- a/tests/test_QAOAtrain_func.py +++ b/tests/test_QAOAtrain_func.py @@ -17,76 +17,41 @@ """ -from qrisp.qaoa import QAOAProblem -from qrisp.qaoa.problems.maxCliqueInfrastr import maxCliqueCostfct,maxCliqueCostOp,init_state -from qrisp.qaoa.mixers import RX_mixer from qrisp import QuantumVariable +from qrisp.qaoa import QAOAProblem, RZ_mixer, create_max_indep_set_cl_cost_function, create_max_indep_set_mixer, max_indep_set_init_function, approximation_ratio import networkx as nx -import matplotlib.pyplot as plt -import numpy as np def test_QAOAtrain_func(): - giraf = nx.erdos_renyi_graph(9,0.7) - #draw graph - #nx.draw(giraf,with_labels = True) - #plt.show() + G = nx.erdos_renyi_graph(9, 0.7, seed = 133) + G_complement = nx.complement(G) + qarg = QuantumVariable(G.number_of_nodes()) + qaoa_max_clique = QAOAProblem(cost_operator=RZ_mixer, + mixer=create_max_indep_set_mixer(G_complement), + cl_cost_function=create_max_indep_set_cl_cost_function(G_complement), + init_function=max_indep_set_init_function) + training_func = qaoa_max_clique.train_function(qarg=qarg, depth=5) - #Instanciate QAOA - #mixer gets Graph as argument - #cost function gets graph as argument - QAOAinstance = QAOAProblem(cost_operator= maxCliqueCostOp(giraf), mixer= RX_mixer, cl_cost_function=maxCliqueCostfct(giraf)) - QAOAinstance.set_init_function(init_function=init_state) - qarg2 = QuantumVariable(giraf.number_of_nodes()) - training_func = QAOAinstance.train_function( qarg=qarg2, depth=5 ) - qarg3 = QuantumVariable(giraf.number_of_nodes()) - training_func(qarg3) - theNiceQAOA = qarg3.get_measurement() + qarg2 = QuantumVariable(G.number_of_nodes()) + training_func(qarg2) + results = qarg2.get_measurement() + cl_cost = create_max_indep_set_cl_cost_function(G_complement) - import itertools - def aClcostFct(state, G): - # we assume solution is right - temp = True - energy = 0 - intlist = [s for s in range(len(list(state))) if list(state)[s] == "1"] - # get all combinations of vertices in graph that are marked as |1> by the solution - combinations = list(itertools.combinations(intlist, 2)) - # if any combination is not found in the list of G.edges(), the solution is wrong, and energy == 0 - for combination in combinations: - if combination not in G.edges(): - temp = False - # else we just add the number of marked as |1> nodes - if temp: - energy = -len(intlist) - return(energy) + cliques = list(nx.find_cliques(G)) + + max = 0 + max_index = 0 + for index, clique in enumerate(cliques): + if len(clique) > max: + max = len(clique) + max_index = index + + optimal_sol = "".join(["1" if index in cliques[max_index] else "0" for index in range(G.number_of_nodes())]) + + # approximation ratio test + assert approximation_ratio(results, optimal_sol, cl_cost)>=0.5 - - # find the nx solutions - the_it = list(nx.find_cliques(giraf)) - the_itt = [set(it) for it in the_it] - print(the_itt) - - #print the ideal solutions - #print("5 most likely Solutions") - #check if condition is fullfilled for 5 best sols - maxfive = sorted(theNiceQAOA, key=theNiceQAOA.get, reverse=True)[:5] - print(maxfive) - for name in theNiceQAOA.keys(): # for name, age in dictionary.iteritems(): (for Python 2.x) - if name in maxfive: - print(name) - print(aClcostFct(name, giraf)) - if aClcostFct(name, giraf) < 0: - intlist = [s for s in range(len(list(name))) if list(name)[s] == "1"] - intlist2 = set(intlist) - set_g = False - for seto in the_itt: - if intlist2.issubset(seto): - set_g = True - break - assert set_g - #assert set(intlist) in the_itt - #assert aClcostFct(name, giraf) <= -1