enter image description here

This is the circuit for NormalDistribution(3, mu=1, sigma=1, bounds=(0, 2)). How do I understand what this circuit is doing?

  • $\begingroup$ How did you come up with the circuit? Do you have a reference? $\endgroup$
    – M. Stern
    Jun 26 '21 at 16:13
  • $\begingroup$ @M.Stern I think the circuit is generated by the NormalDistribution() function, so they didn't need to come up with it, here is the documentation of that function. $\endgroup$
    – epelaaez
    Jun 26 '21 at 17:58
  • $\begingroup$ yes,I just decomposed the NormalDistribution function $\endgroup$ Jun 26 '21 at 18:09
  • $\begingroup$ Did you read the documentation? especially this statement: "Since, in general, it is not yet known how to efficiently prepare the qubit amplitudes to represent a normal distribution, this class computes the expected amplitudes and then uses the QuantumCircuit.initialize method to construct the corresponding circuit." $\endgroup$ Jun 27 '21 at 5:54
  • $\begingroup$ Yes, but still I would like to understand how the circuit tries to accomplish normal distribution. What is the gate logic behind this circuit.Understanding that can help me solve simpler probability distribution problems. $\endgroup$ Jun 27 '21 at 7:44

As stated in the comments, behind the scenes, qiskit relies on the QuantumCircuit.initialize method to generated this state. Indeed, one can find in the source code:

x = np.linspace(bounds[0], bounds[1], num=2**num_qubits)
probabilities = multivariate_normal.pdf(x, mu, sigma)
normalized_probabilities = probabilities / np.sum(probabilities)
initialize = Initialize(np.sqrt(normalized_probabilities))
circuit = initialize.gates_to_uncompute().inverse()
self.compose(circuit, inplace=True)

As you can see, qiskit just computes the desired amplitudes and calls the initialize method with them. In your case, you would get the state, up to a normalization factor: $$\sum_{k=0}^7\exp\left(\frac{\left(\frac{2\,k}{7}-1\right)^2}{2}\right)\,|k\rangle\,.$$ Now, your question is "How does the generated circuit relate to the normal distribution?". Well, it kind of doesn't. The initialize method does not know that you are trying to generate a normal distribution. The algorithm that is used, according to this tutorial, is generic: it works for generating any pure quantum state, and its goal is just to generate a quantum state given a vector of amplitudes.

Another way to think about it is to consider what would happen if you were to use QRAM instead: for any distribution of probabilities, the circuit will be the same. The only thing that changes is the angles of rotation of the gates you apply. Since there is no entanglement in the resulting state you want to create, I guess that this fact also remains true (or almost true) for qiskit's algorithm.

I suspect that there is no "Normal distribution-related" circuit known as of today, otherwise I believe qiskit's developers would have had implemented it for this class. If your goal is to simulate classical distributions, I think it is easier either to use the initialize method like above, or to look for articles giving a dedicated quantum circuit for generating them.

  • $\begingroup$ I understood from your explanation that the circuit is trying to implement the discretized version of Normal distribution. I am curious to know how the circuit implements the formula you mentioned in your application above so that I can implement any other formula on my own. How does the manipulation of the gates in the circuit achieve the formula? $\endgroup$ Jun 30 '21 at 16:57
  • $\begingroup$ @AbhishekKishore What qiskit uses is the method from this article: arxiv.org/pdf/quant-ph/0406176.pdf. You can also use the method from this paper, which uses "QRAM" (just store the amplitudes from your distribution within it): arxiv.org/pdf/1802.08227.pdf (look at page 26). An instance of implementation for the latter can be found here: github.com/tnemoz/iso/blob/master/…. Note that this code isn't commented (whoever wrote it mustn't be proud of it) and uses cirq, but you may be able to understand how it works by reading through. $\endgroup$ Jun 30 '21 at 17:58

Let's break down the specific example you provided and see:

(1) What the circuit is doing

(2) How this represents a normal distribution

(1) What the circuit is doing

If you take the input state vector $\left| 0 \right>^{3}$ and run it through your decomposed circuit you will end up with state vector:

sv_p = {'000': 0.092361231483064, '001': 0.117990637381356, '010': 0.13891615965671, '011': 0.150731971478871, '100': 0.150731971478871, '101': 0.13891615965671, '110': 0.117990637381356, '111': 0.092361231483064}.

This state vector represents the probabilities of each possible 3-qubit state. If you take the square root of these probabilities you will end up with the following set of amplitudes:

sv_z = {'000': '0.30391', '001': '0.34350', '010': '0.37271', '011': '0.38824', '100': '0.38824', '101': '0.37271', '110': '0.34350', '111': '0.30391'}

Which if you look at the Qiskit documentation for NormalDistribution you will see matches the input to QuantumCircuit.initialize() for your example.

You can try to calculate by hand but it will involve a lot of matrix/tensor multiplication. How Qiskit is able to translate it's target state vector into the specific gates and circuits you have decomposed is probably an interesting algorithm inside the QuantumCircuit.initialize() source code, but I don't have that answer for you here.

(2) How this represents a normal distribution

The specific example you cited tries to represent a discretized normal distribution with mu=1, sigma=1 and bounds=(0,2) using 3 qubits resulting in $2^3 = 8$ states.

If you generate 8 equidistant points ($x_i$) between 0 and 2 and calculate the probability of each point using NormalDistribution(1,1) (and then normalize the values), you will end up with probabilities $p(x_i)$ and amplitudes to $z(x_i)$ (square root of the probailities):

p(x_i) = {0.0: '0.09236', 0.2857142857142857: '0.11799', 0.5714285714285714: '0.13892', 0.8571428571428571: '0.15073', 1.1428571428571428: '0.15073', 1.4285714285714284: '0.13892', 1.7142857142857142: '0.11799', 2.0: '0.09236'}
z(x_i) = {0.0: '0.30391', 0.2857142857142857: '0.34350', 0.5714285714285714: '0.37271', 0.8571428571428571: '0.38824', 1.1428571428571428: '0.38824', 1.4285714285714284: '0.37271', 1.7142857142857142: '0.34350', 2.0: '0.30391'}

Now instead of mapping the values to the 8 $x_i$ values, you can map it to the 8 qubit states ['000', '001', '010', '011', '100', '101', '110', '111'] and voila! Some code below may help clarify.

enter image description here


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.