转!如何用python模拟量子电路

我只是个搬运工。。。原文链接https://jarrodmcclean.com/basic-quantum-circuit-simulation-in-python/

BASIC QUANTUM CIRCUIT SIMULATION IN PYTHON

CODE SCIENCE AUGUST 17, 2016

I’ve always been a proponent of the idea that one of the best ways to learn about a topic is to code up a simple example that uses that idea/concept/algorithm.  In conversations I’ve had with students recently, I’ve realized there is some interest in playing with quantum computing, quantum circuits, and quantum simulation without a full background in quantum physics.  I thought a good way to help bridge this gap would be to provide some pedagogical examples of how one might go about simulating a quantum circuit.  This won’t focus on computing performance or approaches to simulating large systems (for information on that topic, this paper is a good reference), but rather simple ways to simulate actions on a small number of qubits.

Brief background

A classical digital computer is built upon a foundation of bits, which take on values of 0 or 1, and store information.  Clearly we also need to manipulate and work with that data, and the action on bits are performed by classical logical gates, such as the “AND” gate.  In analogy to classical bits, in quantum computing, we have quantum bits, which take on values \ket{0} and \ket{1}.  Similarly, we need to manipulate the data stored in quantum bits, and the equivalent operations are called quantum gates.  Quantum bits have a number of nice properties absent in their classical counterparts, such as superposition and entanglement, and quantum gates are designed to make sure they can exploit these features, a property known as unitarity.  For the purposes of this simple post, the exact details of these properties will be unimportant, as a correct mathematical representation of qubits and quantum gates often allows us to have these features naturally in our numerical simulations.

One Qubit

The state of a qubit may be represented as a column vector, and conventionally

 \begin{align*} \ket{0} &= \left( \begin{array}{c} 1 \\ 0 \end{array} \right) \\ \ket{1} &= \left( \begin{array}{c} 0 \\ 1\end{array} \right) \end{align*}

In Python, we can represent such states as arrays in Numpy with the correct dimensions as:

import numpy as np
import scipy as sp
import scipy.linalg
 
Zero = np.array([[1.0],
                 [0.0]])
One = np.array([[0.0],
                [1.0]])

which gives us a good representation of either the \ket{0} (Zero) or \ket{1} (One) state. Say we wanted to create a superposition of \ket{0}and \ket{1}, for example, the standard \ket{+} state which is defined by \ket{+}= 1/\sqrt{2} (\ket{0} + \ket{1}). The factor 1/\sqrt{2} is called a normalization factor, and helps ensure the relation to a probability distribution. If we wanted to create this state, one way of doing this in Python might be to define a normalization function, and input the raw state into it as

NormalizeState = lambda state: state / sp.linalg.norm(state)
 
Plus = NormalizeState(Zero + One)

Now that we know how to create single qubit states, we might want to be able to perform interesting actions on them. Quantum gates in acting on single qubits are given by two-by-two matrices. One interesting gate is the Hadamard gate, given by

 \begin{align*} \frac{1}{\sqrt{2}} \left( \begin{array}{cc} 1 & 1 \\ 1 & -1 \end{array} \right) \end{align*}

we can represent this gate in Python and examine its action on the state \ket{0} (that is, H \ket{0} = ?) through the following:

Hadamard = 1./np.sqrt(2) * np.array([[1, 1],
                                     [1, -1]])
 
NewState = np.dot(Hadamard, Zero)

where “np.dot” in Python serves the role of matrix multiply for Numpy arrays. Take a look at NewState, you should find it has a striking resemblance to state we just examined a few lines ago…

More than one qubit

While one qubit is interesting, more than one is certainly more interesting. How do we generalize what we just learned about representing one qubit to many qubits? States of many qubits sometimes have a few different notations, for example \ket{001} or \ket{0} \otimes \ket{0} \otimes \ket{1}, which are the same state. The symbol \otimes is a source of confusion to many people, as rapid switches between its implicit and explicit use are rampant in quantum computing, and the symbol is essentially absent in some fields of physics, despite the operation being ubiquitous. The symbol \otimes stands for the “tensor product” between two states and it allows for a correct description of many particle or many qubit states. That is, the space occupied by 2 qubits is the tensor product of the space of two single qubits, similarly the space of N qubits is the N-fold tensor product of N single qubit spaces. Time spent on the Wikipedia page for tensor product is prone to causing excessive head-scratching wondering what the “universal property” is, and how it’s relevant. Fear not, most of these abstract properties are not required reading for working with a practical example of a tensor product, which is what we will do here.

