Skip to content

ioanamoflic/pandora

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

465 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Logo

Pandora: Ultra-Large-Scale Quantum Circuit Design Automation

About

Pandora is an open-source tool for compiling, analyzing and optimizing quantum circuits through template rewrite rules. The tool can easily handle quantum circuits with billions of gates, and can operate in a multi-threaded manner offering almost linear speed-ups. Pandora can apply thousands of complex circuit rewrites per second at random circuit locations.

Pandora is HPC friendly and can be used for:

  • Faster and more insightful analysis of quantum circuits
  • Faster compilation for practical, fault-tolerant QC
  • Faster and multi-threaded optimization for practical, fault-tolerant QC
  • Fast widgetisation. A widget is a partition of the circuit which obeys some architectural constraints.

Pandora can take input circuits from / export to:

Apptainer Setup

Installation instructions can be found in the README.md of the apptainer folder. In a nutshell, you need to have Apptainer installed on your computer, a Docker image of Postgres available locally, and then to follow the steps from the README. At the end you will see a pandora.sif file in the apptainer\images folder. This is the Apptainer image that will be used by run_apptainer.sh (see following section).

With Apptainer

For running on HPC hardware, this is highly encouraged. Apptainer does not require sudo rights and is also light-weight and open-source. Multiple Pandora containers can be started in parallel, each with its own .json config file (make sure that the port is different).

  • A PostgreSQL config file example is default_config.json.
  • The database storage location can be configured in run_apptainer.sh. As a rule of thumb, each billion of Clifford+T gates in the Pandora format takes about 100GB of storage.
  • A command example for starting the container and decomposing an 64-bit RSA instance (nproc = 1 & container id = 0) is
bash run_apptainer.sh main.py default_config.json rsa 64 1 0

Note that for all benchmarks that do not need postgres, one can use run_apptainer_no_postgres.sh.

Without Apptainer

  • Install PostgreSQL and get a server running. For example, on MacOS you can use this tutorial.
  • A PostgreSQL config file example is default_config.json.
  • python3.10 main.py rsa 64 1 0 for building and decomposing an 64-bit RSA instance into Pandora.

Widgetization

This is an example of a widgetised Fermi-Hubbard instance (N=2) decomposed into Clifford+T with around 58K gates. Each frame is a visualisation of the widgets with d3 (each node is a gate, the color identifies the widget) for different parameters.

fh2.gif

This is an example of a widgetised 2-bit adder.

Benchmarks

For details and more results, see the Pandora manuscript https://arxiv.org/abs/2508.05608.

Single-threaded performance

  • Pandora vs. TKET or Qiskit for rewriting a specific gate pattern, when the pattern is encountered in the circuit with a certain probability (0.1%, 1% and 10%); pandora_res.png
  • the Qiskit results can be replicated running python3.10 /benchmarking/benchmark_qiskit.py 1
  • the TKET results can be replicated running python3.10 /benchmarking/benchmark_tket.py
  • the Pandora results can be replicated running python3.10 /benchmarking/benchmark_pandora.py default_config.json 1

Equivalence checking

pandora_res_2.png

  • the Pandora results can be replicated running (0 for equivalence and 1 for non-equivalence) python3.10 /benchmarking/benchmark_mqt.py default_config.json 0 pandora
  • the MQT results can be replicated running (0 for equivalence and 1 for non-equivalence) python3.10 /benchmarking/benchmark_mqt.py default_config.json 0 mqt

Issues

  • There is a compatibility issue between Qualtran and pyLIQTR. In order to decompose RSA, we require the latest version of Qualtran (0.6), which is incompatible with pyLIQTR (on which we rely for Fermi-Hubbard circuit decompositions). Therefore, in order to run the Fermi-Hubbard decomposition, requirements.txt needs:
    qualtran==0.4
    pyLIQTR==1.3.3
    

Citing Pandora

Please use

@article{moflic2025ultra,
  title={Ultra-Large-Scale Compilation and Manipulation of Quantum Circuits with Pandora},
  author={Moflic, Ioana and Paler, Alexandru},
  journal={arXiv preprint arXiv:2508.05608},
  year={2025}
}

Acknowledgements

This research was performed in part with funding from the Defense Advanced Research Projects Agency [under the Quantum Benchmarking (QB) program under award no. HR00112230006 and HR001121S0026 contracts].

SQL code example explanation

create or replace procedure cancel_single_qubit(type_1 int, type_2 int, param_1 float, param_2 float, my_proc_id int, nprocs int, pass_count int, timeout int)
    language plpgsql
