> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qbraid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Programs

> Extract and manage metadata from supported quantum program types, with the flexibility to introduce new types.

In this module, you will learn how to use the qBraid-SDK to interface with quantum circuits across various frontends.
We will demonstrate how to load quantum programs, register new quantum program types, and highlight a few other circuit-level
convenience features.

<Info>
  API Reference:
  [qbraid.programs](https://qbraid.github.io/qBraid/api/qbraid.programs.html)
</Info>

## Quantum Program Registry

The `QPROGRAM_REGISTRY` contains dictionary mappings of shorthand identifiers for "registered" quantum program types.
By default (i.e. without any optional dependencies installed) this simply includes three variations of OpenQASM programs:

```python theme={null}
>>> from qbraid import QPROGRAM_REGISTRY
>>> QPROGRAM_REGISTRY
{'openqasm3': openqasm3.ast.Program,
 'qasm2': str,
 'qasm3': str}
```

In this example, 'openqasm3' serves as a type alias for an `openqasm3.ast.Program` object, while 'qasm2' and 'qasm3' are aliases
for raw OpenQASM 2 and OpenQASM 3 strings, respectively.

For each of its 10 natively supported program type aliases, the qBraid-SDK will automatically detect whether the corresponding library is installed,
and add that program type along with its type alias to the `QPROGRAM_REGISTRY`. For example, after installing the following optional dependencies,

```bash theme={null}
pip install cirq-core qiskit amazon-braket-sdk pennylane pyquil pytket pyqir
```

the `QPROGRAM_REGISTRY` is automatically updated:

```python theme={null}
>>> from qbraid import QPROGRAM_REGISTRY
>>> QPROGRAM_REGISTRY
{'cirq': cirq.circuits.circuit.Circuit,
 'qiskit': qiskit.circuit.quantumcircuit.QuantumCircuit,
 'pennylane': pennylane.tape.tape.QuantumTape,
 'pyquil': pyquil.quil.Program,
 'pytket': pytket._tket.circuit.Circuit,
 'braket': braket.circuits.circuit.Circuit,
 'openqasm3': openqasm3.ast.Program,
 'pyqir': Module,
 'qasm2': str,
 'qasm3': str}
```

This arrangement simplifies the process of targeting and transpiling between various quantum programming frameworks,
which will be discussed in detail in the transpiler, interface, and runtime tutorials.

### Register New Program Types

You can register new quantum program types using either custom or default type aliases, and overwrite
existing type aliases when necessary. For example:

```python theme={null}
import cirq
import stim

from qbraid import register_program_type

# register new program type with default module alias
register_program_type(stim.Circuit)

# register native program type with custom alias
register_program_type(cirq.Circuit, alias="myalias")

# overwrite existing type alias with new program type
register_program_type(dict, alias="qasm2", overwrite=True)
```

## Load Program

Any quantum program of a natively supported type can be encapsulated within a `qbraid.programs.QbraidProgram`
object. This encapsulation provides a unified framework for circuit manipulation—such as removing idle qubits
and reversing qubit order—and for extracting valuable metadata, including the number of qubits, circuit depth,
and the unitary representation.

As an example, we'll start with a simple qiskit circuit that creates the bell state,
load the quantum circuit object into the qBraid representation, verify the
circuit depth number of qubits, calculate the circuit's unitary representation,
and draw the circuit.

<CodeGroup>
  ```python Input theme={null}
  from qbraid import load_program
  from qbraid.visualization import circuit_drawer

  qprogram = load_program(qiskit_circuit)

  assert qprogram.num_qubits == 2
  assert qprogram.depth == 2
  assert qprogram.unitary().shape ==  (4, 4)

  circuit_drawer(qprogram.program)
  ```

  ```text Output theme={null}
       ┌───┐
  q_0: ┤ H ├──■──
       └───┘┌─┴─┐
  q_1: ─────┤ X ├
            └───┘
  ```
</CodeGroup>

Now, let's try reversing the qubit order, and drawing the circuit again:

<CodeGroup>
  ```python Input theme={null}
  qprogram.reverse_qubit_order()

  circuit_drawer(qprogram.program)
  ```

  ```text Ouput theme={null}
            ┌───┐
  q_0: ─────┤ X ├
       ┌───┐└─┬─┘
  q_1: ┤ H ├──■──
       └───┘
  ```
</CodeGroup>

Any quantum program type listed in the `qbraid.NATIVE_REGISTRY` can be loaded and utilized in this manner.
Each `QbraidProgram` object has `num_qubits` and `depth` attributes and `unitary()` method, regardless of the input circuit type.
For example, we could load an Amazon Braket circuit, and repeat similar steps:

<CodeGroup>
  ```python Input theme={null}
  from qbraid import load_program
  from qbraid.visualization import circuit_drawer

  qprogram = load_program(braket_circuit)

  assert qprogram.unitary().shape == ((1 << qprogram.num_qubits),) * 2

  circuit_drawer(qprogram.program)
  ```

  ```text Output theme={null}
  T  : │  0  │  1  │  2  │
        ┌───┐
  q0 : ─┤ H ├───●─────────
        └───┘   │
              ┌─┴─┐
  q2 : ───────┤ X ├───●───
              └───┘   │
                    ┌─┴─┐
  q4 : ─────────────┤ X ├─
                    └───┘
  T  : │  0  │  1  │  2  │
  ```
</CodeGroup>

Some wrapped program types support additional methods that can help resolve compatibility issues, such as differences in
qubit indexing rules across frontends. For instance, the `remove_idle_qubits()` method allows you to remap qubit indices
without having to reconstruct the circuit:

<CodeGroup>
  ```python Input theme={null}
  qprogram.remove_idle_qubits()

  circuit_drawer(qprogram.program)
  ```

  ```text Output theme={null}
  T  : │  0  │  1  │  2  │
        ┌───┐
  q0 : ─┤ H ├───●─────────
        └───┘   │
              ┌─┴─┐
  q1 : ───────┤ X ├───●───
              └───┘   │
                    ┌─┴─┐
  q2 : ─────────────┤ X ├─
                    └───┘
  T  : │  0  │  1  │  2  │
  ```
</CodeGroup>

Using the `remove_idle_qubits` method, we mapped the qubit indices of the
Amazon Braket circuit from `[0, 2, 4]` to the contiguous `[0, 1, 2]` convention.
