105105 2,
106106 None,
107107 'implementation-with-pennylane'),
108- ('Kullback-Leibler divergence ',
108+ ('Summary of steps in PennyLane implementation ',
109109 2,
110110 None,
111- 'kullback-leibler-divergence ')]}
111+ 'summary-of-steps-in-pennylane-implementation ')]}
112112end of tocinfo -->
113113
114114< body >
170170 <!-- navigation toc: --> < li > < a href ="#gibbs-sampling " style ="font-size: 80%; "> Gibbs sampling</ a > </ li >
171171 <!-- navigation toc: --> < li > < a href ="#parameter-optimization-and-variational-techniques " style ="font-size: 80%; "> Parameter Optimization and Variational Techniques</ a > </ li >
172172 <!-- navigation toc: --> < li > < a href ="#implementation-with-pennylane " style ="font-size: 80%; "> Implementation with PennyLane</ a > </ li >
173- <!-- navigation toc: --> < li > < a href ="#kullback-leibler-divergence " style ="font-size: 80%; "> Kullback-Leibler divergence </ a > </ li >
173+ <!-- navigation toc: --> < li > < a href ="#summary-of-steps-in-pennylane-implementation " style ="font-size: 80%; "> Summary of steps in PennyLane implementation </ a > </ li >
174174
175175 </ ul >
176176 </ li >
@@ -786,36 +786,69 @@ <h2 id="parameter-optimization-and-variational-techniques" class="anchor">Parame
786786<!-- !split -->
787787< h2 id ="implementation-with-pennylane " class ="anchor "> Implementation with PennyLane </ h2 >
788788
789- < p > As a concrete example, we outline how to implement an RQBM in
790- PennyLane. We consider \( n_v \) visible and \( n_h \) hidden qubits. The ansatz
791- can be, for instance, layers of parameterized single-qubit rotations
792- and entangling gates that respect the bipartite structure. Below is
793- illustrative code (in Python) using PennyLane’s default.qubit
794- simulator.
789+ < p > Below we sketch a toy example: a two-qubit Quantum Circuit Born Machine
790+ (QCBM), which generates a probability distribution via a parameterized
791+ quantum circuit. While a QCBM is not exactly a QBM (it has no thermal
792+ state), it serves to show how to code a quantum generative model.
795793</ p >
796794
797- <!-- !bc pycod -->
798795
799- <!-- !ec -->
796+ <!-- code=python (!bc pycod) typeset with pygments style "default" -->
797+ < div class ="cell border-box-sizing code_cell rendered ">
798+ < div class ="input ">
799+ < div class ="inner_cell ">
800+ < div class ="input_area ">
801+ < div class ="highlight " style ="background: #f8f8f8 ">
802+ < pre style ="line-height: 125%; "> < span style ="color: #008000; font-weight: bold "> import</ span > < span style ="color: #0000FF; font-weight: bold "> pennylane</ span > < span style ="color: #008000; font-weight: bold "> as</ span > < span style ="color: #0000FF; font-weight: bold "> qml</ span >
803+ < span style ="color: #008000; font-weight: bold "> from</ span > < span style ="color: #0000FF; font-weight: bold "> pennylane</ span > < span style ="color: #008000; font-weight: bold "> import</ span > numpy < span style ="color: #008000; font-weight: bold "> as</ span > np
804+
805+ < span style ="color: #408080; font-style: italic "> # Use a 2-qubit simulator</ span >
806+ dev < span style ="color: #666666 "> =</ span > qml< span style ="color: #666666 "> .</ span > device(< span style ="color: #BA2121 "> 'default.qubit'</ span > , wires< span style ="color: #666666 "> =2</ span > )
807+
808+ < span style ="color: #408080; font-style: italic "> # Define a simple 2-qubit QCBM circuit</ span >
809+ < span style ="color: #AA22FF "> @qml</ span > < span style ="color: #666666 "> .</ span > qnode(dev)
810+ < span style ="color: #008000; font-weight: bold "> def</ span > < span style ="color: #0000FF "> circuit</ span > (params):
811+ < span style ="color: #408080; font-style: italic "> # params = 4 angles</ span >
812+ qml< span style ="color: #666666 "> .</ span > RX(params[< span style ="color: #666666 "> 0</ span > ], wires< span style ="color: #666666 "> =0</ span > )
813+ qml< span style ="color: #666666 "> .</ span > RY(params[< span style ="color: #666666 "> 1</ span > ], wires< span style ="color: #666666 "> =1</ span > )
814+ qml< span style ="color: #666666 "> .</ span > CNOT(wires< span style ="color: #666666 "> =</ span > [< span style ="color: #666666 "> 0</ span > , < span style ="color: #666666 "> 1</ span > ])
815+ qml< span style ="color: #666666 "> .</ span > RX(params[< span style ="color: #666666 "> 2</ span > ], wires< span style ="color: #666666 "> =0</ span > )
816+ qml< span style ="color: #666666 "> .</ span > RY(params[< span style ="color: #666666 "> 3</ span > ], wires< span style ="color: #666666 "> =1</ span > )
817+ < span style ="color: #008000; font-weight: bold "> return</ span > qml< span style ="color: #666666 "> .</ span > probs(wires< span style ="color: #666666 "> =</ span > [< span style ="color: #666666 "> 0</ span > ,< span style ="color: #666666 "> 1</ span > ]) < span style ="color: #408080; font-style: italic "> # return probabilities of |00>,|01>,|10>,|11></ span >
818+
819+ < span style ="color: #408080; font-style: italic "> # Initialize random parameters</ span >
820+ params < span style ="color: #666666 "> =</ span > np< span style ="color: #666666 "> .</ span > random< span style ="color: #666666 "> .</ span > randn(< span style ="color: #666666 "> 4</ span > , requires_grad< span style ="color: #666666 "> =</ span > < span style ="color: #008000; font-weight: bold "> True</ span > )
821+
822+ < span style ="color: #408080; font-style: italic "> # Get the output distribution from the circuit</ span >
823+ probs < span style ="color: #666666 "> =</ span > circuit(params)
824+ < span style ="color: #008000 "> print</ span > (< span style ="color: #BA2121 "> "Probabilities:"</ span > , probs)
825+ </ pre >
826+ </ div >
827+ </ div >
828+ </ div >
829+ </ div >
830+ < div class ="output_wrapper ">
831+ < div class ="output ">
832+ < div class ="output_area ">
833+ < div class ="output_subarea output_stream output_stdout output_text ">
834+ </ div >
835+ </ div >
836+ </ div >
837+ </ div >
838+ </ div >
800839
801- < p > This circuit takes a parameter vector params of length \( n_v+n_h \) and
802- returns the probabilities \( p_{\Theta}(v) \) of measuring each visible
803- bitstring \( v \). Notice we measure only the visible wires (the
804- wires=list(range(n$\_$v)) in qml.probs marginalizes out the hidden
805- qubit).
840+ < p > In this code, we create a two-qubit variational circuit and return the
841+ probabilities of each basis state. One could train params to match a
842+ target distribution by defining a cost function between
843+ probs and the data distribution, and using PennyLane’s automatic
844+ differentiation to update params. Though not a true QBM (no
845+ Hamiltonian or Gibbs state), this demonstrates how one might build and
846+ train a small quantum generative model in PennyLane.
806847</ p >
807848
808- <!-- !split -->
809- < h2 id ="kullback-leibler-divergence " class ="anchor "> Kullback-Leibler divergence </ h2 >
810-
811- < p > Next, we train this model to match a target dataset distribution.
812- Suppose our data has distribution target = \( [p(00), p(01), p(10),
813- p(11)] \). We can define the (classical) loss as the Kullback-Leibler
814- divergence \( D_{\mathrm{KL}}(p_{\mathrm{data}}\vert\vert q_\Theta) \) or simply the
815- negative log-likelihood. Then we update params by gradient descent.
816- PennyLane’s automatic differentiation can compute gradients via the
817- parameter-shift rule, but we show an explicit parameter-shift
818- computation for demonstration:
849+ < p > To make it more QBM-like, one could encode the classical visible units
850+ as fixed inputs. For example, to compute the energy of a QBM
851+ Hamiltonian for a given visible bitstring, one might do:
819852</ p >
820853
821854
@@ -825,7 +858,18 @@ <h2 id="kullback-leibler-divergence" class="anchor">Kullback-Leibler divergence
825858 < div class ="inner_cell ">
826859 < div class ="input_area ">
827860 < div class ="highlight " style ="background: #f8f8f8 ">
828- < pre style ="line-height: 125%; "> Initialize parameters < span style ="color: #AA22FF; font-weight: bold "> and</ span > perform a simple gradient descent
861+ < pre style ="line-height: 125%; "> < span style ="color: #408080; font-style: italic "> # Example: compute expectation of a simple Hamiltonian for a given state |v></ span >
862+ H < span style ="color: #666666 "> =</ span > < span style ="color: #666666 "> 1.5</ span > < span style ="color: #666666 "> *</ span > qml< span style ="color: #666666 "> .</ span > PauliZ(< span style ="color: #666666 "> 0</ span > ) < span style ="color: #666666 "> +</ span > < span style ="color: #666666 "> 0.7</ span > < span style ="color: #666666 "> *</ span > qml< span style ="color: #666666 "> .</ span > PauliZ(< span style ="color: #666666 "> 1</ span > ) < span style ="color: #666666 "> +</ span > < span style ="color: #666666 "> 0.9</ span > < span style ="color: #666666 "> *</ span > qml< span style ="color: #666666 "> .</ span > PauliZ(< span style ="color: #666666 "> 0</ span > )< span style ="color: #AA22FF "> @qml</ span > < span style ="color: #666666 "> .</ span > PauliZ(< span style ="color: #666666 "> 1</ span > )
863+
864+ < span style ="color: #AA22FF "> @qml</ span > < span style ="color: #666666 "> .</ span > qnode(dev)
865+ < span style ="color: #008000; font-weight: bold "> def</ span > < span style ="color: #0000FF "> energy_of_state</ span > ():
866+ < span style ="color: #408080; font-style: italic "> # Prepare visible qubits in state |v> = |01>, say</ span >
867+ qml< span style ="color: #666666 "> .</ span > PauliX(wires< span style ="color: #666666 "> =1</ span > ) < span style ="color: #408080; font-style: italic "> # flips qubit 1 to |1></ span >
868+ < span style ="color: #408080; font-style: italic "> # No hidden qubits in this simple example</ span >
869+ < span style ="color: #408080; font-style: italic "> # Return expectation of H in this state</ span >
870+ < span style ="color: #008000; font-weight: bold "> return</ span > qml< span style ="color: #666666 "> .</ span > expval(H)
871+
872+ < span style ="color: #008000 "> print</ span > (< span style ="color: #BA2121 "> "Energy of |01> state:"</ span > , energy_of_state())
829873</ pre >
830874</ div >
831875 </ div >
@@ -841,13 +885,43 @@ <h2 id="kullback-leibler-divergence" class="anchor">Kullback-Leibler divergence
841885 </ div >
842886</ div >
843887
844- < p > This code illustrates the training loop. At each epoch we evaluate
845- the loss, compute gradients (via two forward passes per parameter),
846- and update the parameters. In practice one can use qml.grad for
847- automatic gradients, and more sophisticated optimizers (Adam, natural
848- gradient, etc.). The above shows that PennyLane can seamlessly
849- integrate quantum circuit definitions with classical training logic.
888+ < p > Here, we used a two-qubit Hamiltonian
889+ \( H=1.5\sigma_z^0+0.7\sigma_z^1+0.9\sigma_z^0\sigma_z^1 \) and prepared
890+ the state \( \vert v\rangle=\vert 01\rangle \). The < b > expval(H)</ b > call returns
891+ </ p >
892+ $$
893+ \langle 01 \vert H \vert 01\rangle = -1.5 + 0.7 - 0.9 = -1.7,
894+ $$
895+
896+ < p > (up to sign
897+ conventions). This shows how PennyLane can compute energies of basis
898+ states, which is a key step in evaluating the QBM energy and
899+ probability of data states.
900+ </ p >
901+
902+ < p > In practice, training a QBM in PennyLane would involve preparing
903+ quantum states corresponding to the Gibbs distribution. One could use
904+ functions like qml.qaoa.cost.Hamiltonian or custom circuits to
905+ approximate \( \exp{-\beta H} \), and then use PennyLane’s optimizers. Also,
906+ PennyLane can interface with PyTorch or TensorFlow, enabling hybrid
907+ optimization of quantum circuits with classical parameters (e.g. the
908+ Hamiltonian weights).
850909</ p >
910+
911+ <!-- !split -->
912+ < h2 id ="summary-of-steps-in-pennylane-implementation " class ="anchor "> Summary of steps in PennyLane implementation </ h2 >
913+
914+ < ol >
915+ < li > Define a quantum device (qml.device) with enough wires for visibles+hidden.</ li >
916+ </ ol >
917+ < p > 0 Construct a parametric quantum circuit (ansatz) that depends on trainable parameters (e.g. angles in rotations and entangling gates).</ p >
918+ < ol >
919+ < li > If modeling an RQBM, one might clamp visible qubits (preparing them according to training data) and apply gates only to hidden qubits.</ li >
920+ < li > Measure relevant observables: one can return probabilities (probs) or expectation values (expval) of Pauli operators, depending on the chosen loss function.</ li >
921+ < li > Define a cost function, such as the negative log-likelihood or a distance between output and target distribution.</ li >
922+ </ ol >
923+ < p > Use an optimizer (e.g. gradient descent, Adam) with PennyLane’s gradient calculations to update parameters.</ p >
924+
851925<!-- ------------------- end of main content --------------- -->
852926</ div > <!-- end container -->
853927<!-- include javascript, jQuery *first* -->
0 commit comments