A practical example of a tensor product that we will use here, is the Kronecker product, which is implemented in Numpy, Matlab, and many other code packages. We will use the fact that it faithfully takes care of the important properties of the tensor product for us to represent and act on many qubit states. So how do we build a correct representation of the state \ket{00} or \ket{++} using it in Python? Using the Kronecker product, it’s as easy as

ZeroZero = np.kron(Zero, Zero)
OneOne = np.kron(One, One)
PlusPlus = np.kron(Plus, Plus)

What about an entangled Cat state such as 1/\sqrt{2} (\ket{00} + \ket{11}) you ask?

CatState = NormalizeState(ZeroZero + OneOne)

And if we want to create a 5-qubit state, such as \ket{10101}? We can simply chain together Kronecker products. A simple helper function makes this look a bit less nasty

def NKron(*args):
  """Calculate a Kronecker product over a variable number of inputs"""
  result = np.array([[1.0]])
  for op in args:
    result = np.kron(result, op)
  return result
 
FiveQubitState = NKron(One, Zero, One, Zero, One)   

Take note of how the size of the state grows as you add qubits, and be careful not to take down Python by asking it to make a 100-Qubit state in this way! This should help to give you an inkling of where quantum computers derive their advantages.

So how do we act on multi-qubit states with our familiar quantum gates like the Hadamard gate? The key observation for our implementation here is the implicit use of Identity operators and the Kronecker product. Say H_0 denotes a Hadamard gate on qubit 0. How do we represent H_0? The answer is, it depends, specifically on how many total qubits there are in the system. For example, if I want to act H_0 on \ket{00}, then implicitly I am acting H_0 \otimes I_1 \ket{00} and if I am acting on \ket{10101} then implicitly I am performing H_0 \otimes I_1 \otimes I_2 \otimes I_3 \otimes I_4\ket{10101}, and this is important for the numerical implementation. Lets see the action H_0 \ket{10101} in practice,

Id = np.eye(2)
HadamardZeroOnFive = NKron(Hadamard, Id, Id, Id, Id)
NewState = np.dot(HadamardZeroOnFive, FiveQubitState)

Luckily, that’s basically all there is to single qubit gates acting on many-qubit states. All the work is in keeping track of “Id” placeholders to make sure your operators act on the same space as the qubits.

Many-qubit gates require a bit more care. In some cases, a decomposition amenable to a collection of a few qubits is available, which makes it easy to define on an arbitrary set of qubits. In other cases, it’s easier to define the operation on a fixed set of qubits, then swap the other qubits into those slots, perform the operation, and swap back. Without covering every edge case, we will use what is perhaps the most common 2-qubit gate, the CNOT gate. The action of this gate is to enact a NOT operation on a qubit, conditional on the state of another qubit. The quantum NOT operation is given by the Pauli X operator, defined by

 \begin{align*} X = \left( \begin{array}{cc} 0 & 1 \\ 1 & 0 \end{array} \right) \end{align*}

In order to perform this action dependent on the state of another qubit, we need a sort of quantum IF-statement. We can build this from projectors of the form P_0 = \ket{0}\bra{0} and P_1 = \ket{1}\bra{1} that tell us if a qubit is in state 0 or 1. In our representation, the so-called “bra” \bra{0} is just the conjugate transpose of the “ket” \ket{0}, so these projectors can be written as an outer product

 \begin{align*} P_0 = \left( \begin{array}{c} 1 \\ 0 \end{array} \right) \left( \begin{array}{cc} 1 & 0 \end{array} \right) \\ P_1 = \left( \begin{array}{c} 0 \\ 1 \end{array} \right) \left( \begin{array}{cc} 0 & 1 \end{array} \right) \\ \end{align*}

