API reference
The public surface is small by design: one data model, one problem layer, one solve() front door.
Front door
Solve any QuGrid problem with any registered solver.
import qugrid as qg prob = qg.problems.Islanding(qg.cases.case9()) res = qg.solve(prob, solver="qaoa", seed=0) print(res.summary())
solver="auto" picks a sensible default: QAOA for small combinatorial
problems, simulated annealing beyond statevector reach; HHL for small
linear systems, exact algebra beyond.
repair="greedy" post-processes a combinatorial result with
:func:qugrid.solvers.greedy_repair: the answer bitstring — and, for
solvers that report an output distribution, every top state — is
repaired toward feasibility, and P(optimum | repaired) lands in
result.extras next to the raw P(optimum).
QUBO problems also accept the external stacks — "dimod-exact",
"dwave-sa", "qiskit-qaoa" — which return the same
:class:Result and need the matching extra installed.
Source code in src/qugrid/solvers/__init__.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
Data model
A power network in MATPOWER case format with computed conveniences.
Source code in src/qugrid/network.py
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | |
f_bus
property
Internal index of each branch's from-bus.
t_bus
property
Internal index of each branch's to-bus.
gen_bus
property
Internal bus index of each generator.
ref
property
Internal index of the reference (slack) bus.
load_p
property
Active load per bus [MW].
load_q
property
Reactive load per bus [MVAr].
bus_index(external)
Map external bus number(s) to internal 0-based position(s).
Source code in src/qugrid/network.py
64 65 66 67 68 | |
gen_p_per_bus()
Scheduled in-service generation per bus [MW].
Source code in src/qugrid/network.py
120 121 122 123 124 125 | |
sbus()
Complex net power injection per bus [pu].
Source code in src/qugrid/network.py
127 128 129 130 131 132 133 134 135 136 137 | |
ybus()
Dense complex bus admittance matrix [pu].
Follows MATPOWER's makeYbus (branch pi model with off-nominal tap
ratio and phase shift). Dense is a deliberate choice: QuGrid targets
research-scale cases (up to a few hundred buses), where dense algebra
is simpler and fast enough.
Source code in src/qugrid/network.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
bdc()
DC power flow matrices, following MATPOWER's makeBdc.
Returns (Bbus, Bf, Pbusinj, Pfinj) such that, in per unit,
P_bus = Bbus @ theta + Pbusinj and P_branch = Bf @ theta + Pfinj.
Source code in src/qugrid/network.py
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | |
edges(in_service_only=True)
Branch list as (from, to, |b|) with internal indices.
The weight is the branch susceptance magnitude 1/x — the standard
measure of electrical coupling used in partitioning studies.
Source code in src/qugrid/network.py
197 198 199 200 201 202 203 204 205 206 207 208 209 | |
adjacency()
Symmetric 0/1 adjacency matrix of the in-service network.
Source code in src/qugrid/network.py
211 212 213 214 215 216 | |
scale_loads(factor)
Return a copy with active and reactive loads multiplied by factor.
factor is a scalar or a per-bus array of length n_bus.
Source code in src/qugrid/network.py
242 243 244 245 246 247 248 249 250 | |
drop_branch(line)
Return a copy with one branch switched out (N-1 outage).
Source code in src/qugrid/network.py
252 253 254 255 256 | |
from_ppc(ppc, name='network')
classmethod
Build from a MATPOWER/PYPOWER case dict (baseMVA, bus, gen, branch).
Source code in src/qugrid/network.py
259 260 261 262 263 264 265 266 267 268 269 | |
from_matpower(path, name=None)
classmethod
Read a MATPOWER .m case file.
Source code in src/qugrid/network.py
271 272 273 274 275 276 277 278 279 | |
from_pandapower(net, name=None)
classmethod
Convert a pandapower network (requires pandapower installed).
Source code in src/qugrid/network.py
281 282 283 284 285 286 287 288 289 290 291 292 | |
to_ppc()
Export as a PYPOWER-compatible case dict.
Source code in src/qugrid/network.py
294 295 296 297 298 299 300 301 302 303 304 305 | |
Problem formulations
Bases: CombinatorialProblem
Multi-period unit commitment with binary-encoded dispatch.
Parameters
gens:
Generator parameters (cost coefficients in $/h, limits in MW).
demand:
Demand per period [MW].
power_bits:
Bits per unit and period for the dispatch expansion. 2 gives four
output levels between pmin and pmax.
weight_balance, weight_gate:
Penalty weights. None picks values that dominate the worst-case
cost range, which is sufficient for exactness on feasible instances.
initial_on:
Commitment state before the first period (for startup costs).
Source code in src/qugrid/problems/unit_commitment.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
constraint_residual(x)
MW of violation: imbalance beyond tolerance plus gated-off power.
Source code in src/qugrid/problems/unit_commitment.py
169 170 171 172 173 174 | |
continuous_reference()
True UC optimum with continuous dispatch (enumeration + exact ED).
Source code in src/qugrid/problems/unit_commitment.py
177 178 179 180 | |
Bases: CombinatorialProblem
Minimize generation cost subject to a demand-balance penalty.
Output of unit g: P_g = pmin_g + delta_g * sum_k 2^k b_{g,k} with
delta_g = (pmax - pmin) / (2^K - 1). All units are committed.
Source code in src/qugrid/problems/economic_dispatch.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | |
Bases: CombinatorialProblem
Two-way controlled islanding of a network.
Variables: x_i = 0/1 assigns bus i to island A or B.
Parameters
net:
The network. One binary variable per bus.
alpha:
Weight of the squared power-imbalance term (per-unit power). None
scales it so a one-per-unit imbalance costs as much as cutting the
strongest line.
beta:
Weight of the size-balance term. None uses a small default that
breaks the all-one-island degeneracy without dominating.
.. note::
The no-split assignment (every bus in one island) is always a QUBO
state; on very small or tightly meshed networks it can be the ground
state under the default weights, because cutting any line costs more
than the small size-balance reward. That optimum is infeasible for
islanding — is_feasible is False and decode()["sizes"] shows
an empty island. On the bundled IEEE cases (9 buses and larger) the
defaults give a genuine split; if your network comes back trivial,
raise beta (1.0 x mean edge weight forces a split on every
bundled case) or fix seed buses by editing the QUBO.
Source code in src/qugrid/problems/islanding.py
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
Bases: CombinatorialProblem
Minimum-PMU full observability (zero-injection buses not modeled).
Variables: one placement bit per bus plus ceil(log2(deg_i + 1)) slack
bits per bus. The IEEE 14-bus system needs 14 + 26 = 40 binaries — small
for an annealer, instructive for counting why qubit budgets matter.
Source code in src/qugrid/problems/pmu.py
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
coverage(x)
Boolean per bus: observed by the placement bits in x.
Source code in src/qugrid/problems/pmu.py
58 59 60 61 62 63 64 65 66 67 | |
constraint_residual(x)
Number of buses the placement bits in x leave unobserved.
Source code in src/qugrid/problems/pmu.py
83 84 85 | |
reference()
Exact minimum dominating set by enumeration over placement bits.
Slack bits do not need enumeration: for a feasible placement each
coverage slack has one exact value (coverage - 1), so the full
QUBO assignment — and with it a reference objective that
:meth:Result.gap can use — is reconstructed in closed form.
Source code in src/qugrid/problems/pmu.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
The reduced DC power flow system B' theta = P as a solver-ready problem.
The slack bus row/column are removed (its angle is fixed), leaving a
symmetric positive-definite system — exactly what HHL-style algorithms
want. context carries everything needed to map x back to angles.
Source code in src/qugrid/problems/power_flow.py
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
Newton-Raphson AC power flow with a caller-supplied linear solver.
solve_fn(problem: LinearSystemProblem) -> np.ndarray receives each
Newton step J dx = -F and returns dx. Pass an exact solver, HHL,
or VQLS — this is the standard hybrid quantum-classical decomposition of
AC power flow.
Source code in src/qugrid/problems/power_flow.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
Sample load patterns and label them secure/insecure by DC power flow.
Each load bus gets an independent multiplier drawn uniformly from
load_range; generation is scaled proportionally to keep balance.
The label is 1 when the maximum branch loading (|flow| / RATE_A) exceeds
threshold in the base case or in any screened contingency.
contingencies: "n-1" (default) screens every single-branch outage
that keeps the grid connected; None labels on the base case only; a
list of branch indices screens exactly those outages.
Features are the multipliers — low-dimensional (one per load bus) and physically meaningful, sized for today's quantum feature maps.
Source code in src/qugrid/problems/screening.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
Encodings
Quadratic unconstrained binary optimization: minimize x^T Q x + offset.
Source code in src/qugrid/problems/base.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
energy(x)
Objective value of bitstring(s); x is (n,) or (batch, n) of 0/1.
Source code in src/qugrid/problems/base.py
102 103 104 105 106 107 | |
to_ising()
Exact change of variables s = 1 - 2x. Energies are preserved.
Source code in src/qugrid/problems/base.py
109 110 111 112 113 114 115 116 117 | |
dynamic_range(db=False)
max|coef| / min nonzero |coef| over the energy coefficients.
Coefficients are the linear terms Q_ii and the pair couplings
2 Q_ij (i < j); the offset shifts every energy equally and is
excluded. Penalty-folded formulations stretch this ratio, and
sampling solvers stop resolving the costs underneath: the 2-unit
unit commitment study's penalized QUBO measures 3.9e3 here, and
example 07 documents the QAOA success-probability collapse on it.
db=True returns 20 log10 of the ratio. A QUBO with no
nonzero coefficient reports 1 (0 dB).
Source code in src/qugrid/problems/base.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
Ising Hamiltonian H(s) = sum_{i<j} J_ij s_i s_j + sum_i h_i s_i + offset.
Source code in src/qugrid/problems/base.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | |
energy(s)
Energy of spin configuration(s); s is (n,) or (batch, n) of +-1.
Source code in src/qugrid/problems/base.py
51 52 53 54 55 56 | |
all_energies()
Energies of all 2^n configurations (index bit i = variable i).
Memory stays at O(2^n) — spins are generated per variable, not cached per variable, so n = 24 costs ~400 MB peak instead of ~3 GB.
Source code in src/qugrid/problems/base.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | |
A linear system A x = b destined for a (quantum) linear solver.
scale and names carry the engineering meaning of x so that
solvers can hand back answers in physical units.
Source code in src/qugrid/problems/base.py
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | |
padded()
Embed into the next power-of-two dimension (identity padding).
Quantum registers hold 2^m amplitudes; the padding is inert
(A' block diagonal with the identity, b' zero on the pad).
Returns (A', b', original_dimension).
Source code in src/qugrid/problems/base.py
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
Source code in src/qugrid/problems/builder.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | |
var(name)
Register a binary variable and return its index (idempotent).
Source code in src/qugrid/problems/builder.py
33 34 35 36 37 38 | |
add_squared_penalty(terms, constant, weight)
Add weight * (sum coef_i x_i + constant)^2, expanded exactly.
Source code in src/qugrid/problems/builder.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
add_inequality(terms, constant, weight=1.0, method='slack', lam=None, name=None)
Penalize the constraint sum_i coef_i * x_i + constant >= 0.
method="slack" (default) is the textbook route (Lucas,
arXiv:1302.5843): register binary slack bits covering [0, U]
with U the maximum of the left side over all bitstrings, and add
weight * (sum_i coef_i x_i + constant - S)^2. Exact whenever the
left side is integer-valued; costs ceil(log2(U + 1)) extra bits
per constraint.
method="unbalanced" is unbalanced penalization
(Montanez-Barrera et al., Quantum Sci. Technol. 9, 025022, 2024;
arXiv:2211.13914): add -l1*g + l2*g^2 for
g = sum_i coef_i x_i + constant, with lam=(l1, l2). Zero
slack bits, so the qubit count stops growing with the number of
inequalities — but the encoding is a heuristic: the unconstrained
minimum coincides with the constrained optimum only for suitable
(l1, l2), which need tuning per problem family. weight is
ignored on this path.
Source code in src/qugrid/problems/builder.py
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | |
Results and benchmarking
Outcome of solving a QuGrid problem.
Source code in src/qugrid/solvers/base.py
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
gap()
Relative optimality gap versus the reference objective (if known).
Source code in src/qugrid/solvers/base.py
39 40 41 42 43 44 45 46 47 | |
success_probability()
Probability mass on the reference optimum (quantum solvers only).
Source code in src/qugrid/solvers/base.py
49 50 51 52 53 54 55 56 57 58 59 | |
plot_convergence(ax=None)
Optimizer trajectory (thin wrapper over :func:qugrid.viz.plot_convergence).
Source code in src/qugrid/solvers/base.py
96 97 98 99 100 | |
Paper-ready benchmarking: sweep, aggregate, export.
The loop every quantum-for-power paper runs — problems x solvers x seeds — in one call, with the outputs reviewers ask for: a tidy DataFrame, a LaTeX table, and a JSON config that pins every seed and parameter.
import qugrid as qg from qugrid import bench problems = {"case9": qg.problems.Islanding(qg.cases.case9())} df = bench.sweep(problems, solvers=["exact", "sa", "qaoa"], seeds=range(3)) print(bench.summarize(df)) bench.save_run(df, "runs/islanding") # results.csv + summary.tex + config.json
sweep(problems, solvers, seeds=range(3), verbose=True, **common_kwargs)
Run every solver on every problem with every seed.
solvers is a list of registry names, or a dict mapping name to extra
keyword arguments (for example {"qaoa": {"p": 3}}). Deterministic
solvers still run once per seed; their spread doubles as a sanity check.
Source code in src/qugrid/bench.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
summarize(df)
Mean and spread per (problem, solver), the table most papers print.
Source code in src/qugrid/bench.py
76 77 78 79 80 81 82 83 84 85 86 87 | |
to_latex(df, caption='QuGrid benchmark', label='tab:qugrid')
Booktabs LaTeX for a summarized DataFrame (drop it straight into a paper).
Written by hand rather than through DataFrame.to_latex so the core
install needs no jinja2 (pandas >= 2.0 routes to_latex through its
Styler, which imports jinja2 at call time).
Source code in src/qugrid/bench.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
save_run(df, out_dir, config=None, caption='QuGrid benchmark')
Write results.csv, summary.csv, summary.tex, config.json.
The config records package version, platform, and timestamp so the run can be cited and reproduced.
Source code in src/qugrid/bench.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
Classical references
Classical power flow: DC (linear) and Newton-Raphson AC.
Both follow the MATPOWER formulation, so results are directly comparable to
what researchers get from rundcpf / runpf. Dense linear algebra keeps
the code readable; the target scale (up to a few hundred buses) does not need
sparsity.
DCResult
dataclass
DC power flow solution.
Source code in src/qugrid/classical/power_flow.py
19 20 21 22 23 24 25 26 27 28 29 30 | |
ACResult
dataclass
Newton-Raphson AC power flow solution.
Source code in src/qugrid/classical/power_flow.py
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
solve_dc(net)
Solve the DC power flow Bbus @ theta = P exactly.
This is also the linear system handed to quantum linear solvers
(:func:qugrid.problems.dc_power_flow), so classical and quantum answers
are comparable angle by angle.
Source code in src/qugrid/classical/power_flow.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
newton_raphson(net, tol=1e-08, max_iter=20)
Full Newton-Raphson AC power flow in polar coordinates.
Reference: MATPOWER's newtonpf (Zimmerman, Murillo-Sanchez, Thomas,
"MATPOWER: Steady-State Operations, Planning and Analysis Tools for Power
Systems Research and Education", IEEE Trans. Power Systems, 2011).
Generator reactive-power limits are not enforced.
Source code in src/qugrid/classical/power_flow.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |
Classical dispatch references: economic dispatch and enumerated unit commitment.
These provide the true optima against which QuGrid measures quantum solutions — including the error introduced by QUBO discretization itself, which is reported separately from solver error.
GenParams
dataclass
One thermal generator for dispatch/commitment studies.
Cost is quadratic: c2 * P^2 + c1 * P + c0 [$ per hour], with c0
charged only when the unit is committed. startup is charged on each
off-to-on transition.
Source code in src/qugrid/classical/dispatch.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | |
UCSolution
dataclass
A unit commitment schedule with its dispatch and cost.
Source code in src/qugrid/classical/dispatch.py
82 83 84 85 86 87 88 89 | |
economic_dispatch(gens, demand, tol=1e-09)
Exact single-period economic dispatch via bisection on marginal cost.
Solves min sum_i c_i(P_i) s.t. sum_i P_i = demand, pmin_i <= P_i <= pmax_i
for strictly convex costs (c2 > 0). Returns (P, total_cost).
Raises ValueError when the demand is outside the feasible range.
Source code in src/qugrid/classical/dispatch.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | |
solve_uc_enumerate(gens, demand, initial_on=None)
Exact unit commitment by enumerating all commitment patterns.
For each of the 2^(G*T) on/off patterns, the committed units are
dispatched with exact economic dispatch. Intended for the small instances
(G*T <= ~20) used to validate quantum solvers; it is the ground truth,
not a production UC.
Source code in src/qugrid/classical/dispatch.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | |
Adapters
Bridges into the quantum ecosystems researchers already use.
QuGrid's built-in simulator is for zero-setup research; when you want shots, noise models, annealing hardware, or autodiff, export the same problem:
- :func:
to_qiskit_operator/ :func:to_qiskit_qaoa— IBM Qiskit - :func:
to_bqm/ :func:result_from_sampleset— D-Wave Ocean (dimod) - :func:
to_pennylane_hamiltonian— Xanadu PennyLane
The same bridges also run in the other direction: :mod:external solvers
<qugrid.adapters.external_solvers> hand the problem to a vendor solver and
return the standard :class:~qugrid.solvers.base.Result, so
qg.solve(problem, solver="dwave-sa") reads like any built-in solver.
Each function imports its ecosystem lazily and raises a clear message naming
the extra to install (pip install qugrid[qiskit] and friends).
result_from_sampleset(problem, sampleset)
Wrap an Ocean SampleSet into a standard QuGrid :class:Result.
Source code in src/qugrid/adapters/dimod_adapter.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 | |
to_bqm(problem)
dimod.BinaryQuadraticModel of the problem's QUBO.
Submit it to any Ocean sampler::
from dwave.samplers import SimulatedAnnealingSampler
sampleset = SimulatedAnnealingSampler().sample(to_bqm(prob), num_reads=200)
result = result_from_sampleset(prob, sampleset)
Source code in src/qugrid/adapters/dimod_adapter.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
solve_dimod_exact(problem, **_ignored)
Exhaustive enumeration with dimod.ExactSolver (n <= 24).
Source code in src/qugrid/adapters/external_solvers.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | |
solve_dwave_sa(problem, num_reads=100, num_sweeps=1000, seed=0, **_ignored)
Simulated annealing with dwave.samplers.SimulatedAnnealingSampler.
The Ocean stack's production annealer (C++ inner loop), run on the same
QUBO as the built-in "sa" so the two are directly comparable.
Source code in src/qugrid/adapters/external_solvers.py
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
solve_qiskit_qaoa(problem, reps=2, maxiter=200, seed=0, **_ignored)
QAOA through qiskit-optimization's MinimumEigenOptimizer.
Shot-based sampling on Qiskit's statevector primitive, COBYLA outer
loop. Slower than the built-in "qaoa" by two orders of magnitude at
the same depth — the point is that a second implementation of the same
algorithm lands on the same objective.
Source code in src/qugrid/adapters/external_solvers.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
to_pennylane_hamiltonian(problem)
Ising cost Hamiltonian as a PennyLane operator (autodiff-ready).
Use it in any PennyLane QNode, for example with
qml.qaoa.cost_layer / qml.qaoa.mixer_layer.
Source code in src/qugrid/adapters/pennylane_adapter.py
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
to_qiskit_operator(problem)
Ising cost Hamiltonian as a SparsePauliOp (identity term included).
Source code in src/qugrid/adapters/qiskit_adapter.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | |
to_qiskit_qaoa(problem, p=2)
Parameterized QAOA circuit for the problem.
Returns (circuit, gammas, betas) where the parameter vectors bind the
p cost and mixer angles. Ready for Qiskit primitives, transpilers,
and hardware backends.
Source code in src/qugrid/adapters/qiskit_adapter.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |