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}.' )
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