Source code for mqt.bench.benchmarks.bmw_quark_copula
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License
# Copyright 2021 The QUARK Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Copula circuit from the generative modeling application in QUARK framework. https://github.com/QUARK-framework/QUARK."""
from __future__ import annotations
from math import comb
from typing import TYPE_CHECKING
from qiskit.circuit import ParameterVector, QuantumCircuit
from ._registry import register_benchmark
if TYPE_CHECKING:
from qiskit.circuit import Parameter
[docs]
@register_benchmark("bmw_quark_copula", description="Copula Circuit (QUARK)")
def create_circuit(num_qubits: int, depth: int = 2) -> QuantumCircuit:
"""Returns a Qiskit circuit based on the copula circuit architecture from the QUARK framework.
Arguments:
num_qubits: Number of qubits of the returned quantum circuit. Must be divisible by 2.
depth: Depth of the returned quantum circuit.
"""
if num_qubits % 2 != 0:
msg = "Number of qubits must be divisible by 2."
raise ValueError(msg)
n_registers = 2
n = num_qubits // n_registers
qc = QuantumCircuit(num_qubits)
# === Compute number of parameters ===
num_single_qubit_gates = depth * n_registers * n * 3
num_rxx_gates = depth * n_registers * comb(n, 2)
total_params = num_single_qubit_gates + num_rxx_gates
param_vector = ParameterVector("p", total_params)
param_index = 0
def get_param() -> Parameter:
nonlocal param_index
value = param_vector[param_index]
param_index += 1
return value
# === Initial Hadamards on first register ===
for q in range(n):
qc.h(q)
# === CNOTs to entangle registers ===
for q in range(n):
qc.cx(q, q + n)
qc.barrier()
# === Layered RZ-RX-RZ and RXX ===
for _ in range(depth):
# Apply RZ-RX-RZ to each qubit
for q in range(num_qubits):
qc.rz(get_param(), q)
qc.rx(get_param(), q)
qc.rz(get_param(), q)
# Intra-register RXX (full connectivity)
for reg in range(n_registers):
base = reg * n
for i in range(n):
for j in range(i + 1, n):
qc.rxx(get_param(), base + i, base + j)
qc.barrier()
qc.measure_all()
qc.name = "bmw_quark_copula"
return qc