Statistical Operations¶
Statistical operations are simulator specific mathematical operations allowing you to compute properties of the simulated quantum state without making it collapse. All statistical operations will result in a real or complex number that will be stored in the Z-Register, and can be accessed from the results of the simulation through the zstates option, see Circuits and cloud execution page.
On this page you will find all statistical operations available on MIMIQ with explanations and examples.
Expectation value¶
Mathematical definition¶
An expectation value for a pure state \(| \psi \rangle\) is defined as
where \(O\) is an operator. With respect to a density matrix \(\rho\) it’s given by
Usage on MIMIQ¶
First we need to define the operator \(O\) of which we will compute the expectation value
>>> op = SigmaPlus()
SigmaPlus is only one of the many operators available. Of course, every gate can be used as an operator, for example op = GateZ().
However, MIMIQ also supports many non-unitary operators such as SigmaPlus, more about this on the Operators page.
To ask MIMIQ to compute the expectation value for a circuit you can create an ExpectationValue object and push() it to the circuit like this:
>>> circuit = Circuit()
>>> circuit.push(GateH(), 0)
1-qubit circuit with 1 instructions:
└── H @ q[0]
>>> # Ask to compute the expectation value
>>> ev = ExpectationValue(op)
>>> circuit.push(ev, 0, 0)
1-qubit, 1-zvar circuit with 2 instructions:
├── H @ q[0]
└── ⟨SigmaPlus(1)⟩ @ q[0], z[0]
push() function always follow the order of quantum register index first, classical register second (none in this case), and Z-register index last.0 is the index for the first qubit of the quantum register and the second 0 is the index of the Z-Register.Notice that the expectation value will be computed with respect to the quantum state of the system at the point in the circuit where the ExpectationValue is added.
Entanglement¶
MIMIQ supports statistical operations on entanglement for ordered bipartitions. For instance, for qubits [1...N] MIMIQ can compute the entanglement between the bipartitions [1...k-1] and [k...N].
For this reason when you push() an entanglement operation to a circuit, you need to give it the qubit index k that separates the two bipartitions, as well as the Z-register to store the result.
Warning
The following functions can only be used with the MPS backend.
Von Neumann Entropy¶
Mathematical definition¶
The entanglement entropy for a bipartition into subsystems A and B is defined for a pure state \(\rho = | \psi \rangle\langle \psi\) | as
where \(\rho_A = \mathrm{Tr}_B(\rho)\) is the reduced density matrix. A product state has \(\mathcal{S}(\rho_A)=0\) and a maximally entangled state between A and B gives \(\mathcal{S}(\rho_A)=1\).
We only consider bipartitions where \(A=\{1,\ldots,k-1\}\) and \(B=\{k,\ldots,N\}\),
for some k and where N is the total number of qubits.
Usage on MIMIQ¶
VonNeumannEntropy function.VonNeumannEntropy. To indicate where to create the separation into two subsystems MIMIQ will use the first qubit index given to the push() function, here is an example:>>> circuit = Circuit()
>>> # Asking to compute the Von Neumann entropy between the two subsystems separated between qubit 1 and 2
>>> circuit.push(VonNeumannEntropy(), 2, 0)
3-qubit, 1-zvar circuit with 1 instructions:
└── VonNeumannEntropy @ q[2], z[0]
Here we compute the Von neumann entropy between the two subsystems [1,2] and [3...N] and write the results into the index 0 of the Z-register.
Note
For k=1, A is empty and the Von Neumann entropy will always return 1.
Bond Dimension¶
Mathematical definition¶
The bond dimension is only defined for a matrix-product state (MPS), which can be written as (with \(i_1=i_{N+1}=1\))
Here, \(\chi_k`\) is the bond dimension, i.e. the dimension of the index \(i_k\). The first and last bond dimensions are dummies, \(chi_1=chi_{N+1}=1\). A bond dimension of 1 means there is no entanglement between the two halves of the system.
Usage on MIMIQ¶
To compute the bond dimension between two halves of a system you can use the BondDim operator and push() to the circuit like any entanglement measure:
>>> circuit = Circuit()
>>> # Asking to compute the BondDim between the second and third qubits
>>> circuit.push(BondDim(), 2, 0)
3-qubit, 1-zvar circuit with 1 instructions:
└── BondDim @ q[2], z[0]
Here we compute the bond dimension between the two subsystems [1,2] and [3...N] and write the results into index 0 of the Z-Register.
Note
For k=1 the bond dimension returned will always be 1.
Schmidt Rank¶
Mathematical definition¶
A Schmidt decomposition for a bipartition into subsystems A and B is defined for a pure state as
where \(|\alpha_i\rangle (|\beta_i\rangle)\) are orthonormal states acting on A
(B). The Schmidt rank is the number of terms r in the sum. A product state
gives r=1 and r>1 signals entanglement.
We only consider bipartitions where \(A=\{1,\ldots,k-1\}\) and \(B=\{k,\ldots,N\}\),
for some k and where N is the total number of qubits.
Usage on MIMIQ¶
To compute the Schmidt rank of a bipartition you can use the SchmidtRank operator and push() like all entanglement measures:
>>> circuit = Circuit()
>>> # Asking to compute the Schmidt rank between the two subsystems separated between qubits 1 and 2
>>> circuit.push(SchmidtRank(), 2, 0)
3-qubit, 1-zvar circuit with 1 instructions:
└── SchmidtRank @ q[2], z[0]
Here we compute the Schmidt rank between the two subsystems [1,2] and [3...N] and write the results into index 0 of the Z-Register.
Note
For k=1, A is empty and the Schmidt rank will always return 1.
Amplitude¶
Amplitude.Amplitude function the BitString matching the state for which you want the amplitude.BitString check the BitString documentation page.You can add the Amplitude object to the circuit exactly like any other gate:
>>> mystery_circuit = Circuit()
>>> mystery_circuit.push(GateH(), range(0, 3))
3-qubit circuit with 3 instructions:
├── H @ q[0]
├── H @ q[1]
└── H @ q[2]
>>> # Define the Amplitude operator
>>> amp = Amplitude(BitString("101"))
>>> # Add the amplitude operator to the circuit and write the result in the first complex number of the Z-Register
>>> mystery_circuit.push(amp, 0)
3-qubit, 1-zvar circuit with 4 instructions:
├── H @ q[0]
├── H @ q[1]
├── H @ q[2]
└── Amplitude(bs"101") @ z[0]
This will extract the amplitude of the basis state \(\ket{101}\). When adding the amplitude operation you do not need to give it any specific qubit target, the only index needed is for the Z-register to use for storing the result.
Reference¶
- class mimiqcircuits.ExpectationValue(op)[source]
Bases:
OperationOperation to compute and store the expectation value of an Operator in a z-register.
An expectation value for a pure state \(| \psi \rangle\) is defined as:
Expectation Value for Pure State
\[\langle O \rangle = \langle \psi | O | \psi \rangle\]where \(O\) is an operator. With respect to a density matrix \(\rho\), it’s given by:
Expectation Value for Density Matrix
\[\langle O \rangle = \mathrm{Tr}(\rho O).\]However, when using quantum trajectories to solve noisy circuits, the expectation value is computed with respect to the pure state of each trajectory.
The argument op can be any gate or non-unitary operator.
Note
ExpectationValue is currently restricted to one and two qubit operators.
See also
AbstractOperator,GateExamples
In push!, the first argument corresponds to the qubit, and the second to the z-register.
>>> from mimiqcircuits import * >>> ExpectationValue(GateX()) ⟨X⟩
>>> c = Circuit() >>> c.push(ExpectationValue(GateX()), 1, 1) 2-qubit, 2-zvar circuit with 1 instruction: └── ⟨X⟩ @ q[1], z[1]
>>> c.push(ExpectationValue(SigmaPlus()), 1, 2) 2-qubit, 3-zvar circuit with 2 instructions: ├── ⟨X⟩ @ q[1], z[1] └── ⟨SigmaPlus(1)⟩ @ q[1], z[2]
- __init__(op)[source]
- opname()[source]
- property qregsizes
- property cregsizes
- property zregsizes
- inverse()[source]
- power(_)[source]
- static isunitary()[source]
Check if the class represents a unitary operator.
By default, this method returns False unless explicitly overridden in a subclass.
- iswrapper()[source]
- asciiwidth(qubits, bits=[], zvars=[])[source]
Calculate the width for ASCII drawing.
- get_operation()[source]
- getparams()[source]
- evaluate(d)[source]
- class mimiqcircuits.VonNeumannEntropy[source]
Bases:
OperationOperation to get the bipartite Von Neumann entanglement entropy and store it in a z-register.
The entanglement entropy for a bipartition into subsystems \(A\) and \(B\) is defined for a pure state \(\rho = | \psi \rangle\langle \psi |\) as:
Entanglement Entropy
\[\mathcal{S}(\rho_A) = - \mathrm{Tr}(\rho_A \log_2 \rho_A) = - \mathrm{Tr}(\rho_B \log_2 \rho_B) = \mathcal{S}(\rho_A)\]where \(\rho_A = \mathrm{Tr}_B(\rho)\) is the reduced density matrix. A product state has \(\mathcal{S}(\rho_A)=0\) and a maximally entangled state between \(A\) and \(B\) gives \(\mathcal{S}(\rho_A)=1\).
We only consider bipartitions where \(A=\{1,\ldots,k-1\}\) and \(B=\{k,\ldots,N\}\), for some \(k\) and where \(N\) is the total number of qubits.
When the system is open (i.e., with noise) and we are using quantum trajectories, the entanglement entropy of each trajectory is returned during execution.
See also
BondDim,SchmidtRankExamples
When pushing to a circuit, the qubit index k takes the role of the above bipartition into A and B. For k=1, A is empty and the entanglement entropy will always return 0.
>>> from mimiqcircuits import * >>> k = 5 >>> c = Circuit() >>> c.push(VonNeumannEntropy(), k, 1) 6-qubit, 2-zvar circuit with 1 instruction: └── VonNeumannEntropy @ q[5], z[1]
- __init__()[source]
- property num_qubits
- property parnames
- iswrapper()[source]
- inverse()[source]
- power(p)[source]
- control(num_qubits)[source]
- static isunitary()[source]
Check if the class represents a unitary operator.
By default, this method returns False unless explicitly overridden in a subclass.
- class mimiqcircuits.BondDim[source]
Bases:
OperationOperation to get the bond dimension between two halves of the system and store it in a z-register.
The bond dimension is only defined for a matrix-product state (MPS), which can be written as:
State Representation
\[|\psi \rangle = \sum_{s_1,s_2,\ldots=1}^2 \sum_{i_1}^{\chi_1} \sum_{i_2}^{\chi_2} \ldots \sum_{i_N}^{\chi_N} A^{(s_1)}_{i_0i_1} A^{(s_2)}_{i_1 i_2} A^{(s_3)}_{i_2 i_3} \ldots A^{(s_N)}_{i_{N-1}i_N} | s_1, s_2, s_3, \ldots, s_N \rangle .\]Here, \(\chi_k\) is the bond dimension, i.e., the dimension of the index \(i_k\). The first and last bond dimensions are dummies, \(\chi_0=\chi_N=1\). A bond dimension of 1 means there is no entanglement between the two halves of the system.
See also
VonNeumannEntropy,SchmidtRankExamples
When pushing to a circuit, the qubit index k that we give will return the bond dimension \(i_{k-1}\) in the above notation. In other words, we associate link k with qubit k+1. For k=1, the bond dimension returned will always be 1.
>>> from mimiqcircuits import * >>> k = 5 >>> c = Circuit() >>> c.push(BondDim(), k, 1) 6-qubit, 2-zvar circuit with 1 instruction: └── BondDim @ q[5], z[1]
- __init__()[source]
- property num_qubits
- property parnames
- iswrapper()[source]
- inverse()[source]
- power(p)[source]
- control(num_qubits)[source]
- static isunitary()[source]
Check if the class represents a unitary operator.
By default, this method returns False unless explicitly overridden in a subclass.
- class mimiqcircuits.SchmidtRank[source]
Bases:
OperationOperation to get the Schmidt rank of a bipartition and store it in a z-register.
A Schmidt decomposition for a bipartition into subsystems \(A\) and \(B\) is defined for a pure state as:
Schmidt Decomposition
\[|\psi\rangle = \sum_{i=1}^{r} s_i |\alpha_i\rangle \otimes |\beta_i\rangle,\]where \(|\alpha_i\rangle\) (\(|\beta_i\rangle\)) are orthonormal states acting on \(A\) (\(B\)). The Schmidt rank is the number of terms \(r\) in the sum. A product state gives \(r=1\), and \(r>1\) signals entanglement.
We only consider bipartitions where \(A=\{1,\ldots,k-1\}\) and \(B=\{k,\ldots,N\}\), for some \(k\) and where \(N\) is the total number of qubits.
In MPS (Matrix Product States), when the state is optimally compressed, the Schmidt rank should be equal to the bond dimension (see
BondDim).See also
VonNeumannEntropy,BondDimExamples
When pushing to a circuit, the qubit index k takes the role of the above bipartition into A and B. For k=1, A is empty and the Schmidt rank will always return 1.
>>> from mimiqcircuits import * >>> k = 5 >>> c = Circuit() >>> c.push(SchmidtRank(), k, 1) 6-qubit, 2-zvar circuit with 1 instruction: └── SchmidtRank @ q[5], z[1]
- __init__()[source]
- property num_qubits
- property parnames
- iswrapper()[source]
- inverse()[source]
- power(p)[source]
- control(num_qubits)[source]
- static isunitary()[source]
Check if the class represents a unitary operator.
By default, this method returns False unless explicitly overridden in a subclass.
- class mimiqcircuits.Amplitude(bs)[source]
Bases:
OperationAmplitude operation
multi qubit Amplitude operation in the computational basis
The operation projects the quantum states complex variables and stores in a z-register.
Examples
>>> from mimiqcircuits import * >>> c = Circuit() >>> c.push(Amplitude(BitString(2)),0) 1-zvar circuit with 1 instruction: └── Amplitude(bs"00") @ z[0]
- __init__(bs)[source]
- property zregsizes
- iswrapper()[source]
- inverse()[source]
- static isunitary()[source]
Check if the class represents a unitary operator.
By default, this method returns False unless explicitly overridden in a subclass.