Skip to content

Conversation

@Royschenk
Copy link

Adding Circuit Optimization (CO) Mini-Apps, including VQA + CO, Circuit Cutting + CO, QML-training + CO, and base VQA (VQE) + two base CO (Dynamic Decoupling) Mini-Apps

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @Royschenk, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new collection of quantum mini-applications focused on circuit optimization, specifically leveraging Dynamic Decoupling (DD) techniques. These mini-apps are designed to evaluate the performance and noise resilience of quantum algorithms like VQA, Circuit Cutting, and QML training when integrated with DD, providing a robust framework for benchmarking middleware in hybrid quantum-classical environments.

Highlights

  • New Mini-App Suite: A comprehensive suite of 'Circuit Optimization' mini-apps has been added, designed to explore and benchmark the integration of Dynamic Decoupling (DD) techniques across various quantum computing workflows.
  • Dynamic Decoupling Integration: Dynamic Decoupling (DD) techniques are now integrated into existing and new quantum algorithms, including VQA (VQE), Circuit Cutting, and QML training, to evaluate their effectiveness in mitigating noise.
  • Performance Benchmarking: The newly added mini-apps provide a robust framework for benchmarking middleware performance (specifically Pilot-Quantum) and assessing the impact of DD under realistic noisy quantum conditions.
  • Multi-Framework Support: The mini-apps demonstrate versatility by implementing solutions using both Qiskit and PennyLane, showcasing cross-framework applicability for circuit optimization techniques.
  • Comprehensive Documentation and Dependencies: Detailed documentation in README.md and a requirements.txt file have been added to facilitate easy setup, understanding, and execution of the new mini-apps.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces several new mini-applications demonstrating circuit optimization techniques, specifically Dynamic Decoupling (DD), in conjunction with other quantum computing workflows like Circuit Cutting, QML training, and VQE. The mini-apps leverage the Pilot-Quantum middleware for distributed execution and include noise simulation. The code provides valuable examples for benchmarking these combined techniques. Several areas for improvement were identified, primarily related to code maintainability (hardcoded parameters, redundant code, unused variables/imports), correctness (inconsistent DD application logic, fragile dynamic patching in the QML app, potentially misleading JSD metric in the VQE app), and clarity (lack of comments for complex workarounds). Addressing these points will enhance the robustness, reliability, and clarity of the mini-apps.

Comment on lines +105 to +131
def patch_copula_ansatz_with_dd():
"""Patch the original copula_ansatz with the DD-enhanced version."""
try:
import qugen.main.generator.quantum_circuits.discrete_generator_pennylane as qgen_circuits

# Check if copula_ansatz exists in the module
if hasattr(qgen_circuits, 'copula_ansatz'):
# Store original function
original_copula_ansatz = qgen_circuits.copula_ansatz

# Patch with DD version
qgen_circuits.copula_ansatz = dd_copula_ansatz

print("[INFO] Successfully patched copula_ansatz with DD sequence")
return original_copula_ansatz
else:
print("[WARNING] copula_ansatz not found in qgen_circuits module")
# Create and patch a new function if it doesn't exist
qgen_circuits.copula_ansatz = dd_copula_ansatz
print("[INFO] Created new copula_ansatz function with DD sequence")
return regular_copula_ansatz

except ImportError as e:
print(f"[ERROR] Cannot import from qugen.main.generator.quantum_circuits: {e}")
print("[INFO] Will use local implementation of copula_ansatz")
# No patching performed, will use our local implementation
return regular_copula_ansatz

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Dynamically patching functions in external libraries (qugen) is a fragile approach. It creates a strong dependency on the internal structure and naming of qugen. If qugen is updated or refactored, this patching mechanism is likely to break. A more robust approach would be to define the ansatz function directly within this mini-app or pass the desired ansatz function (either DD-enabled or regular) as a parameter to the training logic, avoiding runtime modification of imported modules.

Comment on lines +465 to +485
def expvals_to_probs(expvals):
# Convert single-qubit Z expectations to probabilities of states
single_probs = [[(1 + ez)/2, (1 - ez)/2] for ez in expvals]

# Simplified approach: use product state approximation
n_states = 2**len(expvals)
all_probs = np.zeros(n_states)

for state_idx in range(n_states):
# Convert index to bit string
bitstring = format(state_idx, f'0{len(expvals)}b')

# Calculate probability of this bitstring
prob = 1.0
for q_idx, bit in enumerate(bitstring):
bit_val = int(bit)
prob *= single_probs[q_idx][bit_val]

all_probs[state_idx] = prob

return all_probs

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The JSD calculation uses a 'Simplified approach: use product state approximation' based on single-qubit Z expectation values. This approximation might not accurately reflect the true state fidelity or JSD, especially for entangled states, and could lead to misleading conclusions about DD effectiveness. For a Master's thesis, a more rigorous metric like fidelity or true JSD based on state vectors (if simulation allows) or full probability distributions might be necessary. If this approximation is used, its limitations should be clearly documented.

