From 8403de1726ba6307563bdd4d321ca0e529bb5b6f Mon Sep 17 00:00:00 2001 From: shangtai Date: Fri, 11 Aug 2023 02:22:39 +0800 Subject: [PATCH 1/4] fixing previous mvc branch --- examples/mvc/README.md | 227 +++++++++++++++++++++++++++++++++++++ examples/mvc/main.py | 83 ++++++++++++++ examples/mvc/mvc.csv | 17 +++ examples/mvc/mvc.py | 141 +++++++++++++++++++++++ examples/mvc/qubo_utils.py | 134 ++++++++++++++++++++++ 5 files changed, 602 insertions(+) create mode 100644 examples/mvc/README.md create mode 100755 examples/mvc/main.py create mode 100644 examples/mvc/mvc.csv create mode 100644 examples/mvc/mvc.py create mode 100644 examples/mvc/qubo_utils.py diff --git a/examples/mvc/README.md b/examples/mvc/README.md new file mode 100644 index 0000000000..c29cff478b --- /dev/null +++ b/examples/mvc/README.md @@ -0,0 +1,227 @@ +```python +import networkx as nx +import csv +import matplotlib.pyplot as plt + +from mvc import qubo_mvc, qubo_mvc_penalty, mvc_feasibility, mvc_easy_fix, mvc_energy +``` + +# MVC Definition + +Given an undirected graph with a set of nodes V and edges E, A vertex cover of a graph is a set of nodes that includes at +least one endpoint of every edge of the graph. The Minimum Vertex Cover (MVC) problem is an optimisation problem +that find smallest vertex cover of a given graph. + +# Load graph from csv + + +```python +def load_csv(filename:str): + """ Load graph from csv file + """ + + with open(filename, 'r', newline='') as f: + reader = csv.reader(f) + data = [[int(row[0]), int(row[1]), float(row[2])] for row in reader] + + nodes = [k0 for k0, k1, v in data if k0 == k1] + edges = [[k0, k1] for k0, k1, v in data if k0 != k1] + + g = nx.Graph() + g.add_nodes_from(nodes) + g.add_edges_from(edges) + + node_weights = {k0: {'weight': v} for k0, k1, v in data if k0 == k1} + edge_weights = {(k0, k1): {'weight': v} for k0, k1, v in data if k0 != k1} + + nx.set_edge_attributes(g, edge_weights) + nx.set_node_attributes(g, node_weights) + + return g +``` + +csv Format: + +0,0,0.4696822179283675 +1,1,0.6917392308135916 +2,2,0.7130420958314817 +3,3,0.49342677332980056 +4,4,0.49661600571433673 +5,5,0.55601135361347 +6,6,0.2831095711244086 +7,7,0.18737823232636885 +0,1,0.98602725407379 +1,2,0.935853268681996 +2,3,0.23080434023289664 +4,5,0.25521731195623476 +5,6,0.37096743935296606 +6,7,0.04408120693490547 +0,2,0.5619060764177991 +4,7,0.48402095290884917 +1,6,0.7106584134580318 + + + +```python +g = load_csv('mvc.csv') +``` + + +```python +pos = nx.spring_layout(g, seed=1234) +nx.draw(g, pos) +``` + + + +![png](README_files/README_7_0.png) + + + + +```python +print(f'Number of node is {len(g.nodes)}') +print(f'Number of edge is {len(g.edges)}') +``` + + Number of node is 8 + Number of edge is 9 + + +# QUBO formulation + +## Estimate penalty + + +```python +penalty = qubo_mvc_penalty(g) +print(f'The penalty is {penalty}') +``` + + The penalty is 1.6548482220717595 + + +## Formulate QUBO (weighted minimum vertex cover) + + +```python +linear, quadratic = qubo_mvc(g, penalty=penalty) +print(f'Number of linear terms: {len(linear)}') +print(f'Number of quadratic terms: {len(quadratic)}\n') +``` + + Number of linear terms: 8 + Number of quadratic terms: 9 + + + +## Generate random solutions + + +```python +import numpy as np +rng = np.random.default_rng(seed=1234) +random_solution = {i: rng.integers(2) for i in g.nodes} +print(f'The random solution is {random_solution}\n') +``` + + The random solution is {0: 1, 1: 1, 2: 1, 3: 0, 4: 0, 5: 1, 6: 0, 7: 0} + + + +## Check feasibility + + +```python +feasibility = mvc_feasibility(g, random_solution) +print(f'The feasibility is {feasibility}\n') +``` + + The feasibility is False + + + +## Fix broken constraints + + +```python +fixed_solution = mvc_easy_fix(g, random_solution) +print(f'After fixation, the solution is {fixed_solution}\n') +``` + + After fixation, the solution is {0: 1, 1: 1, 2: 1, 3: 0, 4: 1, 5: 1, 6: 1, 7: 0} + + + + +```python +feasibility = mvc_feasibility(g, fixed_solution) +print(f'The feasibility is {feasibility}\n') +``` + + The feasibility is True + + + +## Visualisation + + +```python +fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(12.8,4.8)) +colour_b4fix = ['C1' if random_solution[n] else 'C0' for n in g.nodes] +colour_fixed = ['C1' if fixed_solution[n] else 'C0' for n in g.nodes] +nx.draw(g, pos, node_color=colour_b4fix, ax=ax0) +ax0.set_title(f'Vertex cover in orange before fix') +nx.draw(g, pos, node_color=colour_fixed, ax=ax1) +ax1.set_title(f'Vertex cover in orange after fix') +``` + + + + + Text(0.5, 1.0, 'Vertex cover in orange after fix') + + + + + +![png](README_files/README_22_1.png) + + + +## Calculate energy + + +```python +energy = mvc_energy(g, fixed_solution) +print(f'The energy is {energy}') +``` + + The energy is 3.2102004750256556 + + +# Hamiltonian of the MVC problem + + +```python +ham = hamiltonian_mvc(g, penalty=penalty, dense=True) +``` + + [Qibo 0.1.8|INFO|2022-11-01 10:26:19]: Using numpy backend on /CPU:0 + + +## Solve the hamiltonian with QAOA + + +```python +from qibo import models, hamiltonians + +# Create QAOA model +qaoa = models.QAOA(ham) + +# Optimize starting from a random guess for the variational parameters +initial_parameters = 0.01 * np.random.uniform(0,1,2) +best_energy, final_parameters, extra = qaoa.minimize(initial_parameters, method="BFGS") +``` + + [Qibo 0.1.8|WARNING|2022-10-31 14:14:37]: Calculating the dense form of a symbolic Hamiltonian. This operation is memory inefficient. diff --git a/examples/mvc/main.py b/examples/mvc/main.py new file mode 100755 index 0000000000..9648206425 --- /dev/null +++ b/examples/mvc/main.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +"""Minimum Vertex Cover""" +import argparse +import csv + +import networkx as nx +from mvc import ( + hamiltonian_mvc, + mvc_easy_fix, + mvc_energy, + mvc_feasibility, + qubo_mvc, + qubo_mvc_penalty, +) +from qubo_utils import binary2spin, spin2QiboHamiltonian + +from qibo import callbacks, hamiltonians, models + +parser = argparse.ArgumentParser() +parser.add_argument("--filename", default="./mvc.csv", type=str) + + +def load_csv(filename: str): + """Load graph from csv file""" + with open(filename, "r", newline="") as f: + reader = csv.reader(f) + data = [[int(row[0]), int(row[1]), float(row[2])] for row in reader] + + nodes = [k0 for k0, k1, v in data if k0 == k1] + edges = [[k0, k1] for k0, k1, v in data if k0 != k1] + + g = nx.Graph() + g.add_nodes_from(nodes) + g.add_edges_from(edges) + + node_weights = {k0: {"weight": v} for k0, k1, v in data if k0 == k1} + edge_weights = {(k0, k1): {"weight": v} for k0, k1, v in data if k0 != k1} + + nx.set_edge_attributes(g, edge_weights) + nx.set_node_attributes(g, node_weights) + + return g + + +def main(filename: str = "./mvc.csv"): + + print(f"Load a graph from {filename} and make it a QUBO") + g = load_csv(filename) + penalty = qubo_mvc_penalty(g) + linear, quadratic = qubo_mvc(g, penalty=penalty) + + print("A random solution with seed 1234 must be infeasible") + import numpy as np + + rng = np.random.default_rng(seed=1234) + random_solution = {i: rng.integers(2) for i in g.nodes} + feasibility = mvc_feasibility(g, random_solution) + assert not feasibility, "The random solution should be infeasible." + + print("Make a naive fix to the violation of the constraint") + fixed_solution = mvc_easy_fix(g, random_solution) + feasibility = mvc_feasibility(g, fixed_solution) + assert feasibility, "The fixed solution should be feasible." + + print("Calculate the energy of the solution") + energy = mvc_energy(g, fixed_solution) + + print("Construct a hamiltonian based on the QUBO representation") + h, J, _ = binary2spin(linear, quadratic) + h = {k: -v for k, v in h.items()} + J = {k: -v for k, v in J.items()} + ham = spin2QiboHamiltonian(h, J) + + print("Construct a hamiltonian directly from a networkx graph") + ham = hamiltonian_mvc(g) + + print("done.") + + +if __name__ == "__main__": + # by defualt, test on the mvc.csv in the same directory + args = parser.parse_args() + main(args.filename) diff --git a/examples/mvc/mvc.csv b/examples/mvc/mvc.csv new file mode 100644 index 0000000000..71ffdc6380 --- /dev/null +++ b/examples/mvc/mvc.csv @@ -0,0 +1,17 @@ +0,0,0.4696822179283675 +1,1,0.6917392308135916 +2,2,0.7130420958314817 +3,3,0.49342677332980056 +4,4,0.49661600571433673 +5,5,0.55601135361347 +6,6,0.2831095711244086 +7,7,0.18737823232636885 +0,1,0.98602725407379 +1,2,0.935853268681996 +2,3,0.23080434023289664 +4,5,0.25521731195623476 +5,6,0.37096743935296606 +6,7,0.04408120693490547 +0,2,0.5619060764177991 +4,7,0.48402095290884917 +1,6,0.7106584134580318 diff --git a/examples/mvc/mvc.py b/examples/mvc/mvc.py new file mode 100644 index 0000000000..7611a74c31 --- /dev/null +++ b/examples/mvc/mvc.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +import networkx as nx +from qubo_utils import binary2spin, spin2QiboHamiltonian + + +def qubo_mvc(g: nx.Graph, penalty=None): + """Given a graph g, return the QUBO of Minimum Vertex Cover (MVC) problem. + + If penalty is not given, it is inferred from g. + + Args: + g (nx.Graph): graph + penalty (float): penalty weight of the constraints + + Returns: + linear (Dict[Any, float]): linear terms + quadratic (Dict[(Any, Any), float]): quadratic terms + + """ + + q = {(k, k): v for k, v in g.nodes(data="weight")} + + if penalty is None: + penalty = qubo_mvc_penalty(g) + + for s, d in g.edges: + + q[(s, s)] -= penalty + q[(d, d)] -= penalty + + if s > d: + s, d = d, s + + if (s, d) not in q: + q[(s, d)] = penalty + else: + q[(s, d)] += penalty + + linear = {k0: v for (k0, k1), v in q.items() if k0 == k1} + quadratic = {(k0, k1): v for (k0, k1), v in q.items() if k0 != k1 and v != 0} + + return linear, quadratic + + +def qubo_mvc_penalty(g: nx.Graph): + """Find appropriate penalty weight to ensure feasibility + + Args: + g (nx.Graph): graph + + Returns: + penalth (float): penalty + + """ + + # For NISQ devices, you cannot randomly choose a positive penalty weight, otherwise + # you get infeasible solutions very likely. If all weight equals to 1, + # set penalty to the largest degree will ensure high feasibility. + + highest_degree = max( + sum(g.nodes[k]["weight"] for k in g[i].keys()) for i in g.nodes() + ) + return highest_degree + + +def mvc_feasibility(g: nx.Graph, solution): + """Check if the solution violates the constraints of the problem + + Args: + g (nx.Graph): graph + solution (Dict[Any, int]): the solution + + Returns: + feasibility (bool): whether the solution meet the constraints + """ + for k0, k1 in g.edges: + if solution[k0] == solution[k1] == 0: + return False + return True + + +def mvc_energy(g: nx.Graph, solution): + """Calculate the energy of the solution on a MVC problem + + The result is based on the assumption that soltuion is feasible. + + Args: + g (nx.Graph): graph + solution (Dict[Any, int]): the solution + + Returns: + energy (float): the energy of the solution + """ + return sum(solution[k] * v for k, v in g.nodes.data("weight")) + + +def mvc_easy_fix(g: nx.Graph, solution): + """Naively fix violation in an out-of--place manner + + Args: + g (nx.Graph): graph + solution (Dict[Any, int]): the solution + + Returns: + solution (Dict[Any, int]): fixed solution + """ + + t = {k: v for k, v in solution.items()} + + for k0, k1 in g.edges: + if t[k0] == t[k1] == 0: + t[k0] = 1 + + return t + + +def hamiltonian_mvc(g: nx.Graph, penalty: float = None, dense: bool = False): + """Given a graph g, return the hamiltonian of Minimum Vertex Cover (MVC) + problem. + + If penalty is not given, it is inferred from g. + + Args: + g (nx.Graph): graph + penalty (float): penalty weight of the constraints + dense (bool): sparse or dense hamiltonian + + Returns: + ham: qibo hamiltonian + + """ + + linear, quadratic = qubo_mvc(g, penalty) + + h, J, _ = binary2spin(linear, quadratic) + h = {k: -v for k, v in h.items()} + J = {k: -v for k, v in J.items()} + + ham = spin2QiboHamiltonian(h, J, dense=dense) + + return ham diff --git a/examples/mvc/qubo_utils.py b/examples/mvc/qubo_utils.py new file mode 100644 index 0000000000..84a508c469 --- /dev/null +++ b/examples/mvc/qubo_utils.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +from typing import ( + Any, + Dict, + Generator, + Hashable, + Iterator, + List, + Optional, + Tuple, + Union, +) + + +def binary2spin( + linear: Dict[int, float], + quadratic: Dict[Tuple[int, int], float], + offset: float = 0, +): + """Convert binary model to spin model + + Please remember to put a negative sign to h and J after using this + function , if you are going to form a mamiltonian from the spin + model. Hamiltonians usually have a leading negative sign in them, + but QUBOs don't. + + Args: + linear (dict): linear term of the binary model + quadratic (dict): quadratic term of the binary model + offset (float): offset of the binary model + + Returns: + h (dict): bias of the spin model + J (dict): interaction of the spin model + offset (float): offset of the spin model + """ + + h = {x: 0.5 * w for x, w in linear.items()} + + J = [] + for (x, y), w in quadratic.items(): + J.append(((x, y), 0.25 * w)) + h[x] += 0.25 * w + h[y] += 0.25 * w + J = dict(J) + + offset += 0.5 * sum(linear.values()) + offset += 0.25 * sum(quadratic.values()) + + return h, J, offset + + +def spin2binary( + h: Dict[int, float], + J: Dict[Tuple[int, int], float], + offset: float = 0, +): + """Convert spin model to binary model + + Please remember to put a negative sign to h and J before using this + function if you extract them from a hamiltonian. Hamiltonians + usually have a leading negative sign in them, but QUBOs don't. + + Args: + h (dict): bias of the spin model + J (dict): interaction of the spin model + offset (float): offset of the spin model + + Returns: + linear (dict): linear term of the binary model + quadratic (dict): quadratic term of the binary model + offset (float): offset of the binary model + """ + + linear = {s: 2.0 * bias for s, bias in h.items()} + + quadratic = [] + for (s, t), bias in J.items(): + quadratic.append(((s, t), 4.0 * bias)) + linear[s] -= 2.0 * bias + linear[t] -= 2.0 * bias + quadratic = dict(quadratic) + + offset -= sum(linear.values()) + offset += sum(quadratic.values()) + + return linear, quadratic, offset + + +def spin2QiboHamiltonian( + h: Dict[int, float], + J: Dict[Tuple[int, int], float], + dense: bool = True, +): + """Convert spin model to qibo Hamiltonian + + Mixer is not included. + + Please remember to put a negative sign to h and J if you get h and J + from binary2spin. Hamiltonians usually have a leading negative sign + in them, but QUBOs don't. + + .. math:: + H = - \\sum_{i=0}^N \\sum _{j=0}^N J_{ij} Z_i Z_j - \\sum_{i=0}^N h_i Z_i. + + Args: + h (dict): bias of the spin model + J (dict): interaction of the spin model + dense (bool): If ``True`` it creates the Hamiltonian as a + :class:`qibo.core.hamiltonians.Hamiltonian`, otherwise it creates + a :class:`qibo.core.hamiltonians.SymbolicHamiltonian`. + + Returns: + linear (dict): linear term of the binary model + quadratic (dict): quadratic term of the binary model + offset (float): offset of the binary model + """ + + from qibo import hamiltonians + from qibo.symbols import Z + + symbolic_ham = sum(Z(k, commutative=True) * v for k, v in h.items()) + symbolic_ham += sum( + Z(k0, commutative=True) * Z(k1, commutative=True) * v + for (k0, k1), v in J.items() + ) + symbolic_ham = -symbolic_ham + + ham = hamiltonians.SymbolicHamiltonian(symbolic_ham) + + if dense: + return ham.dense + else: + return ham From e08cd0c28fc6413037ce50ad66f3f985828c0939 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 18:07:50 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/mvc/main.py | 4 +--- examples/mvc/mvc.py | 2 -- examples/mvc/qubo_utils.py | 8 -------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/examples/mvc/main.py b/examples/mvc/main.py index 9648206425..c0513e81e4 100755 --- a/examples/mvc/main.py +++ b/examples/mvc/main.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Minimum Vertex Cover""" import argparse import csv @@ -22,7 +21,7 @@ def load_csv(filename: str): """Load graph from csv file""" - with open(filename, "r", newline="") as f: + with open(filename, newline="") as f: reader = csv.reader(f) data = [[int(row[0]), int(row[1]), float(row[2])] for row in reader] @@ -43,7 +42,6 @@ def load_csv(filename: str): def main(filename: str = "./mvc.csv"): - print(f"Load a graph from {filename} and make it a QUBO") g = load_csv(filename) penalty = qubo_mvc_penalty(g) diff --git a/examples/mvc/mvc.py b/examples/mvc/mvc.py index 7611a74c31..2ef1eea5ae 100644 --- a/examples/mvc/mvc.py +++ b/examples/mvc/mvc.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import networkx as nx from qubo_utils import binary2spin, spin2QiboHamiltonian @@ -24,7 +23,6 @@ def qubo_mvc(g: nx.Graph, penalty=None): penalty = qubo_mvc_penalty(g) for s, d in g.edges: - q[(s, s)] -= penalty q[(d, d)] -= penalty diff --git a/examples/mvc/qubo_utils.py b/examples/mvc/qubo_utils.py index 84a508c469..f272f361e5 100644 --- a/examples/mvc/qubo_utils.py +++ b/examples/mvc/qubo_utils.py @@ -1,14 +1,6 @@ -# -*- coding: utf-8 -*- from typing import ( - Any, Dict, - Generator, - Hashable, - Iterator, - List, - Optional, Tuple, - Union, ) From 08d6582cd19c23e3ff3e7466d6853766580e32d6 Mon Sep 17 00:00:00 2001 From: shangtai Date: Thu, 17 Aug 2023 13:55:46 +0800 Subject: [PATCH 3/4] fixing isort --- examples/mvc/qubo_utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/mvc/qubo_utils.py b/examples/mvc/qubo_utils.py index f272f361e5..b8805fc8a8 100644 --- a/examples/mvc/qubo_utils.py +++ b/examples/mvc/qubo_utils.py @@ -1,7 +1,4 @@ -from typing import ( - Dict, - Tuple, -) +from typing import Dict, Tuple def binary2spin( From 49234caa292cd80f7fc8982aaf97571dec45b7b4 Mon Sep 17 00:00:00 2001 From: Stefano Carrazza Date: Mon, 25 Sep 2023 09:46:45 +0200 Subject: [PATCH 4/4] adding examples --- doc/source/code-examples/applications.rst | 1 + .../code-examples/tutorials/mvc/README.md | 1 + examples/README.md | 1 + examples/mvc/README.md | 37 ++++++++++--------- 4 files changed, 22 insertions(+), 18 deletions(-) create mode 120000 doc/source/code-examples/tutorials/mvc/README.md diff --git a/doc/source/code-examples/applications.rst b/doc/source/code-examples/applications.rst index ede7bb33bc..695755ebcc 100644 --- a/doc/source/code-examples/applications.rst +++ b/doc/source/code-examples/applications.rst @@ -93,6 +93,7 @@ Combinatorics :maxdepth: 1 tutorials/qap/README.md + tutorials/mvc/README.md Applications by algorithm diff --git a/doc/source/code-examples/tutorials/mvc/README.md b/doc/source/code-examples/tutorials/mvc/README.md new file mode 120000 index 0000000000..5e9397841f --- /dev/null +++ b/doc/source/code-examples/tutorials/mvc/README.md @@ -0,0 +1 @@ +../../../../../examples/mvc/README.md \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 17ca621ed2..7b68e4ed28 100644 --- a/examples/README.md +++ b/examples/README.md @@ -27,6 +27,7 @@ physics problems. - [Quantum k-medians clustering](qclustering/README.md) - [Determining probability density functions with adiabatic quantum computing](adiabatic_qml/adiabatic-qml.ipynb) - [Quadratic assignment problem (QAP)](qap/README.md) +- [Minimum Vertex Cover (MVC)](mvc/README.md) In the `benchmarks` folder we have included examples concerning: - A generic benchmark script for multiple circuits (`benchmarks/main.py`) diff --git a/examples/mvc/README.md b/examples/mvc/README.md index c29cff478b..fa6312177f 100644 --- a/examples/mvc/README.md +++ b/examples/mvc/README.md @@ -1,21 +1,22 @@ -```python -import networkx as nx -import csv -import matplotlib.pyplot as plt - -from mvc import qubo_mvc, qubo_mvc_penalty, mvc_feasibility, mvc_easy_fix, mvc_energy -``` +# Minimum Vertex Cover (MVC) -# MVC Definition +Code at: [https://github.com/qiboteam/qibo/tree/master/examples/mvc](https://github.com/qiboteam/qibo/tree/master/examples/mvc) Given an undirected graph with a set of nodes V and edges E, A vertex cover of a graph is a set of nodes that includes at least one endpoint of every edge of the graph. The Minimum Vertex Cover (MVC) problem is an optimisation problem that find smallest vertex cover of a given graph. -# Load graph from csv +## Load graph from csv ```python +import networkx as nx +import csv +import matplotlib.pyplot as plt + +from mvc import qubo_mvc, qubo_mvc_penalty, mvc_feasibility, mvc_easy_fix, mvc_energy + + def load_csv(filename:str): """ Load graph from csv file """ @@ -88,9 +89,9 @@ print(f'Number of edge is {len(g.edges)}') Number of edge is 9 -# QUBO formulation +## QUBO formulation -## Estimate penalty +### Estimate penalty ```python @@ -101,7 +102,7 @@ print(f'The penalty is {penalty}') The penalty is 1.6548482220717595 -## Formulate QUBO (weighted minimum vertex cover) +### Formulate QUBO (weighted minimum vertex cover) ```python @@ -115,7 +116,7 @@ print(f'Number of quadratic terms: {len(quadratic)}\n') -## Generate random solutions +### Generate random solutions ```python @@ -129,7 +130,7 @@ print(f'The random solution is {random_solution}\n') -## Check feasibility +### Check feasibility ```python @@ -141,7 +142,7 @@ print(f'The feasibility is {feasibility}\n') -## Fix broken constraints +### Fix broken constraints ```python @@ -163,7 +164,7 @@ print(f'The feasibility is {feasibility}\n') -## Visualisation +### Visualisation ```python @@ -189,7 +190,7 @@ ax1.set_title(f'Vertex cover in orange after fix') -## Calculate energy +### Calculate energy ```python @@ -200,7 +201,7 @@ print(f'The energy is {energy}') The energy is 3.2102004750256556 -# Hamiltonian of the MVC problem +## Hamiltonian of the MVC problem ```python