Quantum Bayesian Network Sampling (`pyagrum.qBNSampling`)
![]() | ![]() |
The pyagrum.qBNSampling module encodes a Bayesian network as a quantum circuit
so that measuring the circuit samples from the networkβs joint distribution.
It also provides a quantum rejection-sampling inference engine that computes
posterior distributions conditioned on evidence using Grover-based amplitude
amplification.
Dependencies: qiskit, qiskit-aer, qiskit-ibm-runtime, scipy.
References
- Circuit encoding: Borujeni et al., Quantum circuit representation of Bayesian networks, Expert Systems with Applications, 2021. arXiv:2004.14803
- Quantum inference: Low, Yoder, Chuang, Quantum inference on Bayesian networks, Physical Review A, 2014. arXiv:1402.7359
import pyagrum as gumimport pyagrum.lib.notebook as gnbimport pyagrum.qBNSampling as qBNSPart 1 β qBNMC: encoding a Bayesian network as a quantum circuit
Section titled βPart 1 β qBNMC: encoding a Bayesian network as a quantum circuitβEach variable in the BN is mapped to qubits. Its CPT is encoded as multi-qubit RY rotations: root nodes get unconditional rotations; non-root nodes get controlled rotations β one block per parent configuration, framed by X gates to select the correct control state.
Measuring the circuit returns a sample from the joint distribution of the network.
Illustrative example: 3-node BN
Section titled βIllustrative example: 3-node BNβbn = gum.fastBN("A->B<-C", 2)gnb.showBN(bn)qbn = qBNS.qBNMC(bn)
print(f"Total qubits: {qbn.getTotNumQBits()}")print(f"Qubit map (node id -> qubit ids): {qbn.n_qb_map}")
circuit = qbn.buildCircuit(add_measure=True)Total qubits: 3Qubit map (node id -> qubit ids): {0: [0], 1: [1], 2: [2]}Running the circuit on the Aer simulator returns marginal probability vectors
for each variable. Let us compare them with the exact marginals from
LazyPropagation.
gnb.flow.row(gnb.getBN(bn), circuit.draw("mpl", scale=0.7))circuit.draw("latex")
circuit.draw() ββββββββββββββ β βββββ βββββ β β Β»
0: β€ Ry(2.5085) βββββ€ X ββββββββ ββββββββ€ X ββββββββββββββββ βββββββββββββββΒ»
ββββββββββββββ β ββββββββββββ΄ββββββββββββ β βββββββ΄βββββββ β Β»
1: βββββββββββββββββββββββ€ Ry(1.7002) βββββββββββββββ€ Ry(1.9446) βββββββββΒ»
ββββββββββββββ β ββββββββββββ¬ββββββββββββ β ββββββββββββ¬ββββββββββββ β Β»
2: β€ Ry(1.6313) βββββ€ X ββββββββ ββββββββ€ X βββββ€ X ββββββββ ββββββββ€ X ββββΒ»
ββββββββββββββ β βββββ βββββ β βββββ βββββ β Β»
meas: 3/βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β»
Β« βββββ βββββ β β βββ
Β« 0: β€ X ββββββββ ββββββββ€ X βββββββββββ βββββββββββ€Mβββββββ
Β« ββββββββββββ΄ββββββββββββ β βββββββ΄βββββββ β ββ₯ββββ
Β« 1: ββββββ€ Ry(2.2965) ββββββββββ€ Ry(1.3641) ββββββ«ββ€Mββββ
Β« βββββββ¬βββββββ β βββββββ¬βββββββ β β ββ₯ββββ
Β« 2: ββββββββββββ ββββββββββββββββββββββ ββββββββββββ«βββ«ββ€Mβ
Β« β β β β ββ₯β
Β«meas: 3/ββββββββββββββββββββββββββββββββββββββββββββββ©βββ©βββ©β
Β« 0 1 2
marginals = qbn.runBN(shots=10000)ie = gum.LazyPropagation(bn)ie.makeInference()
for name, tensor in marginals.items(): print(f"P({name})") print(f" qBNMC (10 000 shots) : {[round(v, 4) for v in tensor.tolist()]}") print(f" LazyPropagation : {[round(v, 4) for v in ie.posterior(name).tolist()]}") print()P(A) qBNMC (10 000 shots) : [0.0976, 0.9024] LazyPropagation : [0.0969, 0.9031]
P(B) qBNMC (10 000 shots) : [0.4488, 0.5512] LazyPropagation : [0.4517, 0.5483]
P(C) qBNMC (10 000 shots) : [0.4706, 0.5294] LazyPropagation : [0.4698, 0.5302]Named example: Oil Company Stock Price (Borujeni et al., 2021)
Section titled βNamed example: Oil Company Stock Price (Borujeni et al., 2021)βA 4-node BN modelling the dependencies between Interest Rate (IR), Stock Market (SM), Oil Import (OI), and Stock Price (SP).
bn_oil = gum.fastBN("IR->SM->SP<-OI", 2)bn_oil.cpt("IR")[:] = [0.75, 0.25]bn_oil.cpt("SM")[:] = [[0.3, 0.7], [0.8, 0.2]]bn_oil.cpt("OI")[:] = [0.6, 0.4]bn_oil.cpt("SP")[:] = [[[0.1, 0.9], [0.3, 0.7]], [[0.4, 0.6], [0.7, 0.3]]]
gnb.showBN(bn_oil)qbn_oil = qBNS.qBNMC(bn_oil)print(f"Total qubits: {qbn_oil.getTotNumQBits()}")qbn_oil.buildCircuit().draw("text")Total qubits: 4 βββββββββββ β βββββ βββββ β β Β»
0: ββ€ Ry(Ο/3) βββββββ€ X ββββββββ ββββββββ€ X βββββββββββ βββββββββββββββΒ»
βββββββββββ β ββββββββββββ΄ββββββββββββ β βββββββ΄βββββββ β βββββΒ»
1: βββββββββββββββββββββββ€ Ry(1.9823) ββββββββββ€ Ry(0.9273) βββββ€ X βΒ»
β ββββββββββββββ β ββββββββββββββ β βββββΒ»
2: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
ββββββββββββββ β β β βββββΒ»
3: β€ Ry(1.3694) βββββββββββββββββββββββββββββββββββββββββββββββββ€ X βΒ»
ββββββββββββββ β β β βββββΒ»
meas: 4/ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β»
Β« β β Β»
Β« 0: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β« βββββ β β βββββ Β»
Β« 1: βββββββ ββββββββ€ X ββββββββββββββββ ββββββββββββββββ€ X ββββββββ βββββββΒ»
Β« βββββββ΄ββββββββββββ β βββββββ΄βββββββ β ββββββββββββ΄βββββββΒ»
Β« 2: β€ Ry(2.4981) βββββββββββββββ€ Ry(1.9823) βββββββββββββββ€ Ry(1.7722) βΒ»
Β« βββββββ¬ββββββββββββ β ββββββββββββ¬ββββββββββββ β βββββββ¬βββββββΒ»
Β« 3: βββββββ ββββββββ€ X βββββ€ X ββββββββ ββββββββ€ X ββββββββββββββββ βββββββΒ»
Β« βββββ β βββββ βββββ β Β»
Β«meas: 4/ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β« Β»
Β« β β βββ
Β« 0: ββββββββββββββββββββββββββ€Mββββββββββ
Β« βββββ β β ββ₯ββββ
Β« 1: β€ X βββββββββββ ββββββββββββ«ββ€Mβββββββ
Β« βββββ β βββββββ΄βββββββ β β ββ₯ββββ
Β« 2: βββββββββ€ Ry(1.1593) ββββββ«βββ«ββ€Mββββ
Β« β βββββββ¬βββββββ β β β ββ₯ββββ
Β« 3: βββββββββββββββ ββββββββββββ«βββ«βββ«ββ€Mβ
Β« β β β β β ββ₯β
Β«meas: 4/βββββββββββββββββββββββββββ©βββ©βββ©βββ©β
Β« 0 1 2 3
marginals_oil = qbn_oil.runBN(shots=10000)ie_oil = gum.LazyPropagation(bn_oil)ie_oil.makeInference()
gnb.sideBySide( marginals_oil["SP"], ie_oil.posterior("SP"), captions=["qBNMC β 10 000 shots", "LazyPropagation β exact"],)Multi-state variables: Naive Bayes Bankruptcy Prediction
Section titled βMulti-state variables: Naive Bayes Bankruptcy PredictionβVariables with more than 2 states require qubits.
Here node B has 2 states and several children have 3 states (2 qubits each).
bn_bk = gum.fastBN("B->AU; B->IT; B->CH[3]; B->LM[3]")bn_bk.generateCPTs()gnb.showInference(bn_bk)qbn_bk = qBNS.qBNMC(bn_bk)print(f"Total qubits: {qbn_bk.getTotNumQBits()}")print(f"Qubit widths: { {bn_bk.variable(n).name(): qbn_bk.getWidth(n) for n in bn_bk.nodes()} }")qbn_bk.buildCircuit().draw("text")Total qubits: 7Qubit widths: {'B': 1, 'AU': 1, 'IT': 1, 'CH': 2, 'LM': 2} ββββββββββββββ β βββββ βββββ β β βββββΒ»
0: β€ Ry(1.9485) βββββ€ X ββββββββ ββββββββ€ X βββββββββββ βββββββββββ€ X βΒ»
ββββββββββββββ β βββββ β βββββ β β β βββββΒ»
1: βββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββββΒ»
β βββββββ΄βββββββ β βββββββ΄βββββββ β Β»
2: βββββββββββββββββββββββ€ Ry(1.4249) ββββββββββ€ Ry(1.5019) βββββββββΒ»
β ββββββββββββββ β ββββββββββββββ β Β»
3_0: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
β β β Β»
3_1: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
β β β Β»
4_0: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
β β β Β»
4_1: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
β β β Β»
meas: 7/ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β»
Β« βββββ β Β»
Β« 0: βββββββ ββββββββββββ βββββββ βββββββββ ββββββββββ βββ€ X βββββββββββ βββββββΒ»
Β« β β β β β βββββ β β Β»
Β« 1: βββββββΌββββββββββββΌβββββββΌβββββββββΌββββββββββΌβββββββββββββββββΌβββββββΒ»
Β« β β β β β β β Β»
Β« 2: βββββββΌββββββββββββΌβββββββΌβββββββββΌββββββββββΌβββββββββββββββββΌβββββββΒ»
Β« β β β β β β β Β»
Β« 3_0: βββββββΌββββββββββββΌβββββββΌβββββββββΌββββββββββΌβββββββββββββββββΌβββββββΒ»
Β« β β β β β β β Β»
Β« 3_1: βββββββΌββββββββββββΌβββββββΌβββββββββΌββββββββββΌβββββββββββββββββΌβββββββΒ»
Β« βββββββ΄βββββββ β βββ΄ββ β βββ΄ββ β βββββββ΄βββββββΒ»
Β« 4_0: β€ Ry(1.3497) ββββββ βββββ€ X ββββββββ ββββββββ€ X ββββββββββ€ Ry(1.4611) βΒ»
Β« βββββββββββββββββββ΄ββββββββββββββββ΄ββββββββββββ β ββββββββββββββΒ»
Β« 4_1: βββββββββββββββ€ Ry(0) βββββββ€ Ry(2.2073) ββββββββββββββββββββββββββββΒ»
Β« βββββββββ ββββββββββββββ β Β»
Β«meas: 7/βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β« Β»
Β« β βββββ βββββ β Β»
Β« 0: βββββ βββββββ βββββββββ ββββββββββ ββββββ€ X ββββββββ ββββββββ€ X ββββΒ»
Β« β β β β β ββββββββββββ΄ββββββββββββ β Β»
Β« 1: βββββΌβββββββΌβββββββββΌββββββββββΌβββββββββββ€ Ry(1.3767) βββββββββΒ»
Β« β β β β β ββββββββββββββ β Β»
Β« 2: βββββΌβββββββΌβββββββββΌββββββββββΌββββββββββββββββββββββββββββββββΒ»
Β« β β β β β β Β»
Β« 3_0: βββββΌβββββββΌβββββββββΌββββββββββΌββββββββββββββββββββββββββββββββΒ»
Β« β β β β β β Β»
Β« 3_1: βββββΌβββββββΌβββββββββΌββββββββββΌββββββββββββββββββββββββββββββββΒ»
Β« β βββ΄ββ β βββ΄ββ β β Β»
Β« 4_0: βββββ βββββ€ X ββββββββ ββββββββ€ X βββββββββββββββββββββββββββββββΒ»
Β« βββββ΄ββββββββββββββββ΄ββββββββββββ β β Β»
Β« 4_1: β€ Ry(0) βββββββ€ Ry(1.6664) ββββββββββββββββββββββββββββββββββββΒ»
Β« βββββββββ ββββββββββββββ β β Β»
Β«meas: 7/βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β« Β»
Β« β βββββ Β»
Β« 0: βββββββ βββββββββββ€ X βββββββββ ββββββββββββ βββββββ βββββββββ ββββββββββ ββΒ»
Β« βββββββ΄βββββββ β βββββ β β β β β Β»
Β« 1: β€ Ry(1.1793) βββββββββββββββββΌββββββββββββΌβββββββΌβββββββββΌββββββββββΌββΒ»
Β« ββββββββββββββ β β β β β β Β»
Β« 2: ββββββββββββββββββββββββββββββΌββββββββββββΌβββββββΌβββββββββΌββββββββββΌββΒ»
Β« β ββββββββ΄βββββββ β βββ΄ββ β βββ΄ββΒ»
Β« 3_0: βββββββββββββββββββββββ€ Ry(0.63789) ββββββ βββββ€ X ββββββββ ββββββββ€ X βΒ»
Β« β ββββββββββββββββββββ΄ββββββββββββββββ΄ββββββββββββΒ»
Β« 3_1: ββββββββββββββββββββββββββββββββββββββ€ Ry(0) βββββββ€ Ry(1.3859) ββββββΒ»
Β« β βββββββββ ββββββββββββββ Β»
Β« 4_0: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β« β Β»
Β« 4_1: ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β« β Β»
Β«meas: 7/ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΒ»
Β« Β»
Β« βββββ β β βββ Β»
Β« 0: β€ X ββββββββββββ ββββββββββββ βββββββ βββββββββ ββββββββββ ββββββ€MβββββββΒ»
Β« βββββ β β β β β β β ββ₯ββββ Β»
Β« 1: ββββββββββββββββΌββββββββββββΌβββββββΌβββββββββΌββββββββββΌβββββββ«ββ€MββββΒ»
Β« β β β β β β β β ββ₯ββββΒ»
Β« 2: ββββββββββββββββΌββββββββββββΌβββββββΌβββββββββΌββββββββββΌβββββββ«βββ«ββ€MβΒ»
Β« β ββββββββ΄βββββββ β βββ΄ββ β βββ΄ββ β β β ββ₯βΒ»
Β« 3_0: βββββββββ€ Ry(0.62371) ββββββ βββββ€ X ββββββββ ββββββββ€ X ββββββ«βββ«βββ«βΒ»
Β« β ββββββββββββββββββββ΄ββββββββββββββββ΄ββββββββββββ β β β β Β»
Β« 3_1: ββββββββββββββββββββββββ€ Ry(0) βββββββ€ Ry(1.2431) βββββββββββ«βββ«βββ«βΒ»
Β« β βββββββββ ββββββββββββββ β β β β Β»
Β« 4_0: βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ«βββ«βββ«βΒ»
Β« β β β β β Β»
Β« 4_1: βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ«βββ«βββ«βΒ»
Β« β β β β β Β»
Β«meas: 7/βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ©βββ©βββ©βΒ»
Β« 0 1 2 Β»
Β«
Β« 0: ββββββββββββ
Β«
Β« 1: ββββββββββββ
Β«
Β« 2: ββββββββββββ
Β« βββ
Β« 3_0: β€Mββββββββββ
Β« ββ₯ββββ
Β« 3_1: ββ«ββ€Mβββββββ
Β« β ββ₯ββββ
Β« 4_0: ββ«βββ«ββ€Mββββ
Β« β β ββ₯ββββ
Β« 4_1: ββ«βββ«βββ«ββ€Mβ
Β« β β β ββ₯β
Β«meas: 7/ββ©βββ©βββ©βββ©β
Β« 3 4 5 6
marginals_bk = qbn_bk.runBN(shots=10000)
ie_bk = gum.LazyPropagation(bn_bk)ie_bk.makeInference()
gnb.sideBySide( marginals_bk["CH"], ie_bk.posterior("CH"), captions=["qBNMC β 10 000 shots", "LazyPropagation β exact"],)Part 2 β qBNRejection: inference with evidence
Section titled βPart 2 β qBNRejection: inference with evidenceβqBNRejection implements quantum rejection sampling (Low et al., 2014).
Given evidence , it uses the Grover iterate
to amplify the amplitude of states consistent with , where:
- is the quantum circuit encoding of the BN (built by
qBNMC); - is a phase flip on the evidence qubits;
- is a phase flip on the all-zero state.
Each call to getSample applies for increasing
until a measurement consistent with the evidence is obtained (Algorithm 1).
makeInference accumulates max_iter such samples to estimate the posterior.
Basic usage: 3-node BN with evidence
Section titled βBasic usage: 3-node BN with evidenceβbn = gum.fastBN("A->B<-C", 2)evidence = {"B": 0}
## Exact referenceie = gum.LazyPropagation(bn)ie.setEvidence(evidence)ie.makeInference()print("Exact P(A | B=0):", ie.posterior("A"))Exact P(A | B=0): A β0 β1 βββββββββββββββββββββ 0.8467 β 0.1533 βqbn = qBNS.qBNMC(bn)qinf = qBNS.qBNRejection(qbn)qinf.setEvidence(evidence)qinf.setMaxIter(500)qinf.makeInference(){'A': [0.8340000000000006, 0.16600000000000012], 'B': [1.0000000000000007, 0.0], 'C': [0.4880000000000004, 0.5120000000000003]}gnb.sideBySide( qinf.posterior("A"), ie.posterior("A"), captions=["qBNRejection β 500 samples", "LazyPropagation β exact"],)Restricting to the relevant subgraph: useFragmentBN
Section titled βRestricting to the relevant subgraph: useFragmentBNβFor a query involving only a subset of nodes, useFragmentBN builds a minimal
BayesNetFragment containing only the ancestors of the target and evidence nodes.
This reduces the number of qubits and speeds up the circuit.
## Larger BN: A->B->C->H; I->H; A->D->C; D->E; G->F->Ebn_large = gum.fastBN("A->B->C->H;I->H;A->D->C;D->E;G->F->E", 2)gnb.showBN(bn_large, size=8)evidence_large = {"H": 0, "A": 1}target = "D"
qbn_large = qBNS.qBNMC(bn_large)qinf_large = qBNS.qBNRejection(qbn_large)qinf_large.setEvidence(evidence_large)
## Restrict the circuit to the ancestors of {target} βͺ evidenceqinf_large.useFragmentBN(target={target})
print(f"Full BN: {bn_large.size()} nodes, {qbn_large.getTotNumQBits()} qubits")print(f"Fragment: {qinf_large.qbn.bn.size()} nodes, {qinf_large.qbn.getTotNumQBits()} qubits")gnb.showBN(qinf_large.qbn.bn)Full BN: 9 nodes, 9 qubitsFragment: 6 nodes, 6 qubitsqinf_large.setMaxIter(500)qinf_large.makeInference()
ie_large = gum.LazyPropagation(bn_large)ie_large.setEvidence(evidence_large)ie_large.makeInference()
gnb.sideBySide( qinf_large.posterior(target), ie_large.posterior(target), captions=[f"qBNRejection β P({target} | H=0, A=1)", "LazyPropagation β exact"],)Named example: 4-node Oil BN with evidence
Section titled βNamed example: 4-node Oil BN with evidenceβbn_oil = gum.fastBN("IR->SM->SP<-OI", 2)bn_oil.cpt("IR")[:] = [0.75, 0.25]bn_oil.cpt("SM")[:] = [[0.3, 0.7], [0.8, 0.2]]bn_oil.cpt("OI")[:] = [0.6, 0.4]bn_oil.cpt("SP")[:] = [[[0.1, 0.9], [0.3, 0.7]], [[0.4, 0.6], [0.7, 0.3]]]
evidence_oil = {"SP": 1}target_oil = "OI"
ie_oil = gum.LazyPropagation(bn_oil)ie_oil.setEvidence(evidence_oil)ie_oil.makeInference()print(f"Exact P({target_oil} | SP=1) = {ie_oil.posterior(target_oil)}")Exact P(OI | SP=1) = OI β0 β1 βββββββββββββββββββββ 0.7336 β 0.2664 βqbn_oil = qBNS.qBNMC(bn_oil)qinf_oil = qBNS.qBNRejection(qbn_oil)qinf_oil.setEvidence(evidence_oil)qinf_oil.useFragmentBN(target={target_oil})qinf_oil.setMaxIter(500)qinf_oil.makeInference()
gnb.sideBySide( qinf_oil.posterior(target_oil), ie_oil.posterior(target_oil), captions=[f"qBNRejection β P({target_oil} | SP=1)", "LazyPropagation β exact"],)QBN Summary
Section titled βQBN Summaryβ| Class | Purpose | Key method |
|---|---|---|
qBNMC | Encode BN as quantum circuit | buildCircuit(), runBN(shots) |
qBNRejection | Posterior inference with evidence | setEvidence(), makeInference(), posterior(node) |
Useful workflow:
- Build
qBNMC(bn)β circuit encoding. - Check marginals via
runBN()(no evidence). - Build
qBNRejection(qbn)and calluseFragmentBN(target, evidence)to reduce circuit size. setEvidence(),setMaxIter(),makeInference(),posterior(node).