Comment on lines +120 to +125
else:
print("[WARNING] copula_ansatz not found in qgen_circuits module")
# Create and patch a new function if it doesn't exist
qgen_circuits.copula_ansatz = dd_copula_ansatz
print("[INFO] Created new copula_ansatz function with DD sequence")
return regular_copula_ansatz

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The fallback logic here (returning regular_copula_ansatz if patching fails) means that if the qugen import or attribute access fails, the experiment will silently run without DD, even if use_dd=True was intended. This can lead to misleading results. Consider raising a more prominent warning or error if the intended DD ansatz cannot be applied due to patching failure.

for d in range(1, depth + 1):
for i in range(0, n_qubits - 1, 2):
qml.CNOT(wires=[wires[i], wires[i+1]])
qml.QubitChannel(get_phase_damping_kraus(0.05), wires=wires[i+1])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The noise probability (0.05) is hardcoded within the ansatz function. It would be more flexible to pass the noise probability as a parameter to the ansatz function itself, allowing different noise levels to be tested without modifying the circuit definition.

Comment on lines +190 to +197
# Optional final DD
for q in range(num_qubits):
idle_time = current_layer - last_gate_layer[q]
if last_gate_layer[q] != -1 and idle_time > 1:
print(f"[DD] Final DD on qubit {q} at end (idle for {idle_time} layers)")
for gate in dd_sequence:
new_circuit.append(gate, [q])
new_circuit.barrier(q)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This loop for applying final DD is a duplicate of the logic inside the main loop (lines 177-185). The main loop already handles the last layer correctly. This final loop is redundant and can be removed.

Comment on lines +287 to +309
def vqe_ansatz(params: np.ndarray, wires: List[int], use_dd: bool = False, dd_inserted: bool = False) -> None:
n_qubits = len(wires)
n_layers = params.shape[0]

# Apply parameterized rotations and entangling layers
for layer in range(n_layers):
# Rotation layer
param_width = params.shape[1]
for i in range(n_qubits):
if i*3 + 2 >= param_width:
break # prevent out-of-bounds access
qml.RY(params[layer, i*3], wires=wires[i])
qml.RZ(params[layer, i*3+1], wires=wires[i])
qml.RY(params[layer, i*3+2], wires=wires[i])

# Entangling layer
for i in range(n_qubits):
qml.CNOT(wires=[wires[i], wires[(i+1) % n_qubits]])

# Apply DD sequence if enabled and not already inserted
if use_dd and not dd_inserted:
#print(f"[DEBUG] Inserting DD sequence on wires {wires}")
apply_dd_sequence(wires)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The dd_inserted parameter in vqe_ansatz appears to be unused. Consider removing it.

Comment on lines +317 to +319
def apply_noise(wires: List[int], noise_level: float = 0.05) -> None:
for wire in wires:
qml.QubitChannel(get_phase_damping_kraus(noise_level), wires=wire)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The noise_level is hardcoded within the apply_noise function. It should be passed as a parameter to this function, likely originating from the calling context (run_combined_qchem_circuit_task).

for q in range(num_qubits):
if q in involved_qubits:
idle_time = current_layer - last_gate_layer[q]
if last_gate_layer[q] != -1 and idle_time > 1: #aanpassen naar 2

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment #aanpassen naar 2 suggests the condition idle_time > 1 should be idle_time > 2. Please ensure the code matches the intended logic.

Suggested change
if last_gate_layer[q] != -1 and idle_time > 1: #aanpassen naar 2
if last_gate_layer[q] != -1 and idle_time > 2:


# Get all results
results = self.executor.get_results(futures)
end_time = time.time()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The resources dictionary is hardcoded for each task submission. Consider making these resource requests configurable, especially num_gpus, as the availability and optimal allocation might vary across different cluster setups.

parameters={
'n_qubits': 2, # H2 molecule typically needs 2 qubits
'circuit_depth': 2,
'max_steps': 25, # Reduce for faster execution

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The default max_steps for VQE optimization is hardcoded to 25 in the OptimizedQChemDDMiniApp constructor parameters. Consider making this configurable via input parameters to the mini-app.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Royschenk - can you revert deletions happened in qml_training & quantum_simulation folders.

@pradeepmantha
Copy link
Collaborator

@Royschenk - also, do we need all the png & notebook files? or can they be created when we run the experiment scripts, if so lets delete them and keep only the necessary scripts.

Notebook files, if they are already scripted, then probably they are duplicates and can be removed from the repo.

@pradeepmantha
Copy link
Collaborator

And can you paste an example output execution of the scripts with fresh installation - so the scripts are manualy tested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants