Add Clifford simulator implementation
Describe the feature you'd like Stabilizer circuits can be classically simulated efficiently, and are very useful in quantum information research, for example in quantum error correction. To support these use cases, it would be good to have a Clifford simulator target for the local simulator:
sim = LocalSimulator("braket.clifford")
This should be done by implementing the OpenQASMSimulator interface to translated OpenQASM into simulator instructions.
How would this feature be used? Please describe. Fast simulation of large stabilizer circuits, for example with thousands of qubits and gates.
Hi @rmshaffer made a PR to clear the issue
Hi @speller26 and @rmshaffer I Have to give up on this issue unfortunately it was great working on this but I will be active for contributions later on
Hi, Do you expect a full simulator, or a wrapper over an existing simulator, such as stim?
Either is fine; a wrapper is much more doable, but we're not ruling out a high-performance custom implementation, which would be incredibly impressive!
Hello, Am I correct in understanding that OpenQASMSimulator implementations do not support modifying existing circuits? Additionally, AbstractProgramContexts require appropriate handling of custom unitaries. While this is convenient for a general simulator, a clifford simulator would have to verify that the provided unitary can be implemented by only clifford gates and also find a corresponding clifford decomposition. This seems beyond the scope of this issue, so would it be acceptable to simply raise an exception if that function is ever called?
Not all gates have to be supported; as well, the simulator implementation does not need to decompose gates. So you are correct, it is enough to raise an exception if a gate is not explicitly supported.
My understanding of the program flow is that an OpenQASMSimulator.run call creates a ProgramContext, calls OpenQASMSimulator.parse_program to create an interpreter which then modifies the ProgramContext by parsing the QASM instructions into whatever representation the ProgramContext specifies (typically braket, but in my case stim), then takes the circuit property of this modified ProgramContext and extracts information about the circuit such as the qubits used, the qubits that need to be measured, and the actual instructions in the circuit, and sends all of this to a Simulation object to evolve the state and get results, which are then returned in the form of a GateModelTaskResult object.
Rather than making a CliffordSimulation class, I believe the best course of action is to create a braket.Circuit object and have CliffordSimulator.run build an appropriate stim.Circuit according to the instructions in the braket.Circuit class, so stim.Circuit would then occupy the same role that Simulation does. The issue with this approach is that braket.Circuit objects store GateOperations, which stim instructions clearly are not. I would like to write my own CliffordCircuit class to store stim operations, but the lack of an abstract Circuit class makes it a bit more difficult to tell what exactly the desired interface is (for example, should circuit.basis_rotation_instructions be implemented?). What exactly would such a CliffordCircuit class need to implement? Or could I just write it as a class exclusively used by CliffordProgramContexts which only stores basic data about the circuit and the list of instructions? I also have some questions about some of the work done in the BaseLocalSimulator.run_openqasm method:
- Much of run_openqasm seems to be focused on validating properties extracted from circuit object returned by the ProgramContext. What criteria exactly are these methods validating against?
- I am unsure as to the purpose of the qubit_map and all the associated lines such as mapping the measured qubits.
- What exactly are the results objects? As far as I can tell the Results referenced in all the simulator code comes from jaqcd (looking here), but this implies that any results taken from a stim circuit would need to be mapped back to one of these objects, and I am unsure exactly where we would want that to take place (should it be done in CliffordProgramContext.add_result? Or in CliffordCircuit.add_result?).
I'd like to work on this issue, feel free to assign it to me.