Beispiel #1
0
 def _append_zeros(state, qubits: List[int], results: List[int]):
     for q, r in zip(qubits, results):
         state = K.expand_dims(state, axis=q)
         if r:
             state = K.concatenate([K.zeros_like(state), state], axis=q)
         else:
             state = K.concatenate([state, K.zeros_like(state)], axis=q)
     return state
Beispiel #2
0
 def control_unitary(unitary):
     shape = tuple(unitary.shape)
     if shape != (2, 2):
         raise_error(ValueError, "Cannot use ``control_unitary`` method for "
                                 "input matrix of shape {}.".format(shape))
     zeros = K.zeros((2, 2), dtype='DTYPECPX')
     part1 = K.concatenate([K.eye(2, dtype='DTYPECPX'), zeros], axis=0)
     part2 = K.concatenate([zeros, unitary], axis=0)
     return K.concatenate([part1, part2], axis=1)
Beispiel #3
0
 def cache(self):
     if self._cache is None:
         cache = self.GateCache()
         qubits = set(self.target_qubits)
         # Create |00...0><00...0| for qubits that are traced out
         n = len(self.target_qubits)
         row0 = K.cast([1] + (2**n - 1) * [0], dtype='DTYPECPX')
         shape = K.cast((2**n - 1, 2**n), dtype='DTYPEINT')
         rows = K.zeros(shape, dtype='DTYPECPX')
         cache.zero_matrix = K.concatenate([row0[K.newaxis], rows], axis=0)
         cache.zero_matrix = K.reshape(cache.zero_matrix, 2 * n * (2, ))
         # Calculate initial transpose order
         order = tuple(sorted(self.target_qubits))
         order += tuple(i for i in range(self.nqubits) if i not in qubits)
         order += tuple(i + self.nqubits for i in order)
         cache.einsum_order = order
         # Calculate final transpose order
         order1 = tuple(i for i in range(self.nqubits) if i not in qubits)
         order2 = tuple(self.target_qubits)
         order = (order1 + tuple(i + self.nqubits for i in order1) +
                  order2 + tuple(i + self.nqubits for i in order2))
         cache.final_order = tuple(
             order.index(i) for i in range(2 * self.nqubits))
         # Shapes
         cache.einsum_shape = K.cast(2 * (2**n, 2**(self.nqubits - n)),
                                     dtype='DTYPEINT')
         cache.output_shape = K.cast(2 * (2**self.nqubits, ),
                                     dtype='DTYPEINT')
         cache.reduced_shape = K.cast(2 * (2**(self.nqubits - n), ),
                                      dtype='DTYPEINT')
         self._cache = cache
     return self._cache
Beispiel #4
0
def test_apply_fsim(nqubits, targets, controls, compile, einsum_str):
    """Check ``K.op.apply_twoqubit_gate`` for random gates."""
    state = random_complex((2**nqubits, ))
    rotation = random_complex((2, 2))
    phase = random_complex((1, ))

    target_state = state.numpy().reshape(nqubits * (2, ))
    gatenp = np.eye(4, dtype=target_state.dtype)
    gatenp[1:3, 1:3] = rotation.numpy()
    gatenp[3, 3] = phase.numpy()[0]
    gatenp = gatenp.reshape(4 * (2, ))

    slicer = nqubits * [slice(None)]
    for c in controls:
        slicer[c] = 1
    slicer = tuple(slicer)
    target_state[slicer] = np.einsum(einsum_str, target_state[slicer], gatenp)
    target_state = target_state.ravel()

    gate = K.concatenate([K.reshape(rotation, (4, )), phase], axis=0)

    def apply_operator(state):
        qubits = qubits_tensor(nqubits, targets, controls)
        return K.op.apply_fsim(state, gate, qubits, nqubits, *targets,
                               get_threads())

    if compile:
        apply_operator = K.compile(apply_operator)

    state = apply_operator(state)
    np.testing.assert_allclose(target_state, state.numpy())
Beispiel #5
0
    def tensor(self):
        """Returns the full state vector as a tensor of shape ``(2 ** nqubits,)``.

        This is done by merging the state pieces to a single tensor.
        Using this method will double memory usage.
        """
        if self.qubits.list == list(range(self.nglobal)):
            with K.device(self.device):
                state = K.concatenate([x[K.newaxis] for x in self.pieces], axis=0)
                state = K.reshape(state, self.shapes["full"])
        elif self.qubits.list == list(range(self.nlocal, self.nqubits)):
            with K.device(self.device):
                state = K.concatenate([x[:, K.newaxis] for x in self.pieces], axis=1)
                state = K.reshape(state, self.shapes["full"])
        else: # fall back to the transpose op
            with K.device(self.device):
                state = K.zeros(self.shapes["full"])
                state = K.transpose_state(self.pieces, state, self.nqubits,
                                          self.qubits.reverse_transpose_order)
        return state
