Exemplo n.º 1
0
def _register_custom_gate(gate_json: Any, registry: Dict[str, CellMaker]):
    if not isinstance(gate_json, Dict):
        raise ValueError(
            f'Custom gate json must be a dictionary.\n' f'Custom gate json={gate_json!r}.'
        )

    if 'id' not in gate_json:
        raise ValueError(
            f'Custom gate json must have an id key.\n' f'Custom gate json={gate_json!r}.'
        )
    identifier = gate_json['id']
    if identifier in registry:
        raise ValueError(f'Custom gate with duplicate identifier: {identifier!r}')

    if 'matrix' in gate_json and 'circuit' in gate_json:
        raise ValueError(
            f'Custom gate json cannot have both a matrix and a circuit.\n'
            f'Custom gate json={gate_json!r}.'
        )

    if 'matrix' in gate_json:
        if not isinstance(gate_json['matrix'], str):
            raise ValueError(
                f'Custom gate matrix json must be a string.\n' f'Custom gate json={gate_json!r}.'
            )
        gate = ops.MatrixGate(parse_matrix(gate_json['matrix']))
        registry[identifier] = CellMaker(
            identifier=identifier,
            size=gate.num_qubits(),
            maker=lambda args: gate(*args.qubits[::-1]),
        )

    elif 'circuit' in gate_json:
        comp = _parse_cols_into_composite_cell(gate_json['circuit'], registry)
        registry[identifier] = CellMaker(
            identifier=identifier,
            size=comp.height,
            maker=lambda args: comp.with_line_qubits_mapped_to(list(args.qubits)),
        )

    else:
        raise ValueError(
            f'Custom gate json must have a matrix or a circuit.\n'
            f'Custom gate json={gate_json!r}.'
        )
Exemplo n.º 2
0
def quirk_json_to_circuit(
    data: dict,
    *,
    qubits: Optional[Sequence['cirq.Qid']] = None,
    extra_cell_makers: Union[
        Dict[str, 'cirq.Gate'],
        Iterable['cirq.interop.quirk.cells.CellMaker']] = (),
    quirk_url: Optional[str] = None,
    max_operation_count: int = 10**6,
) -> 'cirq.Circuit':
    """Constructs a Cirq circuit from Quirk's JSON format.

    Args:
        data: Data parsed from quirk's JSON representation.
        qubits: Qubits to use in the circuit. See quirk_url_to_circuit.
        extra_cell_makers: Non-standard Quirk cells to accept. See
            quirk_url_to_circuit.
        quirk_url: If given, the original URL from which the JSON was parsed, as
            described in quirk_url_to_circuit.
        max_operation_count: If the number of operations in the circuit would
            exceed this value, the method raises a `ValueError` instead of
            attempting to construct the circuit. This is important to specify
            for servers parsing unknown input, because Quirk's format allows for
            a billion laughs attack in the form of nested custom gates.

    Examples:
        >>> print(cirq.quirk_json_to_circuit(
        ...     {"cols":[["H"], ["•", "X"]]}
        ... ))
        0: ───H───@───
                  │
        1: ───────X───

    Returns:
        The parsed circuit.

    Raises:
        ValueError: Invalid circuit URL, or circuit would be larger than
            `max_operations_count`.
    """
    def msg(error):
        if quirk_url is not None:
            return f'{error}\nURL={quirk_url}\nJSON={data}'
        else:
            return f'{error}\nJSON={data}'

    if not isinstance(data, dict):
        raise ValueError(msg('Circuit JSON must have a top-level dictionary.'))
    if not data.keys() <= {'cols', 'gates', 'init'}:
        raise ValueError(msg('Unrecognized Circuit JSON keys.'))

    # Collect registry of quirk cell types.
    if isinstance(extra_cell_makers, Mapping):
        extra_makers = [
            CellMaker(
                identifier=identifier,
                size=protocols.num_qubits(gate),
                maker=(lambda gate: lambda args: gate(*args.qubits))(gate),
            ) for identifier, gate in extra_cell_makers.items()
        ]
    else:
        extra_makers = list(extra_cell_makers)
    registry = {
        entry.identifier: entry
        for entry in [*generate_all_quirk_cell_makers(), *extra_makers]
    }

    # Include custom gates in the registry.
    if 'gates' in data:
        if not isinstance(data['gates'], list):
            raise ValueError('"gates" JSON must be a list.')
        for custom_gate in data['gates']:
            _register_custom_gate(custom_gate, registry)

    # Parse out the circuit.
    comp = _parse_cols_into_composite_cell(data, registry)
    if max_operation_count is not None and comp.gate_count(
    ) > max_operation_count:
        raise ValueError(
            f'Quirk URL specifies a circuit with {comp.gate_count()} '
            f'operations, but max_operation_count={max_operation_count}.')
    circuit = comp.circuit()

    # Convert state initialization into operations.
    circuit.insert(0, _init_ops(data))

    # Remap qubits if requested.
    if qubits is not None:
        qs = cast(Sequence['cirq.Qid'], qubits)

        def map_qubit(qubit: 'cirq.Qid') -> 'cirq.Qid':
            q = cast(devices.LineQubit, qubit)
            if q.x >= len(qs):
                raise IndexError(
                    f'Only {len(qs)} qubits specified, but the given quirk '
                    f'circuit used the qubit at offset {q.x}. Provide more '
                    f'qubits.')
            return qs[q.x]

        circuit = circuit.transform_qubits(map_qubit)

    return circuit