コード例 #1
0
def random_rotations_between_grid_interaction_layers_circuit(
    qubits: Iterable['cirq.GridQubit'],
    depth: int,
    *,  # forces keyword arguments
    two_qubit_op_factory: Callable[
        ['cirq.GridQubit', 'cirq.GridQubit', 'np.random.RandomState'], 'cirq.OP_TREE'
    ] = lambda a, b, _: ops.CZPowGate()(a, b),
    pattern: Sequence[GridInteractionLayer] = GRID_STAGGERED_PATTERN,
    single_qubit_gates: Sequence['cirq.Gate'] = (
        ops.X ** 0.5,
        ops.Y ** 0.5,
        ops.PhasedXPowGate(phase_exponent=0.25, exponent=0.5),
    ),
    add_final_single_qubit_layer: bool = True,
    seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
) -> 'cirq.Circuit':
    """Generate a random quantum circuit of a particular form.

    This construction is based on the circuits used in the paper
    https://www.nature.com/articles/s41586-019-1666-5.

    The generated circuit consists of a number of "cycles", this number being
    specified by `depth`. Each cycle is actually composed of two sub-layers:
    a layer of single-qubit gates followed by a layer of two-qubit gates,
    controlled by their respective arguments, see below. The pairs of qubits
    in a given entangling layer is controlled by the `pattern` argument,
    see below.

    Args:
        qubits: The qubits to use.
        depth: The number of cycles.
        two_qubit_op_factory: A callable that returns a two-qubit operation.
            These operations will be generated with calls of the form
            `two_qubit_op_factory(q0, q1, prng)`, where `prng` is the
            pseudorandom number generator.
        pattern: A sequence of GridInteractionLayers, each of which determine
            which pairs of qubits are entangled. The layers in a pattern are
            iterated through sequentially, repeating until `depth` is reached.
        single_qubit_gates: Single-qubit gates are selected randomly from this
            sequence. No qubit is acted upon by the same single-qubit gate in
            consecutive cycles. If only one choice of single-qubit gate is
            given, then this constraint is not enforced.
        add_final_single_qubit_layer: Whether to include a final layer of
            single-qubit gates after the last cycle.
        seed: A seed or random state to use for the pseudorandom number
            generator.
    """
    prng = value.parse_random_state(seed)
    qubits = list(qubits)
    coupled_qubit_pairs = _coupled_qubit_pairs(qubits)

    circuit = circuits.Circuit()
    previous_single_qubit_layer = ops.Moment()
    single_qubit_layer_factory = _single_qubit_gates_arg_to_factory(
        single_qubit_gates=single_qubit_gates, qubits=qubits, prng=prng
    )

    for i in range(depth):
        single_qubit_layer = single_qubit_layer_factory.new_layer(previous_single_qubit_layer)
        circuit += single_qubit_layer

        two_qubit_layer = _two_qubit_layer(
            coupled_qubit_pairs, two_qubit_op_factory, pattern[i % len(pattern)], prng
        )
        circuit += two_qubit_layer
        previous_single_qubit_layer = single_qubit_layer

    if add_final_single_qubit_layer:
        circuit += single_qubit_layer_factory.new_layer(previous_single_qubit_layer)

    return circuit
コード例 #2
0
 def new_layer(self, previous_single_qubit_layer: 'cirq.Moment') -> 'cirq.Moment':
     return ops.Moment(v.on(q) for q, v in self.fixed_single_qubit_layer.items())