Beispiel #6
0
    def density_matrix_call(self, state):
        state = K.reshape(state, self.tensor_shape)
        if self.is_controlled_by:
            ncontrol = len(self.control_qubits)
            nactive = self.nqubits - ncontrol
            n = 2**ncontrol
            state = K.transpose(state, self.control_cache.order(True))
            state = K.reshape(state, 2 * (n, ) + 2 * nactive * (2, ))
            state01 = K.gather(state, indices=range(n - 1), axis=0)
            state01 = K.squeeze(K.gather(state01, indices=[n - 1], axis=1),
                                axis=1)
            state01 = self.einsum(self.calculation_cache.right0, state01,
                                  K.conj(self.matrix))
            state10 = K.gather(state, indices=range(n - 1), axis=1)
            state10 = K.squeeze(K.gather(state10, indices=[n - 1], axis=0),
                                axis=0)
            state10 = self.einsum(self.calculation_cache.left0, state10,
                                  self.matrix)

            state11 = K.squeeze(K.gather(state, indices=[n - 1], axis=0),
                                axis=0)
            state11 = K.squeeze(K.gather(state11, indices=[n - 1], axis=0),
                                axis=0)
            state11 = self.einsum(self.calculation_cache.right, state11,
                                  K.conj(self.matrix))
            state11 = self.einsum(self.calculation_cache.left, state11,
                                  self.matrix)

            state00 = K.gather(state, indices=range(n - 1), axis=0)
            state00 = K.gather(state00, indices=range(n - 1), axis=1)
            state01 = K.concatenate([state00, state01[:, K.newaxis]], axis=1)
            state10 = K.concatenate([state10, state11[K.newaxis]], axis=0)
            state = K.concatenate([state01, state10[K.newaxis]], axis=0)
            state = K.reshape(state, 2 * self.nqubits * (2, ))
            state = K.transpose(state, self.control_cache.reverse(True))
        else:
            state = self.einsum(self.calculation_cache.right, state,
                                K.conj(self.matrix))
            state = self.einsum(self.calculation_cache.left, state,
                                self.matrix)
        return K.reshape(state, self.flat_shape)
Beispiel #7
0
    def add_shot(self, probabilities=None):
        """Adds a measurement shot to an existing measurement symbol.

        Useful for sampling more than one shots with collapse measurement gates.
        """
        if self.nshots:
            if probabilities is not None:
                self.probabilities = probabilities
            self.nshots += 1
            # sample new shot
            new_shot = K.cpu_fallback(K.sample_shots, self.probabilities, 1)
            self._decimal = K.concatenate([self.decimal, new_shot], axis=0)
            self._binary = None
        else:
            if probabilities is None:
                raise_error(ValueError, "Cannot add shots in measurement that "
                                        "for which the probability distribution "
                                        "is not specified.")
            self.set_probabilities(probabilities)
Beispiel #8
0
 def state_vector_call(self, state):
     state = K.reshape(state, self.tensor_shape)
     if self.is_controlled_by:
         ncontrol = len(self.control_qubits)
         nactive = self.nqubits - ncontrol
         state = K.transpose(state, self.control_cache.order(False))
         # Apply `einsum` only to the part of the state where all controls
         # are active. This should be `state[-1]`
         state = K.reshape(state, (2**ncontrol, ) + nactive * (2, ))
         updates = self.einsum(self.calculation_cache.vector, state[-1],
                               self.matrix)
         # Concatenate the updated part of the state `updates` with the
         # part of of the state that remained unaffected `state[:-1]`.
         state = K.concatenate([state[:-1], updates[K.newaxis]], axis=0)
         state = K.reshape(state, self.nqubits * (2, ))
         # Put qubit indices back to their proper places
         state = K.transpose(state, self.control_cache.reverse(False))
     else:
         einsum_str = self.calculation_cache.vector
         state = self.einsum(einsum_str, state, self.matrix)
     return K.reshape(state, self.flat_shape)
Beispiel #9
0
 def construct_unitary(self):
     t = K.cast(self.parameters)
     phase = K.exp(1j * t / 2.0)[K.newaxis]
     diag = K.concatenate([K.conj(phase), phase], axis=0)
     return K.diag(diag)