Circuit
=======

The :class:`~qdk_chemistry.data.Circuit` class in QDK/Chemistry represents a quantum circuit.
It stores the circuit in one or more formats and converts between them on demand.


Worked example
--------------

The following example defines a Q# operation from a string, wraps the compiled circuit in a :class:`~qdk_chemistry.data.Circuit`, inspects the gate-level diagram, and runs resource estimation:

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/circuit.py
      :language: python
      :start-after: # start-cell-qsharp-workflow
      :end-before: # end-cell-qsharp-workflow


Overview
--------

A :class:`~qdk_chemistry.data.Circuit` wraps a quantum circuit that may be represented in any combination of the following formats:

.. list-table::
   :header-rows: 1
   :widths: 25 75

   * - Format
     - Description
   * - ``qasm``
     - An OpenQASM string.
   * - ``qir``
     - A :term:`QIR` (Quantum Intermediate Representation) object for cross-platform compilation.
   * - ``qsharp``
     - A compiled ``qdk._native.Circuit`` object for inspection and visualization.
   * - ``qsharp_op``
     - A Q# callable — a native Q# operation that can be composed with other Q# operations.
   * - ``qsharp_factory``
     - A ``QsharpFactoryData`` — a deferred Q# program that compiles on demand (see `Q# factory harness`_ below).

At least one representation must be provided at construction.
When a format that is not stored is requested, the class converts automatically — for example, calling :meth:`~qdk_chemistry.data.Circuit.get_qsharp_circuit` on a circuit that only has ``qasm`` will compile it to Q# via the ``qsharp`` package.

The full constructor signature is:

.. code-block:: python

   Circuit(
       qasm: str | None = None,
       qir: QirInputData | str | None = None,
       qsharp: qdk._native.Circuit | None = None,
       qsharp_op: Callable | None = None,
       qsharp_factory: QsharpFactoryData | None = None,
       encoding: str | None = None,
   )

The ``encoding`` parameter records the fermion-to-qubit mapping assumed by the circuit (e.g., ``"jordan-wigner"``).


.. _qsharp-factory-harness:

Q# factory harness
-------------------

The Q# factory is the primary mechanism by which QDK/Chemistry builds circuits internally.
Rather than constructing a circuit as a gate sequence, the library stores a reference to a **Q# callable** along with the **classical parameters** that configure it.
The circuit is compiled only when a concrete representation is actually needed.

A ``QsharpFactoryData`` is a frozen dataclass with two fields:

``program``
   A Q# callable — a reference to a Q# operation that constructs the circuit.
   In practice this is typically a Q# factory function exposed via the ``qsharp`` Python package (e.g., ``QSHARP_UTILS.StatePreparation.MakeStatePreparationCircuit``).

``parameter``
   A ``dict[str, Any]`` of classical parameters that are passed to the Q# callable at compilation time.
   These are Python-native values — lists, ints, floats, nested dicts, or even other Q# operations — that the Q# program uses to configure the circuit it produces.

The standard pattern for building a factory is:

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/circuit.py
      :language: python
      :start-after: # start-cell-qsharp-harness
      :end-before: # end-cell-qsharp-harness


This pattern is what enables end-to-end Q# composition: because the factory stores Q# callables rather than serialized gate sequences, multi-stage circuits compose natively in the Q# runtime without intermediate format conversions.


.. _shared-qsharp-context:

Shared Q# context
-----------------

QDK enforces that Q# operations composed together belong to the **same** ``qdk.Context``.
Composing a callable from one context into a program owned by another raises ``QSharpError``.

Three scenarios cover how users interact with this:

**Using QDK/Chemistry algorithms only.**
You never touch the Q# context. Every circuit the library builds is created against one internal
shared context, so everything composes automatically.

**Bringing your own Q# operation.**
When you define your own Q# program (for example, a custom ``BellState`` state preparation) and want
to compose it with a chemistry builder such as QPE, it must be built against that same shared
context. Obtain the context with :func:`~qdk_chemistry.utils.qsharp.get_qsharp_context`, define your
operation on it, and pass the resulting callable to the builder.

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/circuit.py
      :language: python
      :start-after: # start-cell-byo-operation
      :end-before: # end-cell-byo-operation

**Bringing your own Q# context.**
If you want to change the target profile — or any other ``qdk.Context`` option — build a context with
:func:`~qdk_chemistry.utils.qsharp.create_qsharp_context` (it keeps the chemistry utilities loaded on
the project root), then make the library use it: :func:`~qdk_chemistry.utils.qsharp.set_qsharp_context`
installs it process-wide (pass ``None`` to restore the default), while
:func:`~qdk_chemistry.utils.qsharp.use_qsharp_context` switches to it temporarily on the current
thread and restores it automatically on exit.

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/circuit.py
      :language: python
      :start-after: # start-cell-byo-context
      :end-before: # end-cell-byo-context

The four helpers in :mod:`qdk_chemistry.utils.qsharp` that manage this are:

:func:`~qdk_chemistry.utils.qsharp.get_qsharp_context`
   Returns the shared context that owns ``QSHARP_UTILS``. Build your own operations against it so they
   compose with the chemistry builders.

:func:`~qdk_chemistry.utils.qsharp.create_qsharp_context`
   Creates a fresh context preloaded with the chemistry utilities. Forwards ``target_profile`` and any
   other ``qdk.Context`` keyword arguments; use it when you need a non-default configuration.

:func:`~qdk_chemistry.utils.qsharp.set_qsharp_context`
   Installs a caller-supplied context process-wide. Pass ``None`` to reset to the default context.

:func:`~qdk_chemistry.utils.qsharp.use_qsharp_context`
   A context manager that overrides the active context for the current thread only and restores it
   on exit.


Conversion methods
------------------

Each method returns the circuit in the requested format, converting from whatever representation is available:

.. list-table::
   :header-rows: 1
   :widths: 35 65

   * - Method
     - Description
   * - :meth:`~qdk_chemistry.data.Circuit.get_qsharp_circuit`
     - Returns a ``qdk._native.Circuit`` for inspection. Accepts ``prune_classical_qubits`` to remove unused qubits.
   * - :meth:`~qdk_chemistry.data.Circuit.get_qir`
     - Returns the :term:`QIR` representation. Compiles from Q# factory or converts from QASM if needed.
   * - :meth:`~qdk_chemistry.data.Circuit.get_qasm`
     - Returns the OpenQASM string. Converts from :term:`QIR` via Qiskit if only :term:`QIR` is available.
   * - :meth:`~qdk_chemistry.data.Circuit.get_qiskit_circuit`
     - Returns a Qiskit ``QuantumCircuit``. Requires ``qiskit`` to be installed.
   * - :meth:`~qdk_chemistry.data.Circuit.estimate`
     - Runs Q#'s resource estimator on the circuit. Accepts optional ``params`` for estimation configuration.

Example: Convert state preparation circuits to different formats
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/circuit.py
      :language: python
      :start-after: # start-cell-conversion
      :end-before: # end-cell-conversion


Properties
----------

.. list-table::
   :header-rows: 1
   :widths: 25 15 60

   * - Property
     - Type
     - Description
   * - ``qasm``
     - str | None
     - OpenQASM circuit string, if available.
   * - ``qir``
     - QirInputData | str | None
     - :term:`QIR` representation, if available. Lazily compiled from the Q# factory on first access.
   * - ``qsharp``
     - qdk._native.Circuit | None
     - Compiled Q# circuit object, if available.
   * - ``encoding``
     - str | None
     - Qubit encoding label (e.g., ``"jordan-wigner"``), if set.


Serialization
-------------

:class:`~qdk_chemistry.data.Circuit` supports the same :doc:`serialization <serialization>` formats as other QDK/Chemistry data classes:

.. tab:: Python API

   .. literalinclude:: ../../../_static/examples/python/circuit.py
      :language: python
      :start-after: # start-cell-serialization
      :end-before: # end-cell-serialization

.. note::

   Serialization persists the OpenQASM and :term:`QIR` representations.
   Q# callables and factories are not directly serializable — they are reconstructed when the producing algorithm re-creates the circuit.


Related classes
---------------

- :class:`~qdk_chemistry.data.CircuitExecutorData`: Measurement results from circuit execution
- :class:`~qdk_chemistry.data.QubitOperator`: Often paired with a circuit for energy estimation

Further reading
---------------

- The above examples can be downloaded as a complete `Python <../../../_static/examples/python/circuit.py>`_ script.
- :doc:`CircuitExecutor <../algorithms/circuit_executor>`: Execute circuits on backends
- :doc:`Serialization <serialization>`: Data persistence formats
