How do you make projective measurements?

Quick summary of my question:

Beginner here looking to simulate a time evolution of an initial state with low entanglement to which local projective measurements, not expectation values, are applied. I know how to do the Hamiltonian time evolution but not the projective measurements.

Further details:

I am interested in a 1D chain of spin-1/2s with a many-body Hamiltonian

H = \sum_n J(X_n X_{n+1} + Y_n Y_{n+1} + Z_n Z_{n+1}) + h Z_n.

I want to evolve an initial state |\psi(0)\rangle, probably using TEBD, from t=0 to t=T. During this evolution, I want to apply many local projective measurements M^{(n)}, at different times, of observables that look like

O^{(1)} = X \otimes \mathbb{I} \otimes \mathbb{I}\otimes \cdots \otimes \mathbb{I}

and store their outcomes (without halting the simulation). This means I will project M^{(1)}:|\psi(t)\rangle\mapsto|\alpha_n^{(1)}\rangle \langle \alpha_n^{(1)}|\psi(t)\rangle with probability |\langle \alpha_n^{(1)}|\psi(t)\rangle|^2, where |\alpha_n^{(1)}\rangle is an eigenstate of O^{(1)}. For this, I need to of course randomly sample the set of |\alpha_n^{(1)}\rangle's each with probability |\langle \alpha_n^{(1)}|\psi(t)\rangle|^2. I haven’t been able to find an example code where I have clearly understood projective measurements like the ones I’m describing were performed and not expectation values like \langle \psi(t) | O^{(1)} |\psi(t)\rangle.

I see that there is a measure! method that can be used for DMRG but I don’t understand why is specific to DMRG, I don’t want/need to use DMRG.

I do realise I must have missed this and I apologise in advance for that, I’m very new to the package!

Regardless, thanks a lot in advance!

Good question, let me answer in two ways.

First method is to look at PastaQ.jl which can do projective measurements (see docs here). The heart of the algorithm, after rotations into the appropriate basis is a sample call (see below): PastaQ.jl/src/circuits/getsamples.jl at 2a58ee8296df6ada4ccffd8e80e829d3470a6664 · GTorlai/PastaQ.jl · GitHub

Second answer is why a sample call? With an MPS we could grab each 1RDM by moving the orthogonality center to the site, and forming the RDM (a 2x2 matrix for qubits). The orthogonality conditions make this efficient, and the diagonal of this 1RDM tell us the probabilities of projection (in the current basis) for the site. This essentially gives you Z basis measurements, and for others, say X or Y, you need to rotate into that basis and project. The function for computing measurements of a MPS in its current basis is sample as mentioned above.

1 Like

Hi @ryanlevy , thanks for chipping in!

So I took a look at PastaQ.jl (is this part of ITensor? does it automatically work with ITensor objects?) and indeed found this doc page which looks relevant. However, this is still a bit too abstract for me and I can’t follow it. Is there a basic example where a projective measurement is done? For instance, a 1D Ising chain that at different points in the evolution local projective measurements are performed.

For instance:

“”"
getsamples(M::Union{MPS,MPO}, nshots::Int;
local_basis = [“X”, “Y”, “Z”],
ndistinctbases = nothing)
Perform nshots projective measurements of a wavefunction
|ψ⟩ or density operator ρ. The measurement consists of
a binary vector σ = (σ₁,σ₂,…), drawn from the probabilty
distribution:
- P(σ) = |⟨σ|Û|ψ⟩|² : if M = ψ is MPS
- P(σ) = ⟨σ|Û ρ Û†|σ⟩ : if M = ρ is MPO
For a single measurement, Û is the depth-1
local circuit rotating each qubit, where the rotations are determined by
randomly choosing from the bases specified by local_basis
keyword argument (i.e. if "X" is chosen out of ["X", "Y", "Z"],
the rotation is the eigenbasis of "X").

  function getsamples(M::Union{MPS,MPO}, nshots::Int64;
                      local_basis = ["X", "Y", "Z"],
                      ndistinctbases = nothing,
                      readout_errors = (p1given0 = nothing,
                                        p0given1 = nothing))
    if isnothing(local_basis)
      data = getsamples!(copy(M), nshots; readout_errors = readout_errors)
    else
      bases = randombases(length(M), nshots;
                          local_basis = local_basis,
                          ndistinctbases = ndistinctbases)
      data = getsamples(M, bases; readout_errors = readout_errors)
    end
    return data
  end
  1. Where do I input my local observable O^{(1)} = X \otimes \mathbb{I} \otimes \mathbb{I}\otimes \cdots \otimes \mathbb{I} ?
  2. Does this automatically update my MPS representation of my state |\psi(t)\rangle ?
  3. What is the role of that U ?