コード例 #3
0
def random_circuit(
        qubits: Union[Sequence[ops.Qid], int],
        n_moments: int,
        op_density: float,
        gate_domain: Optional[Dict[ops.Gate, int]] = None,
        random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None) -> Circuit:
    """Generates a random circuit.

    Args:
        qubits: If a sequence of qubits, then these are the qubits that
            the circuit should act on. Because the qubits on which an
            operation acts are chosen randomly, not all given qubits
            may be acted upon. If an int, then this number of qubits will
            be automatically generated, and the qubits will be
            `cirq.NamedQubits` with names given by the integers in
            `range(qubits)`.
        n_moments: The number of moments in the generated circuit.
        op_density: The probability that a gate is selected to operate on
            randomly selected qubits. Note that this is not the expected number
            of qubits that are acted on, since there are cases where the
            number of qubits that a gate acts on does not evenly divide the
            total number of qubits.
        gate_domain: The set of gates to choose from, specified as a dictionary
            where each key is a gate and the value of the key is the number of
            qubits the gate acts on. If not provided, the default gate domain is
            {X, Y, Z, H, S, T, CNOT, CZ, SWAP, ISWAP, CZPowGate()}. Only gates
            which act on a number of qubits less than len(qubits) (or qubits if
            provided as an int) are selected from the gate domain.
        random_state: Random state or random state seed.

    Raises:
        ValueError:
            * op_density is not in (0, 1].
            * gate_domain is empty.
            * qubits is an int less than 1 or an empty sequence.

    Returns:
        The randomly generated Circuit.
    """
    if not 0 < op_density <= 1:
        raise ValueError(f'op_density must be in (0, 1] but was {op_density}.')
    if gate_domain is None:
        gate_domain = DEFAULT_GATE_DOMAIN
    if not gate_domain:
        raise ValueError('gate_domain must be non-empty.')

    if isinstance(qubits, int):
        qubits = tuple(ops.NamedQubit(str(i)) for i in range(qubits))
    n_qubits = len(qubits)
    if n_qubits < 1:
        raise ValueError('At least one qubit must be specified.')
    gate_domain = {k: v for k, v in gate_domain.items() if v <= n_qubits}
    if not gate_domain:
        raise ValueError(f'After removing gates that act on less than '
                         f'{n_qubits} qubits, gate_domain had no gates.')
    max_arity = max(gate_domain.values())

    prng = value.parse_random_state(random_state)

    moments: List[ops.Moment] = []
    gate_arity_pairs = sorted(gate_domain.items(), key=repr)
    num_gates = len(gate_domain)
    for _ in range(n_moments):
        operations = []
        free_qubits = set(qubits)
        while len(free_qubits) >= max_arity:
            gate, arity = gate_arity_pairs[prng.randint(num_gates)]
            op_qubits = prng.choice(sorted(free_qubits),
                                    size=arity,
                                    replace=False)
            free_qubits.difference_update(op_qubits)
            if prng.rand() <= op_density:
                operations.append(gate(*op_qubits))
        moments.append(ops.Moment(operations))

    return Circuit(moments)
コード例 #4
0
def random_rotations_between_two_qubit_circuit(
    q0: 'cirq.Qid',
    q1: 'cirq.Qid',
    depth: int,
    two_qubit_op_factory: Callable[
        ['cirq.Qid', 'cirq.Qid', 'np.random.RandomState'], 'cirq.OP_TREE'
    ] = lambda a, b, _: ops.CZPowGate()(a, b),
    single_qubit_gates: Sequence['cirq.Gate'] = (
        ops.X ** 0.5,
        ops.Y ** 0.5,
        ops.PhasedXPowGate(phase_exponent=0.25, exponent=0.5),
    ),
    add_final_single_qubit_layer: bool = True,
    seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
) -> 'cirq.Circuit':
    """Generate a random two-qubit quantum circuit.

    This construction uses a similar structure to those in the paper
    https://www.nature.com/articles/s41586-019-1666-5.

    The generated circuit consists of a number of "cycles", this number being
    specified by `depth`. Each cycle is actually composed of two sub-layers:
    a layer of single-qubit gates followed by a layer of two-qubit gates,
    controlled by their respective arguments, see below.

    Args:
        q0: The first qubit
        q1: The second qubit
        depth: The number of cycles.
        two_qubit_op_factory: A callable that returns a two-qubit operation.
            These operations will be generated with calls of the form
            `two_qubit_op_factory(q0, q1, prng)`, where `prng` is the
            pseudorandom number generator.
        single_qubit_gates: Single-qubit gates are selected randomly from this
            sequence. No qubit is acted upon by the same single-qubit gate in
            consecutive cycles. If only one choice of single-qubit gate is
            given, then this constraint is not enforced.
        add_final_single_qubit_layer: Whether to include a final layer of
            single-qubit gates after the last cycle (subject to the same
            non-consecutivity constraint).
        seed: A seed or random state to use for the pseudorandom number
            generator.
    """
    prng = value.parse_random_state(seed)

    circuit = circuits.Circuit()
    previous_single_qubit_layer = ops.Moment()
    single_qubit_layer_factory = _single_qubit_gates_arg_to_factory(
        single_qubit_gates=single_qubit_gates, qubits=(q0, q1), prng=prng
    )

    for _ in range(depth):
        single_qubit_layer = single_qubit_layer_factory.new_layer(previous_single_qubit_layer)
        circuit += single_qubit_layer
        circuit += two_qubit_op_factory(q0, q1, prng)
        previous_single_qubit_layer = single_qubit_layer

    if add_final_single_qubit_layer:
        circuit += single_qubit_layer_factory.new_layer(previous_single_qubit_layer)

    return circuit