With this, we can define a CNOT gate on qubits 0 and 1 as CNOT=P_0 \otimes I + P_1 \otimes X. So if we wanted to perform a CNOT operation, controlled on qubit 0, acting on qubit 3 of our 5 qubit state, we could do so in Python as

P0 = np.dot(Zero, Zero.T)
P1 = np.dot(One, One.T)
X = np.array([[0,1],
              [1,0]])
 
CNOT03 = NKron(P0, Id, Id, Id, Id) + NKron(P1, Id, Id, X, Id)
NewState = np.dot(CNOT03, FiveQubitState)

This covers almost all the requirements for simulating the action of quantum circuit of your choosing, assuming you have the computational power to do so! So long as you can reduce the circuit you are interested in into an appropriate set of 1- and 2-qubit gates. Doing this in the generic case is still a bit of a research topic, but many algorithms can already be found in this form, so it should be reasonably easy to find a few to play with!

Measurement

Now that we’ve talked about how to simulate the action of a circuit on a set of qubits, it’s important to think about how to actually get information out! This is the topic of measurement of qubits! At a basic level, we will want to be able to ask if a particular qubit is 0 or 1, and simulate the resulting state of the system after receiving the information on that measurement! Since qubits can be in superpositions between 0 and 1 (remember the state \ket{+}), when we ask that qubit for its value, we get 0 or 1 with some probability. The probability of qubit i being in a state 0 is given by the fraction of the state matching that, which can be concisely expressed as

 \begin{align*} \text{Tr}\left[\ket{\Psi}\bra{\Psi} P_0^i \right] \end{align*}

where \ket{\Psi} is some generic state of qubits and P_0 is the same projector we had above, acting on the i‘th qubit. If we measure a 0, then the resulting state of the system is given by

 \begin{align*} P_0^i \ket{\Psi} / || P_0^i \ket{\Psi}||_2 \end{align*}

recalling that anywhere where the number of qubits don’t match up, we are following the generic rule of inserting identity operators to make sure everything works out! In Python, we could simulate measurements on a Cat state using the above rules as follows

import numpy.random
 
CatState = NormalizeState(ZeroZero + OneOne)
RhoCatState = np.dot(CatState, CatState.T)
 
#Find probability of measuring 0 on qubit 0
Prob0 = np.trace(np.dot(NKron(P0, Id), RhoCatState))
 
#Simulate measurement of qubit 0
if (np.random.rand() < Prob0):
    #Measured 0 on Qubit 0
    Result = 0
    ResultState = NormalizeState(np.dot(NKron(P0, Id), CatState))
else:
    #Measured 1 on Qubit 1
    Result = 1
    ResultState = NormalizeState(np.dot(NKron(P1, Id), CatState))
 
print "Qubit 0 Measurement Result: {}".format(Result)
print "Post-Measurement State:"
print ResultState

If you look you will see that upon measurement the state collapses to either the state \ket{00} or \ket{11} with equal probability, meaning that a subsequent measurement of the second qubit will yield a deterministic result! This is a signature of entanglement between the two qubits!

Summary

So in this short post we’ve covered how to represent qubits, quantum gates and their actions, and measurement of qubits in Python by using a few simple features in Numpy/Scipy. These simple actions are the foundation for numerical simulation of quantum circuits and hopefully gives you some idea of how these simulations work in general. If you have any questions or want me to elaborate on a specific point, feel free to leave a comment and I will update the post or make a new one when I get the chance!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 量子纠缠是量子物理学中的一个重要概念,指的是两个或更多的量子系统之间存在的特殊的相互关系。这种相互关系使得这些系统的性质和行为是相互联系的,即使它们被隔离在相互独立的环境中。 举个例子,假设有两个纠缠的电子,它们的自旋状态是相互联系的。假如我们测量其中一个电子的自旋状态,那么另一个电子的自旋状态也会立即确定。这种现象被称为“远程协调”。 在 Python 中,我们可以使用量子计算框架如 Qiskit 来模拟量子纠缠。例如,下面的代码演示了如何使用 Qiskit 创建一个简单的量子纠缠态,即两个电子的 Bell 状态: ``` from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute, Aer # 创建量子寄存器和经典寄存器 qr = QuantumRegister(2) cr = ClassicalRegister(2) # 创建量子电路 qc = QuantumCircuit(qr, cr) # 在量子寄存器 qr 的第一个电子上执行 Hadamard 操作 qc.h(qr[0]) # 在量子寄存器 qr 的第二个电子上执行 CNOT 操作,控制电子为第一个电子,目标电子为第二个电子 qc.cx(qr[0], qr[1]) # 在量子寄存 ### 回答2: 量子纠缠是一种特殊的量子力学现象,它描述的是两个或多个量子系统之间的一种紧密的联系,即使这些系统之间处于不同的空间位置也可以互相影响。在Python中,我们可以使用量子计算库(例如Qiskit)来模拟和探索量子纠缠。 下面是一个简单的例子,演示了两个量子比特之间的纠缠: ```python from qiskit import QuantumCircuit, execute, Aer from qiskit.visualization import plot_bloch_multivector # 创建两个量子比特的量子电路 qc = QuantumCircuit(2) # 将第一个量子比特置于叠加态(|0> + |1>) / sqrt(2) qc.h(0) # 对第二个量子比特应用CNOT门,使其与第一个量子比特纠缠在一起 qc.cx(0, 1) # 在模拟器上运行量子电路并获取结果 simulator = Aer.get_backend('statevector_simulator') result = execute(qc, simulator).result() statevector = result.get_statevector() # 打印量子系统的状态向量 print(statevector) # 绘制两个量子比特的量子态向量图 plot_bloch_multivector(statevector) ``` 在上述代码中,我们首先创建了一个含有两个量子比特的量子电路。然后,我们将第一个量子比特置于叠加态(|0> + |1>) / sqrt(2)中。接下来,我们对第二个量子比特应用CNOT门,以实现两个量子比特之间的纠缠。最后,我们在模拟器上运行量子电路并获取结果,得到纠缠后的量子态信息。我们打印了量子系统的状态向量,并使用可视化工具绘制了两个量子比特的量子态向量图。 通过以上代码的运行,我们可以观察到量子系统的状态向量和量子态向量图,从而可直观地了解两个量子比特之间的纠缠现象。这个简单的例子展示了Python量子计算领域的应用,并且可以为学习和研究量子纠缠提供一定的帮助。 ### 回答3: 量子纠缠是一种神奇的现象,它是量子力学的核心概念之一。在python中,我们可以使用量子计算库Qiskit来模拟和演示量子纠缠。 下面是一个简单的例子,演示了如何使用Qiskit创建两个纠缠的量子比特: ``` from qiskit import QuantumCircuit, execute, Aer # 创建一个包含两个量子比特的量子电路 circuit = QuantumCircuit(2, 2) # 在第一个量子比特上应用一个Hadamard门 circuit.h(0) # 在第一个量子比特和第二个量子比特之间创建纠缠 circuit.cx(0, 1) # 在两个量子比特上分别应用测量门 circuit.measure(0, 0) circuit.measure(1, 1) # 使用一个模拟器来模拟运行结果 simulator = Aer.get_backend('qasm_simulator') job = execute(circuit, simulator, shots=1000) # 获取测量结果并打印 result = job.result() counts = result.get_counts(circuit) print(counts) ``` 运行这段代码,我们会得到一个类似于`{'00': 500, '11': 500}`的输出结果。这表示在1000次运行中,约有500次测量结果为00,500次测量结果为11。这说明两个量子比特是纠缠在一起的,当其中一个量子比特测量为`|0>`时,另一个量子比特也会测量为`|0>`,同理,当其中一个量子比特测量为`|1>`时,另一个量子比特也会测量为`|1>`。这正是量子纠缠的特性所在。 通过这个例子,我们可以看到,使用Qiskit这样的量子计算库,我们可以方便地模拟和演示量子纠缠现象,这对于进一步探索和理解量子力学的奥秘具有重要意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值