I found this local projective measurement code snipet in PastaQ.jl which I will attach here for future reference. Unfortunately, it only does local Z measurements, i.e. \mathbb{I} \otimes Z \otimes \mathbb{I}\otimes \cdots \otimes \mathbb{I}, but it is better than nothing:

# define the two measurement projectors
gate(::GateName"Π0") =
  [1 0
   0 0]
gate(::GateName"Π1") =
  [0 0
   0 1]

# perform a projective measurement in the computational basis
# at a given site
function projective_measurement!(ψ₀::MPS, site::Int)
  ψ = orthogonalize!(ψ₀, site)
  ϕ = ψ[site]
  # 1-qubit reduced density matrix
  ρ = prime(ϕ, tags="Site") * dag(ϕ)
  # Outcome probabilities
  prob = real.(diag(array(ρ)))
  # Sample
  σ = (rand() < prob[1] ? 0 : 1)
  # Projection
  ψ = runcircuit(ψ, ("Π"*"$(σ)", site))
  normalize!(ψ)
  ψ₀[:] = ψ
  return ψ₀
end

I guess I could rotate the state locally first and then measure in the Z basis, i.e. apply Hadamard and then measure Z should be like measuring X although I would expect this to be automatically taken care of in the package?

PastQ.jl works with ITensor objects yes, it’ll be written as a quantum circuit simulator in the language of tensor networks.

The rotation can be done automatically via the getsamples (for example data = getsamples(ψ, [["X" for i=1:N]])), however it doesn’t project the MPS after. I expect you’d need to modify that snippet to do exactly what you want, or write a pure ITensors code whichever is easiest.

1 Like

I would not recommend using PastaQ.jl right now, we haven’t been able to properly maintain it and for the sake of tasks like gate evolution, projective measurements, etc. it doesn’t do anything ITensors.jl/ITensorMPS.jl can’t do (it is just a wrapper around ITensorMPS.jl functionality), and ITensors.jl/ITensorMPS.jl is better supported.

1 Like

Hi @mtfishman thanks for chipping in too!

Could you point me towards how to implement the projective measurements using ITensors.jl alone then?

Hi @acroscarrillo, we don’t have few-site projective measurements of the type you are asked about coded as a kind of black-box function you can call. Currently we only have a function in ITensorMPS called sample that will perform a projective measurement on all of the sites, in the computational basis.

So you will need to write some code, which can be very short, to compute a single-site or few-site projective measurement.

Say your measurement is defined by projectors P_1 and P_2, which for a spin half in the z-basis would be P_1 = P_\uparrow = (Z+1)/2 and P_2 = P_\downarrow = (Z-1)/2.

Here are the two steps you can do:

  1. first, compute the probabilities p_1 and p_2 by taking expected values p_a = \bra{\psi}P_a \ket{\psi_a}. Here you can do this cleverly by taking one expected value just of Z and then using linearity to construct the other one, or equivalently just using p_2 = 1-p_1.
  2. then select which measurement you are going to do. Say you call a weighted random number generator and select outcome number 2.
  3. finally, act with the operator P_2 onto the site you are measuring. You can do this following the code example about applying a single-site operator onto an MPS in our documentation.

That should be all you need to do, and basically all the previous code in PastaQ was doing. To generalize to other bases, you can either work out the projectors manually or apply a unitary onto that site beforehand, then do the same projective measurement in the computational basis just after performing the change of basis.

Of course if that doesn’t quite give you what you need, let us know.

In terms of mixing this with the time evolution, I don’t think we have a facility right now for modifying the state in this way during time evolution, so you will just need to time evolve for the partial time that you want, return the state, modify it with the projective measurements, then call the time evolution code again using your modified state as the initial state for the next portion of time evolution.

2 Likes