as
$$
    --     We are performing this rewrite:
    --
    --     First (type_1, param_2) ---- Second (type_2, param_2) ----  =  ------------

    --     LinkID (edge in the circuit DAG) has the format *IPTT where:
    --     - unlimited number of digits for the gate id I
    --     - one digit for the port P. For example, a CNOT gate has 2 ports, a Toffoli has 3 ports etc.
    --     - two digits for the gate type T. For example, a Toffoli is 23, a CNOT is 15/18 etc.
    --
    --     Considering the LinkID X, in order to:
    --     - get the gate id: X / 1000 will return the *I digits
    --     - get the port number: (X / 100) % 10 will return the P digit
    --     - get the type: X % 100 will return the T digits
declare
    -- helper variables
    first_prev_id bigint;
    second_next_id bigint;

    gate record;
    first record;
    second record;

    a record;
    b record;

	start_time timestamp;

begin
    start_time := CLOCK_TIMESTAMP();

     -- pass_count is usually set for benchmarking purposes (we know exactly how many templates we should
     -- rewrite), otherwise it is set to a very large number
     -- we should never loop infinitely as we have a timeout set (see below)

	 while pass_count > 0 loop
	    -- loop through all candidate gates that currently fit the pattern we are looking for:
	    -- first gate (L) with type = type_1 and parameter = param_1 having a neighbouring second gate (R)
        -- which also has the type type_2 (see *IPTT format)
        for gate in
            select * from linked_circuit
                     where
                       id % nprocs = my_proc_id
                       and type = type_1
                       and param = param_1
                       and mod(next_q1, 100) = type_2
        loop
            -- attempt to lock the two gates
            -- if not already locked by another process (skip locked), lock the pair of gates (for update)
            select * into first from linked_circuit where id = gate.id for update skip locked;
            select * into second from linked_circuit where id = div(first.next_q1, 1000) for update skip locked;

            -- if acquiring locks is not successful (e.g. gates deleted already by another process),
            -- commit and move to the next candidate pair
            -- locks are released at commit!
            if first.id is null or second.id is null then
                commit;
                continue;
            end if;

            -- note that we never actually checked whether the second gate has the correct parameter
            -- if not, commit and move to the next candidate pair
            if second.param != param_2 or second.type != type_2 then
                commit;
                continue;
            end if;

            -- compute the ids of the two gates (see *IPTT format)
            first_prev_id := div(first.prev_q1, 1000);
            second_next_id := div(second.next_q1, 1000);

            -- attempt to lock the neighbours of the pair (left of first gate and right of second gate)
            -- this is needed as we have to update the LinkIDs and we need to lock the neighbours pointing to
            -- the pair as well
            select * into a from linked_circuit where id = first_prev_id for update skip locked;
            select * into b from linked_circuit where id = second_next_id for update skip locked;

            -- if locking the neighbours failed, commit and move to the next candidate pair
            if a.id is null or b.id is null then
                commit;
                continue;
            end if;

            -- if we made it this far, it means we have managed to acquire all the necessary locks
            -- before deleting the single-qubit gate pair, we make sure to update the links of the neighbours

            -- we link the left neighbour of the first gate to the right neighbour of the second gate (see *IPTT format)
            if mod(div(first.prev_q1, 100), 10) = 0 then
                update linked_circuit set next_q1 = second.next_q1 where id = first_prev_id;
            else
                update linked_circuit set next_q2 = second.next_q1 where id = first_prev_id;
            end if;

            -- we link the right neighbour of the second gate to the left neighbour of the first gate (see *IPTT format)
            if mod(div(second.next_q1, 100), 10) = 0 then
                update linked_circuit set prev_q1 = first.prev_q1 where id = second_next_id;
            else
                update linked_circuit set prev_q2 = first.prev_q1 where id = second_next_id;
            end if;

            -- neighbouring links are updated, the only thing left to do is to delete the rows in the database
            delete from linked_circuit where id in (first.id, second.id);

            commit; -- release the locks

        end loop; -- end gate loop

        -- we finished a circuit pass
	    pass_count = pass_count - 1;

        -- if we exceeded the allotted time, return to avoid looping infinitely
	    if extract(epoch from (clock_timestamp() - start_time)) > timeout then
            exit;
        end if;

    end loop; -- end pass loop
end;$$;

About

Ultra-Large-Scale Quantum Circuit Design Automation

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors