Challenge 4: Inversion

Author

Le magicien quantique

Published

May 12, 2024

import perceval as pcvl
from perceval import BS, catalog, PERM, Circuit, Processor, pdisplay, PS, BasicState
from perceval.rendering.circuit import SymbSkin, DisplayConfig
from exqalibur import FockState

import numpy as np
from numpy import pi
from typing import Optional, Dict, List, Tuple 

DisplayConfig.select_skin(SymbSkin)

1 Before we begin…

qubits = {
    "00": BasicState([1, 0, 1, 0]),
}

def measure2p(processor: Processor, input_state: Optional[FockState] = None) -> None:
    if input_state is None:
        input_state = qubits["00"]

    # We enforce the rule: the sum of photons per pair of rails must be equal to 1.
    processor.set_postselection(pcvl.utils.PostSelect("[0,1]==1 & [2,3]==1"))
    processor.min_detected_photons_filter(0)

    # Finally, we take the measurement:
    processor.with_input(input_state)
    measure2p_s = pcvl.algorithm.Sampler(processor)

    print(f"Input: {qubits_[input_state]}")
    for k, v in measure2p_s.probs()["results"].items():
        print(f"> {qubits_[k]}: {round(v, 2)}")

H = BS.H()
RX = BS.Rx
RY = BS.Ry
CNOT = catalog["klm cnot"].build_processor()
NOT = PERM([1, 0])
HP = Circuit(2, "HP") // H // (1, PS(-pi/2))
q = lambda x: [2*x, 2*x+1]
theta = pi/3
gamma = pi/5

2 The final scene

It’s time to finish this dreadful series of challenges. This time, no new concepts. Just two circuits and one goal: add the two missing parts to get the expected results. Good luck… may the odds be in your favor…

step_one = Circuit(2, "S1") 
p_step_one = Processor("SLOS", 4)
p_step_one.add(q(0), H)
p_step_one.add(q(1), step_one)
p_step_one.add(q(1), RX(-gamma))
p_step_one.add(q(0) + q(1), CNOT)
p_step_one.add([2], PS(theta))
p_step_one.add(q(0) + q(1), CNOT)
p_step_one.add(q(0), H)
p_step_one.add(q(0), RX(theta))
pdisplay(p_step_one)
print("Expected result: {|1,0,0,1>: 1.0}")
measure2p(p_step_one, input_state=qubits["00"])
step_two = Circuit(2, "S2")
p_step_two = Processor("SLOS", 4)
p_step_two.add(q(0), H)
p_step_two.add(q(1), HP)
p_step_two.add(q(1), RY(theta))
p_step_two.add(q(0) + q(1), CNOT)
p_step_two.add(q(1), RY(-theta))
p_step_two.add(q(0), H)
p_step_two.add(q(1) + q(0), CNOT)
p_step_two.add(q(1), step_two)
pdisplay(p_step_two)
print("Expected result: {|1,0,0,1>: 0.93, |0,1,0,1>: 0.07})")
measure2p(p_step_two, input_state=qubits["00"])

3 Flag recovery

import requests as rq

def circuit_to_list(circuit: Circuit) -> List[List[Tuple[float, float]]]:
    return [[(x.real, x.imag) for x in l] for l in np.array(circuit.compute_unitary())]


d = {
    "step_one": circuit_to_list(step_one),
    "step_two": circuit_to_list(step_two),
}

URL = ...
# URL = "https://perceval.challenges.404ctf.fr"
rq.get(URL + "/healthcheck").json()
rq.post(URL + "/challenges/4", json=d).json()