problems.n_minus_1
The n_minus_1
package provides quantum computing solvers for 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 using the fewest possible edges.
Example
This script below uses annealing based optimization to solve the N-1 problem for the
small dataset (load_small_dataset()
)
with some additional edits.
Edge (1,4) is removed (this is the “broken” edge). Furthermore, the I_max attribute of Edge (1,5) is set to 0. In this way, there is exactly one solution: Turning edge (0,1) on, while all other edges are not toggled.
from tno.quantum.problems.n_minus_1 import QABasedNMinusOneSolver
from tno.quantum.problems.n_minus_1.quantum_annealing.datasets import load_small_dataset
graph = load_small_dataset()
graph.edges[1, 5]["I_max"] = 0
failing_edge = (1, 4)
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)
results = solver.run(solver_config=solver_config)
print(results)
This prints the following result:
k | count | turned on | turned off
---+-------+-------------------------+-------------------------
1 | 4 | (1, 5) |
1 | 1 | (0, 1) |
3 | 1 | (0, 1), (1, 5) | (5, 6)
Example
This script below uses the gate grover algorithm to solve the N-1 problem for a custom dataset.
import networkx as nx
from qiskit.visualization import plot_distribution
from tno.quantum.problems.n_minus_1 import GateBasedNMinusOneSolver
# Create a graph with edges: 0-1, 0-2, 0-3, 1-4, 2-4, 2-3, 3-4, 1-2, 0-5, 4-5.
graph = nx.Graph()
active_edges = [(0, 1), (0, 2), (0, 3), (1, 4), (0, 5)]
inactive_edges = [(2, 4), (2, 3), (3, 4), (1, 2), (4, 5)]
for node_n, node_m in active_edges:
graph.add_edge(node_n, node_m, weight=1)
for node_n, node_m in inactive_edges:
graph.add_edge(node_n, node_m, weight=-1)
load_flow_correctness = [[(0, 2), (0, 1), (0, 3), (4, 5), (3, 4)]]
# Run the algorithm
solver = GateBasedNMinusOneSolver(
graph, (1, 4), load_flow_correctness, n_iter=8, n_toggles=2
)
executed_run = solver.run()
# Plot the results
counts = executed_run.result().results[0].data.counts
fig = plot_distribution(
counts,
title="Failed Edge (1,4) - Correct Toggles (0,5), (3,4), or (4,5)",
bar_labels=True,
)
fig.savefig("grover_n-1.png")
- class tno.quantum.problems.n_minus_1.GateBasedNMinusOneSolver(graph, failing_edge, load_flow_correctness, n_toggles=1, n_iter=1, quantum_backend=None, *, exact_superposition=False)[source]
Bases:
object
Gate Based N-1 Solver.
The gate based N-1 solver uses Grover to search for grid configurations that have the specified number of toggles and are load flow compliant.
- __init__(graph, failing_edge, load_flow_correctness, n_toggles=1, n_iter=1, quantum_backend=None, *, exact_superposition=False)[source]
Init GateBasedNMinusOneSolver.
- Parameters:
graph (
Graph
) – A networkx graph representation of an electric network.failing_edge (
tuple
[int
,int
]) – The edge that is currently failing i.e (1,4).load_flow_correctness (
Iterable
[Iterable
[tuple
[Hashable
,Hashable
]]]) – A list of edges that are load flow compliantn_toggles (
SupportsInt
) – Number of toggles possible.n_iter (
SupportsInt
) – Number of iterationsquantum_backend (
Optional
[Backend
]) – A ‘qiskit’ backend. Default is ‘qasm_simulator’.exact_superposition (
bool
) – Choose if you want to use exact superposition with only useful states or an easier (superfluous) superposition with additional indices.
- class tno.quantum.problems.n_minus_1.QABasedNMinusOneSolver(graph, failing_edge)[source]
Bases:
object
Quantum Annealing Based N-1 Solver.
The Quantum Annealing solver performs the following steps:
Transform the problem to a Binary Quadratic Model, also known as a Quadratic Unconstrained Optimization (QUBO).
Approximate a solution to this problem with the given QUBO solver. This solver can be a quantum solver.
Decode the bitstrings returned by the solver.
- __init__(graph, failing_edge)[source]
Init of the
QABasedNMinusOneSolver
.- Parameters:
graph (
Graph
) – Network represented as a graph. Each node should have the following attributes: type, U_ref, P_low, P_high, U_min and U_max. Each edge should have the following attributes: active, edge_idx, Z and I_max.failing_edge (
tuple
[Hashable
,Hashable
]) – The edge that will fail in the scenario.
- run(factory_arguments=None, qubo_arguments=None, solver_config=None)[source]
Run the algorithm.
- Parameters:
factory_arguments (
UnionType
[FactoryArguments
,Mapping
[str
,Any
],None
]) – Keyword arguments for the Binary Quadratic Model factory. See the init of theFactoryArguments
for a more detailed description. IfNone
is given (default), thedefault()
arguments are used.qubo_arguments (
UnionType
[QUBOArguments
,Mapping
[str
,Any
],None
]) – Keyword arguments for building the QUBO. SeeQUBOArguments
for a more detailed description. IfNone
is given (default), thedefault()
arguments are used.solver_config (
UnionType
[SolverConfig
,Mapping
[str
,Any
],None
]) – Configuration for the qubo solver to use. Must be aSolverConfig
or a mapping with"name"
and"options"
keys. IfNone
(default) is provided, the``{"name": "simulated_annealing_solver", "options": {}}`
.
- Return type:
- Returns:
Result overview object containing the results.