コード例 #5
0
def _apply_amplitude_damp_noise(duration, t1, moments, system_qubits):
    moments.append(
        ops.Moment(
            ops.amplitude_damp(1 -
                               np.exp(-duration / t1)).on_each(system_qubits)))
コード例 #6
0
ファイル: qsim_simulator.py プロジェクト: rmlarose/qsim
    def _sample_measure_results(
        self,
        program: circuits.Circuit,
        repetitions: int = 1,
    ) -> Dict[str, np.ndarray]:
        """Samples from measurement gates in the circuit.

    Note that this will execute the circuit 'repetitions' times.

    Args:
        program: The circuit to sample from.
        repetitions: The number of samples to take.

    Returns:
        A dictionary from measurement gate key to measurement
        results. Measurement results are stored in a 2-dimensional
        numpy array, the first dimension corresponding to the repetition
        and the second to the actual boolean measurement results (ordered
        by the qubits being measured.)

    Raises:
        ValueError: If there are multiple MeasurementGates with the same key,
            or if repetitions is negative.
    """
        if not isinstance(program, qsimc.QSimCircuit):
            program = qsimc.QSimCircuit(program, device=program.device)

        # Compute indices of measured qubits
        ordered_qubits = ops.QubitOrder.DEFAULT.order_for(program.all_qubits())

        qubit_map = {
            qubit: index
            for index, qubit in enumerate(ordered_qubits)
        }

        # Computes
        # - the list of qubits to be measured
        # - the start (inclusive) and end (exclusive) indices of each measurement
        # - a mapping from measurement key to measurement gate
        measurement_ops = [
            op for _, op, _ in program.findall_operations_with_gate_type(
                ops.MeasurementGate)
        ]
        measured_qubits = []  # type: List[ops.Qid]
        bounds = {}  # type: Dict[str, Tuple]
        meas_ops = {}  # type: Dict[str, cirq.GateOperation]
        current_index = 0
        for op in measurement_ops:
            gate = op.gate
            key = protocols.measurement_key(gate)
            meas_ops[key] = op
            if key in bounds:
                raise ValueError(
                    "Duplicate MeasurementGate with key {}".format(key))
            bounds[key] = (current_index, current_index + len(op.qubits))
            measured_qubits.extend(op.qubits)
            current_index += len(op.qubits)

        # Set qsim options
        options = {}
        options.update(self.qsim_options)

        results = {}
        for key, bound in bounds.items():
            results[key] = np.ndarray(shape=(repetitions, bound[1] - bound[0]),
                                      dtype=int)

        if program.are_all_measurements_terminal() and repetitions > 1:
            print('Provided circuit has no intermediate measurements. ' +
                  'Sampling repeatedly from final state vector.')
            # Measurements must be replaced with identity gates to sample properly.
            # Simply removing them may omit qubits from the circuit.
            for i in range(len(program.moments)):
                program.moments[i] = ops.Moment(
                    op if not isinstance(op.gate, ops.MeasurementGate) else
                    [ops.IdentityGate(1).on(q) for q in op.qubits]
                    for op in program.moments[i])
            options['c'] = program.translate_cirq_to_qsim(
                ops.QubitOrder.DEFAULT)
            options['s'] = self.get_seed()
            final_state = qsim.qsim_simulate_fullstate(options)
            full_results = sim.sample_state_vector(final_state,
                                                   range(len(ordered_qubits)),
                                                   repetitions=repetitions,
                                                   seed=self._prng)

            for i in range(repetitions):
                for key, op in meas_ops.items():
                    meas_indices = [qubit_map[qubit] for qubit in op.qubits]
                    for j, q in enumerate(meas_indices):
                        results[key][i][j] = full_results[i][q]
        else:
            options['c'] = program.translate_cirq_to_qsim(
                ops.QubitOrder.DEFAULT)
            for i in range(repetitions):
                options['s'] = self.get_seed()
                measurements = qsim.qsim_sample(options)
                for key, bound in bounds.items():
                    for j in range(bound[1] - bound[0]):
                        results[key][i][j] = int(measurements[bound[0] + j])

        return results
コード例 #7
0
def random_circuit(qubits: Union[Sequence[ops.Qid], int],
                   n_moments: int,
                   op_density: float,
                   gate_domain: Optional[Dict[ops.Gate, int]] = None,
                   random_state: value.RANDOM_STATE_LIKE = None) -> Circuit:
    """Generates a random circuit.

    Args:
        qubits: If a sequence of qubits, then these are the qubits that
            the circuit should act on. Because the qubits on which an
            operation acts are chosen randomly, not all given qubits
            may be acted upon. If an int, then this number of qubits will
            be automatically generated.
        n_moments: the number of moments in the generated circuit.
        op_density: the expected proportion of qubits that are acted on in any
            moment.
        gate_domain: The set of gates to choose from, with a specified arity.
        random_state: Random state or random state seed.

    Raises:
        ValueError:
            * op_density is not in (0, 1).
            * gate_domain is empty.
            * qubits is an int less than 1 or an empty sequence.

    Returns:
        The randomly generated Circuit.
    """
    if not 0 < op_density < 1:
        raise ValueError('op_density must be in (0, 1).')
    if gate_domain is None:
        gate_domain = DEFAULT_GATE_DOMAIN
    if not gate_domain:
        raise ValueError('gate_domain must be non-empty')
    max_arity = max(gate_domain.values())

    if isinstance(qubits, int):
        qubits = tuple(ops.NamedQubit(str(i)) for i in range(qubits))
    n_qubits = len(qubits)
    if n_qubits < 1:
        raise ValueError('At least one qubit must be specified.')

    prng = value.parse_random_state(random_state)

    moments: List[ops.Moment] = []
    gate_arity_pairs = sorted(gate_domain.items(), key=repr)
    num_gates = len(gate_domain)
    for _ in range(n_moments):
        operations = []
        free_qubits = set(qubits)
        while len(free_qubits) >= max_arity:
            gate, arity = gate_arity_pairs[prng.randint(num_gates)]
            op_qubits = prng.choice(sorted(free_qubits),
                                    size=arity,
                                    replace=False)
            free_qubits.difference_update(op_qubits)
            if prng.rand() <= op_density:
                operations.append(gate(*op_qubits))
        moments.append(ops.Moment(operations))

    return Circuit(moments)
コード例 #8
0
ファイル: noise_model.py プロジェクト: PWJ1900/Rlearncirq
 def _noisy_operation_impl_moment(
         self, operation: 'cirq.Operation') -> 'cirq.OP_TREE':
     return self.noisy_moment(ops.Moment([operation]), operation.qubits)
コード例 #9
0
ファイル: linearize_circuit.py プロジェクト: YZNIU/Cirq-1
 def map_moment(self, moment: ops.Moment) -> ops.Moment:
     return ops.Moment(self.map_operation(op) for op in moment.operations)
コード例 #10
0
ファイル: noise_model.py プロジェクト: vanwelsenes/Cirq
 def noisy_moment(self, moment: 'cirq.Moment',
                  system_qubits: Sequence['cirq.Qid']):
     return [
         moment,
         ops.Moment([self.qubit_noise_gate(q) for q in system_qubits])
     ]
コード例 #11
0
def merge_operations(
    circuit: CIRCUIT_TYPE,
    merge_func: Callable[[ops.Operation, ops.Operation],
                         Optional[ops.Operation]],
) -> CIRCUIT_TYPE:
    """Merges operations in a circuit by calling `merge_func` iteratively on operations.

    Two operations op1 and op2 are merge-able if
        - There is no other operations between op1 and op2 in the circuit
        - is_subset(op1.qubits, op2.qubits) or is_subset(op2.qubits, op1.qubits)

    The `merge_func` is a callable which, given two merge-able operations
    op1 and op2, decides whether they should be merged into a single operation
    or not. If not, it should return None, else it should return the single merged
    operations `op`.

    The method iterates on the input circuit moment-by-moment from left to right and attempts
    to repeatedly merge each operation in the latest moment with all the corresponding merge-able
    operations to it's left.

    If op1 and op2 are merged, both op1 and op2 are deleted from the circuit and
    the resulting `merged_op` is inserted at the index corresponding to the larger
    of op1/op2. If both op1 and op2 act on the same number of qubits, `merged_op` is
    inserted in the smaller moment index to minimize circuit depth.

    The number of calls to `merge_func` is O(N), where N = Total no. of operations, because:
        - Every time the `merge_func` returns a new operation, the number of operations in the
            circuit reduce by 1 and hence this can happen at most O(N) times
        - Every time the `merge_func` returns None, the current operation is inserted into the
            frontier and we go on to process the next operation, which can also happen at-most
            O(N) times.

    Args:
        circuit: Input circuit to apply the transformations on. The input circuit is not mutated.
        merge_func: Callable to determine whether two merge-able operations in the circuit should
            be merged. If the operations can be merged, the callable should return the merged
            operation, else None.

    Returns:
        Copy of input circuit with merged operations.

    Raises:
        ValueError if the merged operation acts on new qubits outside the set of qubits
            corresponding to the original operations to be merged.
    """
    def apply_merge_func(op1: ops.Operation,
                         op2: ops.Operation) -> Optional[ops.Operation]:
        new_op = merge_func(op1, op2)
        qubit_set = frozenset(op1.qubits + op2.qubits)
        if new_op is not None and not qubit_set.issuperset(new_op.qubits):
            raise ValueError(
                f"Merged operation {new_op} must act on a subset of qubits of "
                f"original operations {op1} and {op2}")
        return new_op

    ret_circuit = circuits.Circuit()
    for current_moment in circuit:
        new_moment = ops.Moment()
        for op in sorted(current_moment.operations, key=lambda op: op.qubits):
            op_qs = set(op.qubits)
            idx = ret_circuit.prev_moment_operating_on(tuple(op_qs))
            if idx is not None and op_qs.issubset(
                    ret_circuit[idx][op_qs].operations[0].qubits):
                # Case-1: Try to merge op with the larger operation on the left.
                left_op = ret_circuit[idx][op_qs].operations[0]
                new_op = apply_merge_func(left_op, op)
                if new_op is not None:
                    ret_circuit.batch_replace([(idx, left_op, new_op)])
                else:
                    new_moment = new_moment.with_operation(op)
                continue

            while idx is not None and len(op_qs) > 0:
                # Case-2: left_ops will merge right into `op` whenever possible.
                for left_op in ret_circuit[idx][op_qs].operations:
                    is_merged = False
                    if op_qs.issuperset(left_op.qubits):
                        # Try to merge left_op into op
                        new_op = apply_merge_func(left_op, op)
                        if new_op is not None:
                            ret_circuit.batch_remove([(idx, left_op)])
                            op, is_merged = new_op, True
                    if not is_merged:
                        op_qs -= frozenset(left_op.qubits)
                idx = ret_circuit.prev_moment_operating_on(tuple(op_qs))
            new_moment = new_moment.with_operation(op)
        ret_circuit += new_moment
    return _to_target_circuit_type(ret_circuit, circuit)