Energy network operators are planning to expand their grid capacity over the next decade by an amount comparable to what was achieved in the past 100 years. This rapid growth is driven by the increasing adoption of technologies such as solar panels and electric vehicles, which introduce additional complexity to the electricity network.
To ensure reliable service, operators aim to meet stringent security standards. One such standard is N-1 security for medium-voltage networks, which ensures that electricity supply can continue even if a single connection fails.
This repository contains code of a feasibility study of quantum computing for optimising the robustness of energy networks in the event of cable failures, known as the 'N-1 problem'.
The N-1 problem arises in graph theory as a reconfigurational mathematical challenge. When a cut is introduced (or an edge is removed) in a given graph of nodes and edges, the N-1 problem seeks the most efficient method of reconfiguring the nodes with the fewest possible edges.
Results of the feasibility study can be found:
This project was carried out in collaboration with Alliander, a major Dutch grid operator. See also the Alliander Quantum technology GitHub page, which includes resources and updates related to their quantum research initiatives in energy grid optimization.
This work is supported by the Dutch National Growth Fund (NGF) as part of the Quantum Delta NL programme.
Documentation and usage examples of the tno.quantum.problems.n_minus_1
package can be found here.
Easily install the tno.quantum.problems.n_minus_1
package using pip:
$ python -m pip install tno.quantum.problems.n_minus_1
If you wish to run the tests you can use:
$ python -m pip install tno.quantum.problems.n_minus_1[tests]
-
Gate-based solver.
First create a network graph
import networkx as nx graph = nx.Graph() # Active edges have weight 1 active_edges = [(0, 1), (0, 2), (0, 3), (1, 4), (0, 5)] for node_n, node_m in active_edges: graph.add_edge(node_n, node_m, weight=1) # Inactive edges have weight 1 inactive_edges = [(2, 4), (2, 3), (3, 4), (1, 2), (4, 5)] for node_n, node_m in inactive_edges: graph.add_edge(node_n, node_m, weight=-1) load_flow_correctness = [[(0, 2), (0, 5), (0, 3), (1, 4), (4, 5)]] # Failing edge failing_edge = (0, 1)
Use a Grover's algorithm to efficiently find required edge to toggle.
from qiskit.visualization import plot_distribution from tno.quantum.problems.n_minus_1 import GateBasedNMinusOneSolver # Run the algorithm solver = GateBasedNMinusOneSolver( graph, failing_edge, load_flow_correctness, n_toggles=1, n_iter=1 ) executed_run = solver.run() # Plot the results counts = executed_run.result().results[0].data.counts labled_counts = { inactive_edge: counts[f"0x{i}"] for i, inactive_edge in enumerate(inactive_edges) } fig = plot_distribution( labled_counts, title="Failed Edge (0,1) - Correct Toggle (4,5)", bar_labels=True, ) fig.show()
-
Annealing based solver. Note: the example uses simulated annealing.
from tno.quantum.problems.n_minus_1 import QABasedNMinusOneSolver from tno.quantum.problems.n_minus_1.quantum_annealing.datasets import load_small_dataset # Create network graph graph = load_small_dataset() graph.edges[1, 5]["I_max"] = 0 failing_edge = (1, 4) # Create Solver solver_config = { "name": "simulated_annealing_solver", "options": { "num_reads": 100, "num_sweeps": 100000, "num_sweeps_per_beta": 100, "seed": 2024, }, } solver = QABasedNMinusOneSolver(graph, failing_edge) # Perform experiment results = solver.run(solver_config=solver_config) print(results)
Results the following output
k | count | turned on | turned off --+-------+-------------------------+------------------------- 1 | 4 | (1, 5) | 1 | 1 | (0, 1) | 3 | 1 | (0, 1), (1, 5) | (5, 6)
The content of this software may solely be used for applications that comply with international export control laws.