Mach-zehnder interferometer simmulation

Hi there,
I am a new learner of strawberry fields and I want to write a code to simulate a mach-zehnder interferometer with two built-in phase shifters. I wrote the code bellow ( discarding program headers ). it has 2 qmodes which are initialized to | 01 > . then I applied two 50:50 BS gates and two beam splitters and then measure output fock states.
I expect by changing the phase value of the phase shifters I can change the probability of output fock states but I have no idea how to do it.

would you please provide me a sample code or help me complete ( and probably correct ) my code for this purpose.
I would be so grateful if you provide me a good programming guide for strawberry fields and continuous variable QC.

Thanks

prog=sf.Program(2)

with prog.context as q:

ops.Fock(0) | q[0]
ops.Fock(1) | q[1]

ops.BSgate(np.pi/4,0) | (q[0],q[1])
ops.Rgate(np.pi) | q[0]
ops.Rgate(np.pi/2) |q[1]
ops.BSgate(np.pi/4,0) | (q[0],q[1])

ops.MeasureFock() | [0 , 1 ]

eng=sf.Engine(“fock”,backend_options={“cutoff_dim” : 5})

Hello Mehdi,
changing the phase shift values in a Mach-Zehnder interferometer without any other modifying operations won’t lead to observable changes in the output Fock state probabilities.
To observe significant changes in the Fock state probabilities, you may need to incorporate operations that directly affect the amplitudes and probabilities of different Fock states. For example, you could use gates like the displacement gate (Dgate), squeezing gate (Sgate), or a combination of different gates.
An example using a Sqeezing gate:

prog = sf.Program(2)

with prog.context as q:

# Initialize the modes to |01>
ops.Fock(0) | q[0]
ops.Fock(1) | q[1]

# Define initial phase shift values
phase_shift_1 = np.pi/4
phase_shift_2 = np.pi/2

# First 50:50 beamsplitter
ops.BSgate(np.pi/4, 0) | (q[0], q[1])

# First phase shifter with the initial value
ops.Rgate(phase_shift_1) | q[0]

# Second phase shifter with the initial value
ops.Rgate(phase_shift_2) | q[1]

# Squeezing gate before the second beamsplitter
ops.Sgate(0.4) | q[0]

# Second 50:50 beamsplitter
ops.BSgate(np.pi/4, 0) | (q[0], q[1])

# Measurement in Fock basis
ops.MeasureFock() | [0, 1]

eng = sf.Engine(“fock”, backend_options={“cutoff_dim”: 5})

result = eng.run(prog)

print(''Output Fock states:", result.samples)

Dear Alex
Thanks for answering my question. I run your sample code but the result were the same regardless of using or not using squeeze gate.
what I want to do is putting two phase shifters in two paths of a Mach-Zhender interferometer and show the probability of fock state |1> at the outputs changes when I change the phase shifter values ( |1> is applied two one input and the other input is |0> )
would you please provide me a sample code with proper output visualization.
Thank you so much in advance

Regards Mehdi

My sample code should output different results if you vary the parameter of the Squeeze gate.
for example at the line: ops.Sgate(0.4) |[q0] , try inputs from 0.1 to 0.9.
I got [[1 0]] for ops.Sgate(0.2) |[q0] , then I got [[0 1]] for ops.Sgate(0.4) |[q0].

Also, I think this is what you meant, try this sample:

import strawberryfields as sf
from strawberryfields import ops
import numpy as np

 # Function to run the program with specified phase shift values

def run_program(phase_shift_1, phase_shift_2):
# Create a Strawberry Fields program with 2 modes
prog = sf.Program(2)

# Start a quantum context
with prog.context as q:

    # Initialize the modes to |01>
    ops.Fock(0) | q[0]
    ops.Fock(1) | q[1]

    # First 50:50 beamsplitter
    ops.BSgate(np.pi/4, 0) | (q[0], q[1])

    # Apply |1> to the first mode
    ops.Fock(1) | q[0]

    # Second phase shifter with the specified value
    ops.Rgate(phase_shift_1) | q[0]

    # Third phase shifter with the specified value
    ops.Rgate(phase_shift_2) | q[1]

    # Second 50:50 beamsplitter
    ops.BSgate(np.pi/4, 0) | (q[0], q[1])

    # Measurement in Fock basis
    ops.MeasureFock() | [0, 1]

# Create the Strawberry Fields engine
eng = sf.Engine("fock", backend_options={"cutoff_dim": 5})

# Run the engine
result = eng.run(prog)

# Return the measurement results
return result.samples

# Initial phase shift values

initial_phase_shift_1 = np.pi/4
initial_phase_shift_2 = np.pi/2

# Run the program with initial phase shift values

initial_result = run_program(initial_phase_shift_1, initial_phase_shift_2)
print(f"Initial Output Fock states: {initial_result}")

# Updated phase shift values

updated_phase_shift_1 = np.pi/6
updated_phase_shift_2 = np.pi/3

# Run the program with updated phase shift values

updated_result = run_program(updated_phase_shift_1, updated_phase_shift_2)
print(f"Updated Output Fock states: {updated_result}")

You can change updated_phase_shift_1 and updated_phase_shift_2 to other values.
I hope it helps, this sample’s output is:

Initial Output Fock states: [[0 1]]
Updated Output Fock states: [[2 0]]

Dear Alex

As you know in a mach-zhender interferometer the difference between photon counts in 2 ports are proportional to cos ( Phi ) where Phi is the phase difference between two paths I want to show this effect in single-photon state using fock-states and the measurement of fock-state probabilities.

Best Regards
Mehdi

import strawberryfields as sf
from strawberryfields import ops
import numpy as np
import matplotlib.pyplot as plt

# Function to run the program with specified phase shift values
def run_program(phase_shift_difference):
    # Create a Strawberry Fields program with 2 modes
    prog = sf.Program(2)

    # Start a quantum context
    with prog.context as q:

        # Initialize the modes to |01>
        ops.Fock(0) | q[0]
        ops.Fock(1) | q[1]

        # First 50:50 beamsplitter
        ops.BSgate(np.pi/4, 0) | (q[0], q[1])

        # Apply |1⟩ to the first mode
        ops.Fock(1) | q[0]

        # Second phase shifter with the specified value
        ops.Rgate(phase_shift_difference / 2) | q[0]

        # Third phase shifter with the specified value
        ops.Rgate(-phase_shift_difference / 2) | q[1]

        # Second 50:50 beamsplitter
        ops.BSgate(np.pi/4, 0) | (q[0], q[1])

        # Measurement in Fock basis
        ops.MeasureFock() | [0, 1]

    # Create the Strawberry Fields engine
    eng = sf.Engine("fock", backend_options={"cutoff_dim": 5})

    # Run the engine
    result = eng.run(prog)

    # Return the measurement results
    return result.samples

# Values for the phase shift difference
phi_values = np.linspace(0, 2*np.pi, 200)

# Collect results for each phase shift difference
probabilities = []
for phi in phi_values:
    samples = run_program(phi)
    prob_1 = np.sum(samples[:, 0] == 1) / len(samples)
    probabilities.append(prob_1)

# Plot the results
plt.plot(phi_values, probabilities, label="Probability of |1⟩")
plt.xlabel("Phase Difference (phi)")
plt.ylabel("Probability")
plt.title("Effect of Phase Difference in a Mach-Zehnder Interferometer")
plt.legend()
plt.show()

1 Like

thank you so much Alex

I run the program and it seems that it works correctly with a simple modification.
after the first beam splitter you set q1 to |1> for the second time. I have no idea what it does. actually I removed this line and I think the result was sensible.

I have also another question. I want to create a Bell state with a beam splitter and squeeze gates can you give me a sample code.

Really thanks for your favour

1 Like

You’re welcome, I’m glad it works for you.
I’m interested if you could post the code with the modification.That is because I also tried myself now, but I don’t get the interference phenomenon of a MZI, so I wish to check more.
For a Bell state:

import strawberryfields as sf
from strawberryfields.ops import *
import numpy as np

# Create a quantum program
prog = sf.Program(2)

# Apply a squeezing gate on the first qubit
with prog.context as q:
    Sgate(0.5) | q[0]

# Apply a squeezing gate on the second qubit
with prog.context as q:
    Sgate(0.5) | q[1]

# Apply a beamsplitter gate between the qubits
with prog.context as q:
    BSgate(np.pi / 4, 0.0) | (q[0], q[1])

# Run the quantum program
eng = sf.Engine("gaussian")
result = eng.run(prog)

# Extract the state vector
state = result.state

# Display the result
print("Bell State:")
print(state.mean_photon(0), state.mean_photon(1))

Dear Alex,
this is my code which is yours with a slight modification.

#!pip install strawberryfields
import strawberryfields as sf
from strawberryfields import ops
import numpy as np
import matplotlib.pyplot as plt

Function to run the program with specified phase shift values

def run_program(phase_shift_difference):
# Create a Strawberry Fields program with 2 modes
prog = sf.Program(2)

# Start a quantum context
with prog.context as q:

    # Initialize the modes to |01>
    ops.Fock(0) | q[0]
    ops.Fock(1) | q[1]

    # First 50:50 beamsplitter
    ops.BSgate(np.pi/4, 0) | (q[0], q[1])

######################################
# Apply |1⟩ to the first mode

ops.Fock(1) | q[0]

######################################
# Second phase shifter with the specified value
ops.Rgate(phase_shift_difference ) | q[0]

    # Third phase shifter with the specified value
    ops.Rgate(np.pi / 2) | q[1]

    # Second 50:50 beamsplitter
    ops.BSgate(np.pi/4, 0) | (q[0], q[1])

    # Measurement in Fock basis
    ops.MeasureFock() | [0, 1]

# Create the Strawberry Fields engine
eng = sf.Engine("fock", backend_options={"cutoff_dim": 5})

# Run the engine
result = eng.run(prog)

# Return the measurement results
return result.samples

Values for the phase shift difference

phi_values = np.linspace(0, 2*np.pi, 300)

Collect results for each phase shift difference

probabilities_0 =
probabilities_1 =

for phi in phi_values:
samples = run_program(phi)
prob_0 = np.sum(samples[:, 0] == 1) / len(samples)
prob_1 = np.sum(samples[:, 1] == 1) / len(samples)

probabilities_0.append(prob_0)
probabilities_1.append(prob_1)

Plot the results

plt.plot(phi_values, probabilities_0, label=" q0 =|1⟩“)
plt.plot(phi_values, probabilities_1, label=” q1= |1⟩")

plt.xlabel(“Phase Difference (phi)”)
plt.ylabel(“Probability”)
plt.title(“Effect of Phase Difference in a Mach-Zehnder Interferometer”)
plt.legend()
plt.show()

I removed the extra phase shift as I said before and fixed the value of phase shift of one path to pi/2 and then swept only the other one. you can see the output is random as we expected. but when the phase shift of the second path gets closed to pi/2 the randomness vanishes and the output becomes 1. the randomness also vanishes when the phase shift becomes pi+pi/2. The output is certain again but this time the probablity is zero as expected for a mach-zhender interferometer. you can see that each time either q0 or q1 is |1>.
you can run the simulation several times and see the random pattern changes but the rule above is always true.
if we increase the number of trials and measure the probability by dividing the number of ones to the total number of trials we expect the cosine behaviour of output probability shows itself.
unfortunately I’m not familiar with python so I can’t do it by myself. I need to show this result through simulation.

you also have sent me the squeeze gate test. thank you so much.
I had a problem when compile it. it needs some packages to be installed. I installed this packages but the problem remains.

lida 0.0.10 requires kaleido, which is not installed.
lida 0.0.10 requires python-multipart, which is not installed.
lida 0.0.10 requires uvicorn, which is not installed.

Thank you so much again for your kindness.I don’t forget your favour and I hope I could pay you back.

Best Regards
Mehdi

you can see when

Looks like I wasn’t notified of your reply.
If you have problems with requirements and want something to work now, run the code on colab or anaconda, with a fresh Strawberry Fields install, this is what I do. Then deal with errors locally later.
You’re welcome!

Dear Alex
I installed strawberryfields on a fresh notebook but the errors remained.

ERROR: pip’s dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
lida 0.0.10 requires fastapi, which is not installed.
lida 0.0.10 requires kaleido, which is not installed.
lida 0.0.10 requires python-multipart, which is not installed.
lida 0.0.10 requires uvicorn, which is not installed.
llmx 0.0.15a0 requires cohere, which is not installed.
llmx 0.0.15a0 requires openai, which is not installed.
llmx 0.0.15a0 requires tiktoken, which is not installed.
tensorflow-probability 0.22.0 requires typing-extensions<4.6.0, but you have typing-extensions 4.9.0 which is incompatible.

Thank you so much and best Regards
Mehdi

I don’t get these errors.
Make sure you run on a google colab notebook.
The first cell to run is:

!pip install strawberryfields --upgrade

then run the sample I provided.

1 Like

I got same error,any other solution? :disappointed:

Hi @alexm3012 , you need to provide more information.
What code did you run? what is the error message and so on.

Welcome to the forum @alexm3012 !

Please share as much information as you can. This video can give you a good sense of the information we need in order to know how to help you.