Skip to content
This repository was archived by the owner on Oct 7, 2019. It is now read-only.

Stabilizer simulator#17

Merged
ncrubin merged 11 commits into
rigetti:masterfrom
ncrubin:stabilizer_simulator
Aug 9, 2018
Merged

Stabilizer simulator#17
ncrubin merged 11 commits into
rigetti:masterfrom
ncrubin:stabilizer_simulator

Conversation

@ncrubin
Copy link
Copy Markdown
Contributor

@ncrubin ncrubin commented Aug 2, 2018

Implements the "Improved Simulation of Stabilizer Circuits" from Aaronson and Gottesman. This qvm also contains nice functions for projecting out states starting from stabilizers and returning the density matrix starting from stabilizers.

ncrubin added 7 commits July 17, 2018 15:55
Seems like we have measurement in correctly now!
Stabilizer simulator can now return the state (which has been tested)
and the density matrix (which has not been tested)

More tests need to be generated to validate that the stabilizer
simulation actually works.
Error was on how we were pulling the qubits from the instruction.  NOTE:
Do not use get_qubits() as it always return qubit indices in numerical
order.
returns wavefunction and density matrix.
@ncrubin ncrubin requested a review from mpharrigan August 2, 2018 17:57
Comment thread referenceqvm/api.py Outdated
qvm = QVM_Density(gate_set=gate_set, noise_model=noise_model)

elif type_trans == 'stabilizer':
qvm = QVM_Stabilizer(gate_set=stabilizer_gate_set)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing imports

Comment thread referenceqvm/unitary_generator.py Outdated
return param_obj.address
elif isinstance(param_obj, Slot):
return param_obj.value()
# elif isinstance(param_obj, Slot):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?


S T A B I L I Z E R

S T A T E S
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol

Comment thread referenceqvm/qam.py
if invalid is True and self.all_inst is False:
raise TypeError("In QVM_Unitary, only Gates and DefGates are "
"supported")
raise TypeError("Some gates used are not allowed in this QAM")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will defgates let the user sneak through invalid programs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but that was always a risk of defgates that was weighed in the original quil spec. If users want to use defgate they should know that it takes them outside of the QAM model. Remember QAM requires gate specification in the definition. If the user wants to add something to this list then we are trusting them to know what they're doing.

if not isinstance(pyquil_program, Program):
raise TypeError("I can only generate from pyQuil programs")

if self.all_inst is None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems out of place

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a better way to do this in terms of ABC's but that will be a future upgrade. I will add to the issue list.

else:
self.tableau = self._n_qubit_tableau(num_qubits)

def load_program(self, pyquil_program):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole function is redundant with the superclass

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I make a note that at some point each subclass or QAM should make its own load_program. The reason for this is that if load_program needs to check particular gate sets (like matchgates only...or something like that) each new subclass has to provide this functionality. In the future I think I'd like to remove load_program from QAM.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then can you remove the obviously-not-appropriate-for-stabalizer-qvm parts of this implementation

Comment thread referenceqvm/qvm_stabilizer.py Outdated
defined_gates[dg.name] = dg.matrix
self.defgate_set = defined_gates

# if QVM_Unitary, check if all instructions are valid.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's probably not QVM_Unitary at this point

Slots are no longer in pyquil
# return HALTED (i.e. program_counter is end of program)
return self.program_counter == len(self.program)

def pre(self):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why bother with these?

Copy link
Copy Markdown
Contributor Author

@ncrubin ncrubin Aug 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asymmetric Pauli channel model still works in stabilizer simulation. preparing for the future


elif isinstance(instruction, Jump):
# unconditional Jump; go directly to Label
self.program_counter = self.find_label(instruction.target)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.find_label doesn't exist

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to QAM superclass from wavefunction. Fixes the same problem in density_qvm that slipped by!!!

self._apply_cnot(instruction)
elif instruction.name == 'I':
pass
else:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about X, Y, Z?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future additions! Right now this is just CHP simulator

# set up stabilizers
self.tableau = self._n_qubit_tableau(self.num_qubits)
self.kernel()
results.append(list(map(int, self.classical_memory[classical_addresses])))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[int(b) for b in self.classical_memory[classical_addresses]]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment thread referenceqvm/qvm_stabilizer.py Outdated
self.tableau = self._n_qubit_tableau(self.num_qubits)
self.kernel()
stabilizers = binary_stabilizer_to_pauli_stabilizer(self.stabilizer_tableau())
pauli_ops = list(map(lambda x: 0.5 * (sI(0) + x), stabilizers))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write as list comprehension

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment thread referenceqvm/qvm_stabilizer.py Outdated
self.kernel()
stabilizers = binary_stabilizer_to_pauli_stabilizer(self.stabilizer_tableau())
stabilizer_state = project_stabilized_state(stabilizers)
stabilizer_state = np.array(stabilizer_state.todense()) # todense() returns a matrixlib type
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stabalizer_state.toarray() does this in one fell swoop

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment thread referenceqvm/qvm_stabilizer.py Outdated
"""
phase_accumulator = 0
for j in range(self.num_qubits):
# self._g_update(x_{hj}, z_{hj}, x_{ij}, z_{ij})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment doesn't match the actual indices?

Comment thread referenceqvm/qvm_stabilizer.py Outdated
:param t: target qubit index
:return: 0/1 phase update for r_{i}
"""
return self.tableau[i, -1] ^ (self.tableau[i, c] * self.tableau[i, t + self.num_qubits]) * (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line is wrapped weirdly

# We need to check if R(i) anticommutes with Za...which it does if x_{ia} = 1
if self.tableau[ii, t_qbit] == 1: # referencing the destabilizers

# TODO: Remove this for performance?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you want to remove this?

Comment thread referenceqvm/stabilizer_utils.py Outdated
rindices, cindices = state.nonzero()
for ridx, cidx in zip(rindices, cindices):
# this is so gross looking
bitstring = list(map(int, np.binary_repr(ridx, width=num_qubits)))[::-1]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list comprehension!

# this is so gross looking
bitstring = list(map(int, np.binary_repr(ridx, width=num_qubits)))[::-1]
new_ket, new_coefficient = compute_action(bitstring, pauli_operator, num_qubits)
new_indices.append(int("".join([str(x) for x in new_ket[::-1]]), 2))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like an odd way of going from bits to an integer. maybe make a function to make it clear what you're doing here. You can avoid converting to string if you do something like

places = 2**np.arange(n)
num = np.sum(places * bits)

Comment thread referenceqvm/stabilizer_utils.py Outdated
state += state_family_generator(state, generator)
state /= 2

normalization = (state.conj().T.dot(state)).todense()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.toarray()

if vector1.shape != vector2.shape:
raise ValueError("vectors must be the same size.")

# TODO: add a check for binary or integer linear arrays
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to do this?


# TODO: add a check for binary or integer linear arrays

hadamard_product = np.multiply(vector1, vector2)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not vector1 * vector2

Clean up and list comps
@ncrubin ncrubin merged commit 8465e57 into rigetti:master Aug 9, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants