Closed ajavadia closed 6 years ago
@ajavadia i think the current Clifford vs stabilizer needs some thinking. I think we want to have two. One does the tableu and one does the vector for the stablizer (current). I am also not sure we need to return counts for this unless it is used by the local_qasm_simulator.
For the snapshot in wavefunction I am a little confused. I am assuming it means that we are saving the wavefunction at the points in time when snapshot qr->sr. Does this map the full quantum state to the sr label or can i select a register. If i select a register is it a reduced state?
For projectq maybe we should make it just local_wavefunction_simulator_projectq as the part that turns it into a qasm simulator is the slow part and im not sure that they support if and reset anyway. (@ewinston is this correct). It is really a wavefunction simulator.
Also i think this makes #244 obsolete so we could close that issue.
@jaygambetta ok updated my comment with your suggestions about clifford and stabilizer.
@chriseclectic: does the #save command in you simulator record reduced states, or it has to be for the full qubits? (see jay's comment above)
Probably hard to do without separable states, in which case the syntax should become snapshot sr
Also with the snapshot extensions we should also add it to the unitary. Also what happens if i dont put a snapshot in?
If you use the save
command and the appropriate config option to return the saves states the output from the C++ simulator will return information about the full state vector (across all registers) regardless of which registers the gate is defined to "act" on. This was done to make it compatible with QASM since you can't have a QASM gate definition that doesn't act on a quantum register.
How the snapshot is returned depends on your config settings. Currently it can be returned as
{"b[0] a[1]a[0]": coeff,...}
. Eg for a GHZ state {"0 00": [0.707.., 0], "1 11": [0.707.., 0]}
The clifford simulator currently returns its internal representation of tableaus in the form :
three_qubit_tableau_example=
{
"destabilizers": [
{'X': [1], "Z": [0], "phase": 0},
{'X': [2], "Z": [0], "phase": 0},
{'X': [4], "Z": [0], "phase": 0}
],
"stabilizers": [
{"X": [0], "Z": [1], "phase": 1},
{"X": [0], "Z": [2], "phase": 1},
{"X": [0], "Z": [4], "phase": 1}
]
}
So the stabilizers and destabilizers are each a list of length num_qubits
, and in each are binary vector for the X and Z.
Here is an example for the qiskit simulator:
import qiskit
from qiskit import QuantumProgram
import qiskit.extensions.qiskit_simulator
qp = QuantumProgram()
qr = qp.create_quantum_register('qr', 2);
cr = qp.create_classical_register('cr', 2);
circ = qp.create_circuit('bell', [qr], [cr])
circ.h(qr[0])
circ.cx(qr[0], qr[1])
circ.barrier(qr)
circ.save(1, qr)
circ.barrier(qr)
circ.measure(qr, cr)
backend = 'local_qiskit_simulator'
config = {'data': ['saved_quantum_state',
'saved_density',
'saved_quantum_state_ket',
'saved_probabilities']}
result = qp.execute('bell', shots=10, backend=backend, config=config)
result.get_data('bell')
The output is
{'counts': {'00': 5, '11': 5},
'saved_probabilities': {'1': [0.5, 0.0, 0.0, 0.5]},
'saved_quantum_states': [{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])},
{1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])}],
'saved_quantum_states_ket': [{'1': {'00': [0.707106781186548, 0.0],
'11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}},
{'1': {'00': [0.707106781186548, 0.0], '11': [0.707106781186547, 0.0]}}],
'time_taken': 0.000511}
For the clifford simulator:
backend = 'local_clifford_simulator'
config = {'data': ['saved_quantum_state']}
result = qp.execute('bell', shots=5, backend=backend, config=config)
result.get_data('bell')
returns
{'counts': {'00': 4, '11': 1},
'saved_quantum_states': [{'1': {'destabilizers': [{'X': [0],
'Z': [1],
'phase': 0},
{'X': [2], 'Z': [0], 'phase': 0}],
'stabilizers': [{'X': [3], 'Z': [0], 'phase': 0},
{'X': [0], 'Z': [3], 'phase': 0}]}},
{'1': {'destabilizers': [{'X': [0], 'Z': [1], 'phase': 0},
{'X': [2], 'Z': [0], 'phase': 0}],
'stabilizers': [{'X': [3], 'Z': [0], 'phase': 0},
{'X': [0], 'Z': [3], 'phase': 0}]}},
{'1': {'destabilizers': [{'X': [0], 'Z': [1], 'phase': 0},
{'X': [2], 'Z': [0], 'phase': 0}],
'stabilizers': [{'X': [3], 'Z': [0], 'phase': 0},
{'X': [0], 'Z': [3], 'phase': 0}]}},
{'1': {'destabilizers': [{'X': [0], 'Z': [1], 'phase': 0},
{'X': [2], 'Z': [0], 'phase': 0}],
'stabilizers': [{'X': [3], 'Z': [0], 'phase': 0},
{'X': [0], 'Z': [3], 'phase': 0}]}},
{'1': {'destabilizers': [{'X': [0], 'Z': [1], 'phase': 0},
{'X': [2], 'Z': [0], 'phase': 0}],
'stabilizers': [{'X': [3], 'Z': [0], 'phase': 0},
{'X': [0], 'Z': [3], 'phase': 0}]}}],
'time_taken': 0.000404}
@jaygambetta I believe that is true about the projectq simulator.
I think values (like in the saved_quantum_states
) should be strings. There is no difference and we will be allowed to use symbols, such as pi
.
PS: complex number are not supported in JSON. Another reason to make them strings?
Summary:
This issue is regarding the way we expose simulator backends to the user. From a user's perspective, it would make sense to group these simulators by functionality. This is separate from how they are actually implemented "under the hood". Functionally, we have the following simulators:
qasm_simulator: This is to mimic how things are done in a real experiment.
measure qr->cr
) as the result will only be a bit-string, similar to experiment results. No measurement means you just read out 0s.reset
andif
.shots
. At the end, the returnedresult
will be a histogram of the classical register for that many shots.wavefunction_simulator: This is for obtaining wavefunction from qubit registers.
snapshot qr->sr
command, whereqr
is the register to be queried andsr
is a (to-be-implemented)snapshot_register
(or wavefunction_register).measure
,if
andshots
.snapshot_register
.unitary_simulator: for getting the
2^n x 2^n
unitary matrix equivalent of the circuit.measure
,if
andshots
are disabled.stabilizer_simulator: for efficient simulation of stabilizer circuits, according to Gottesman's paper.
snapshot qr->sr
will record the 2n+1 X 2n dimensional matrix of a clifford tableau.tgate_simulator: for fast simulation of circuits dominated by Clifford gates with relatively few T gates, according to Bravyi and Gosset's paper
Expected Behavior
Implementation optimizations:
a. There are currently 3 implementations for qasm simulator: python, c++ and an external one by projectq. We should make the c++ one the standard
local_qasm_simulator
, and call the otherslocal_qasm_simulator_py
,local_qasm_simulator_projectq
. There isn't really any reason for the latter 2 to be exposed throughget_available_backends()
, but someone who knows they absolutely want to use one of them can know they exist through documentation.b. For the
local_qasm_simulator
, if there are no intermediatemeasures
, then the compiler should be smart and not calllocal_qasm_simulator
many times. Instead, it should call thelocal_wavefunction_simulator
, then sample the output wavefunction to produce thecounts
histogram. Similarly, if there is an intermediate measure but that qubit is not modified later (it can be a control), then the measurement should be commuted to the end and the whole simulation, again, only done once.c. For the
local_qasm_simulator
, if the circuit is entirely made up of CNOT, Hadamard, Phase, the compiler should be smart and call thelocal_stabalizer_simulator
which is much more scalable. Similarly for a Clifford-dominated circuit with some Ts, it should call thelocal_tgate_simulator
.Current Behavior
local_qiskit_simulator
(as opposed tolocal_qasm_simulator
) has to go.shots=1
has to go.local_clifford_simulator
should be calledlocal_stabilizer_simulator
. Alocal_clifford_simulator
will be added later.Context
This is a summary of some discussions with @jaygambetta, @awcross1, @levbishop. Everyone, please comment if you have any thoughts, and feel free to correct any mistakes by editing my comment.