quimb.tensor.circuit.core ========================= .. py:module:: quimb.tensor.circuit.core .. autoapi-nested-parse:: Representation-agnostic quantum circuit interface (:class:`CircuitBase`). Classes ------- .. autoapisummary:: quimb.tensor.circuit.core.CircuitBase Module Contents --------------- .. py:class:: CircuitBase(N=None, psi0=None, gate_opts=None, gate_contract='auto-split-gate', gate_propagate_tags='register', tags=None, psi0_dtype='complex128', psi0_tag='PSI0', tag_gate_numbers=True, gate_tag_id='GATE_{}', tag_gate_rounds=True, round_tag_id='ROUND_{}', tag_gate_labels=True, bra_site_ind_id='b{}', dtype=None, to_backend=None, convert_eager=False) Representation-agnostic interface shared by every circuit simulator. Holds the circuit *description* (the list of :class:`Gate` objects, gate application front-end and convenience methods, named-parameter management, backend/dtype conversion, the ``from_*`` constructors, drawing) plus the generic ``_apply_gate`` dispatch. It carries **no** exact-contraction machinery, so representation-specific simulators can compose this interface without inheriting the exact :class:`Circuit`. Subclasses must implement the ``_init_state`` and ``psi`` hooks and may override ``calc_qubit_ordering`` (the default is a trivial sorted order). .. rubric:: Notes The named-parameter methods (``register_named_params``/``get_params``/ ``set_params``/``update_params_from``) index gate tensors by tag, so they are only functional when ``tag_gate_numbers=True`` (the exact :class:`Circuit` default). Representations that disable gate-number tagging (MPS/PEPS/PEPO) inherit them but they are non-functional there. .. py:attribute:: tag_gate_numbers :value: True .. py:attribute:: tag_gate_rounds :value: True .. py:attribute:: tag_gate_labels :value: True .. py:attribute:: dtype :value: None .. py:attribute:: to_backend :value: None .. py:attribute:: convert_eager :value: False .. py:attribute:: _backend_gate_cache .. py:attribute:: gate_opts .. py:attribute:: _gates :value: [] .. py:attribute:: _ket_site_ind_id .. py:attribute:: _bra_site_ind_id :value: 'b{}' .. py:attribute:: _gate_tag_id :value: 'GATE_{}' .. py:attribute:: _round_tag_id :value: 'ROUND_{}' .. py:attribute:: _sample_n_gates :value: -1 .. py:attribute:: _storage .. py:attribute:: _sampled_conditionals .. py:attribute:: _named_params .. py:attribute:: _named_param_exprs .. py:method:: copy() Copy the circuit and its state. .. py:method:: _maybe_convert(obj, dtype=None) .. py:method:: apply_to_arrays(fn) Apply a function to all the arrays in the circuit. .. py:method:: _normalize_named_param_value(value) :staticmethod: .. py:property:: named_params Named circuit parameters and their current values. .. py:property:: named_param_names Names of registered circuit parameters. .. py:property:: param_expressions Gate parameter expressions keyed by gate index. .. py:method:: register_named_params(named_params, gate_expressions=None) Register named circuit parameters and gate dependencies. :param named_params: Either names to register, which default to ``nan`` until bound, or a mapping supplying initial values. :type named_params: sequence[str] or mapping[str, scalar] :param gate_expressions: Mapping from gate index to the expressions used to generate that gate's parameters. Each expression can be a constant, a string expression referencing the named parameters, or a callable taking the current named parameter mapping. :type gate_expressions: mapping[int, tuple], optional .. py:method:: _set_gate_params(i, params) .. py:method:: _apply_named_param_updates() .. py:method:: get_params() Get a pytree - in this case a dict - of all the parameters in the circuit. :returns: Dictionary containing any named parameters plus any directly parametrized gates not driven by named parameter expressions. :rtype: dict .. py:method:: set_params(params) Set the parameters of the circuit. :param params: Dictionary mapping gate numbers and/or registered named parameter names to new values. :type params: dict .. py:method:: from_qsim_str(contents, progbar=False, **circuit_opts) :classmethod: Generate a ``Circuit`` instance from a 'qsim' string. .. py:method:: from_qsim_file(fname, progbar=False, **circuit_opts) :classmethod: Generate a ``Circuit`` instance from a 'qsim' file. The qsim file format is described here: https://quantumai.google/qsim/input_format. .. py:method:: from_qsim_url(url, progbar=False, **circuit_opts) :classmethod: Generate a ``Circuit`` instance from a 'qsim' url. .. py:attribute:: from_qasm .. py:attribute:: from_qasm_file .. py:attribute:: from_qasm_url .. py:method:: from_openqasm2_str(contents, progbar=False, **circuit_opts) :classmethod: Generate a ``Circuit`` instance from an OpenQASM 2.0 string. .. py:method:: from_openqasm2_file(fname, progbar=False, **circuit_opts) :classmethod: Generate a ``Circuit`` instance from an OpenQASM 2.0 file. .. py:method:: from_openqasm2_url(url, progbar=False, **circuit_opts) :classmethod: Generate a ``Circuit`` instance from an OpenQASM 2.0 url. .. py:method:: from_openqasm3_str(contents, progbar=False, **circuit_opts) :classmethod: Construct a circuit from an OpenQASM 3.0 string. :param contents: The OpenQASM 3 source code to parse. :type contents: str :param progbar: Whether to show a progress bar while applying the parsed gates. :type progbar: bool, optional :param \*\*circuit_opts: Options forwarded to the ``Circuit`` constructor. :returns: A circuit populated with the parsed gates. If symbolic ``input`` declarations are present, they are registered as generic named circuit parameters so that :meth:`set_params` can bind them later. :rtype: Circuit .. py:method:: from_openqasm3_file(fname, progbar=False, **circuit_opts) :classmethod: Construct a circuit from an OpenQASM 3.0 file. :param fname: Path to the OpenQASM 3 file. :type fname: str or path-like :param progbar: Whether to show a progress bar while applying the parsed gates. :type progbar: bool, optional :param \*\*circuit_opts: Options forwarded to the ``Circuit`` constructor. :returns: The parsed circuit instance. :rtype: Circuit .. py:method:: from_openqasm3_url(url, progbar=False, **circuit_opts) :classmethod: Construct a circuit from an OpenQASM 3.0 URL. :param url: URL pointing to an OpenQASM 3 source file. :type url: str :param progbar: Whether to show a progress bar while applying the parsed gates. :type progbar: bool, optional :param \*\*circuit_opts: Options forwarded to the ``Circuit`` constructor. :returns: The parsed circuit instance. :rtype: Circuit .. py:method:: from_gates(gates, N=None, progbar=False, **kwargs) :classmethod: Generate a ``Circuit`` instance from a sequence of gates. :param gates: The sequence of gates to apply. :type gates: sequence[Gate] or sequence[tuple] :param N: The number of qubits. If not given, will be inferred from the gates. :type N: int, optional :param progbar: Whether to show a progress bar. :type progbar: bool, optional :param kwargs: Supplied to the ``Circuit`` constructor. .. py:property:: gates .. py:property:: num_gates .. py:method:: ket_site_ind(i) Get the site index for the given qubit. .. py:method:: bra_site_ind(i) Get the 'bra' site index for the given qubit, if forming an operator. .. py:method:: gate_tag(g) Get the tag for the given gate, indexed linearly. .. py:method:: round_tag(r) Get the tag for the given round (/layer). .. py:method:: _apply_gate(gate, tags=None, **gate_opts) Apply a ``Gate`` to this ``Circuit``. This is the main method that all calls to apply a gate should go through. :param gate: The gate to apply. :type gate: Gate :param tags: Tags to add to the gate tensor(s). :type tags: str or sequence of str, optional .. py:method:: apply_gate(gate_id, *gate_args, params=None, qubits=None, controls=None, gate_round=None, parametrize=None, **gate_opts) Apply a single gate to this tensor network quantum circuit. If ``gate_round`` is supplied the tensor(s) added will be tagged with ``'ROUND_{gate_round}'``. Alternatively, putting an integer first like so:: circuit.apply_gate(10, 'H', 7) Is automatically translated to:: circuit.apply_gate('H', 7, gate_round=10) :param gate_id: Which gate to apply. This can be: - A ``Gate`` instance, i.e. with parameters and qubits already specified. - A string, e.g. ``'H'``, ``'U3'``, etc. in which case ``gate_args`` should be supplied with ``(*params, *qubits)``. - A raw array, in which case ``gate_args`` should be supplied with ``(*qubits,)``. :type gate_id: Gate, str, or array_like :param gate_args: The arguments to supply to it. :type gate_args: list[str] :param gate_round: The gate round. If ``gate_id`` is integer-like, will also be taken from here, with then ``gate_id, gate_args = gate_args[0], gate_args[1:]``. :type gate_round: int, optional :param gate_opts: Supplied to the gate function, options here will override the default ``gate_opts``. .. py:method:: apply_gate_raw(U, where, controls=None, gate_round=None, **gate_opts) Apply the raw array ``U`` as a gate on qubits in ``where``. It will be assumed to be unitary for the sake of computing reverse lightcones. .. py:method:: apply_gates(gates, progbar=False, **gate_opts) Apply a sequence of gates to this tensor network quantum circuit. :param gates: The sequence of gates to apply. :type gates: Sequence[Gate] or Sequence[Tuple] :param gate_opts: Supplied to :meth:`~quimb.tensor.circuit.Circuit.apply_gate`. .. py:method:: h(i, gate_round=None, **kwargs) .. py:method:: x(i, gate_round=None, **kwargs) .. py:method:: y(i, gate_round=None, **kwargs) .. py:method:: z(i, gate_round=None, **kwargs) .. py:method:: s(i, gate_round=None, **kwargs) .. py:method:: sdg(i, gate_round=None, **kwargs) .. py:method:: t(i, gate_round=None, **kwargs) .. py:method:: tdg(i, gate_round=None, **kwargs) .. py:method:: sx(i, gate_round=None, **kwargs) .. py:method:: sxdg(i, gate_round=None, **kwargs) .. py:method:: x_1_2(i, gate_round=None, **kwargs) .. py:method:: y_1_2(i, gate_round=None, **kwargs) .. py:method:: z_1_2(i, gate_round=None, **kwargs) .. py:method:: w_1_2(i, gate_round=None, **kwargs) .. py:method:: hz_1_2(i, gate_round=None, **kwargs) .. py:method:: cnot(i, j, gate_round=None, **kwargs) .. py:method:: cx(i, j, gate_round=None, **kwargs) .. py:method:: cy(i, j, gate_round=None, **kwargs) .. py:method:: cz(i, j, gate_round=None, **kwargs) .. py:method:: iswap(i, j, gate_round=None, **kwargs) .. py:method:: iden(i, gate_round=None) .. py:method:: swap(i, j, gate_round=None, **kwargs) .. py:method:: rx(theta, i, gate_round=None, parametrize=False, **kwargs) .. py:method:: ry(theta, i, gate_round=None, parametrize=False, **kwargs) .. py:method:: rz(theta, i, gate_round=None, parametrize=False, **kwargs) .. py:method:: u3(theta, phi, lamda, i, gate_round=None, parametrize=False, **kwargs) .. py:method:: u2(phi, lamda, i, gate_round=None, parametrize=False, **kwargs) .. py:method:: u1(lamda, i, gate_round=None, parametrize=False, **kwargs) .. py:method:: phase(lamda, i, gate_round=None, parametrize=False, **kwargs) .. py:method:: cu3(theta, phi, lamda, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: cu2(phi, lamda, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: cu1(lamda, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: cphase(lamda, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: fsim(theta, phi, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: fsimg(theta, zeta, chi, gamma, phi, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: givens(theta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: givens2(theta, phi, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: xx_plus_yy(theta, beta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: xx_minus_yy(theta, beta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: rxx(theta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: ryy(theta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: rzz(theta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: crx(theta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: cry(theta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: crz(theta, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: su4(theta1, phi1, lamda1, theta2, phi2, lamda2, theta3, phi3, lamda3, theta4, phi4, lamda4, t1, t2, t3, i, j, gate_round=None, parametrize=False, **kwargs) .. py:method:: ccx(i, j, k, gate_round=None, **kwargs) .. py:method:: ccnot(i, j, k, gate_round=None, **kwargs) .. py:method:: toffoli(i, j, k, gate_round=None, **kwargs) .. py:method:: ccy(i, j, k, gate_round=None, **kwargs) .. py:method:: ccz(i, j, k, gate_round=None, **kwargs) .. py:method:: cswap(i, j, k, gate_round=None, **kwargs) .. py:method:: fredkin(i, j, k, gate_round=None, **kwargs) .. py:method:: clear_storage() Clear all cached data. .. py:method:: _maybe_init_storage() .. py:method:: simulate_counts(C, seed=None, reverse=False, **to_dense_opts) Simulate measuring all qubits in the computational basis many times. Unlike :meth:`~quimb.tensor.circuit.Circuit.sample`, this generates all the samples simultaneously using the full wavefunction constructed from :meth:`~quimb.tensor.circuit.Circuit.to_dense`, then calling :func:`~quimb.calc.simulate_counts`. .. warning:: Because this constructs the full wavefunction it always requires exponential memory in the number of qubits, regardless of circuit depth and structure. :param C: The number of 'experimental runs', i.e. total counts. :type C: int :param seed: A seed for reproducibility. :type seed: int, optional :param reverse: Whether to reverse the order of the subsystems, to match the convention of qiskit for example. :type reverse: bool, optional :param to_dense_opts: Suppled to :meth:`~quimb.tensor.circuit.Circuit.to_dense`. :returns: **results** -- The number of recorded counts for each :rtype: dict[str, int] .. py:method:: xeb(samples_or_counts, cache=None, cache_maxsize=2**20, progbar=False, **amplitude_opts) Compute the linear cross entropy benchmark (XEB) for samples or counts, amplitude per amplitude. :param samples_or_counts: Either the raw bitstring samples or a dict mapping bitstrings to the number of counts observed. :type samples_or_counts: Iterable[str] or Dict[str, int] :param cache: A dictionary to store the probabilities in, if not supplied ``quimb.utils.LRU(cache_maxsize)`` will be used. :type cache: dict, optional :param cache_maxsize: The maximum size of the cache to be used. :param optional: The maximum size of the cache to be used. :param progbar: Whether to show progress as the bitstrings are iterated over. :param optional: Whether to show progress as the bitstrings are iterated over. :param amplitude_opts: Supplied to :meth:`~quimb.tensor.circuit.Circuit.amplitude`. .. py:method:: update_params_from(tn) Assuming ``tn`` is a tensor network with tensors tagged ``GATE_{i}`` corresponding to this circuit (e.g. from ``circ.psi`` or ``circ.uni``) but with updated parameters, update the current circuit parameters and tensors with those values. This is an inplace modification of the ``Circuit``. :param tn: The tensor network to find the updated parameters from. :type tn: TensorNetwork .. py:method:: draw(figsize=None, radius=1 / 3, drawcolor=(0.5, 0.5, 0.5), linewidth=1) Draw a simple linear schematic of the circuit. :param figsize: The size of the figure, if not given will be set based on the number of gates and qubits. :type figsize: tuple, optional :param radius: The radius of the gates. :type radius: float, optional :param drawcolor: The color of the wires. :type drawcolor: tuple, optional :param linewidth: The linewidth of the wires. :type linewidth: float, optional :returns: * **fig** (*matplotlib.Figure*) -- The figure object. * **ax** (*matplotlib.Axes*) -- The axis object. .. py:method:: __repr__() .. py:method:: _init_state(N, dtype='complex128') :abstractmethod: .. py:method:: get_psi() :abstractmethod: Get a copy of the current state tensor network. This is the single method each representation must implement to expose its state, and is what the ``psi`` property calls. .. py:property:: psi Tensor network representation of the current state, a copy, see :meth:`get_psi`. .. py:method:: calc_qubit_ordering(qubits=None) Default trivial qubit ordering; the exact ``Circuit`` overrides this with a lightcone-aware ordering used for sampling.