Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

468 469 #2

Open
wants to merge 26 commits into
base: 473
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/notebook-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install nbconvert nbclient ipykernel
pip install -e .

- name: Run Jupyter Notebooks
run: |
Expand Down
42 changes: 41 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,44 @@
- **Purpose**: Visualizes the quantum circuit.
- **Usage**: Provides a graphical representation of the quantum circuit for better understanding.
- **Note**: Just a pass through function, will use underlying libraries
method for drawing circuit.
method for drawing circuit.

## `apply_rx_gate(self, qubit_index, angle)`
- **Purpose**: Applies a rotation around the X-axis to a specified qubit with an optional parameter for optimization.
- **Parameters**:
- `qubit_index` (int): Index of the qubit.
- `angle` (str or float): Angle in radians for the rotation. Can be a static value or a parameter name for optimization.
- **Usage**: Used to rotate a qubit around the X-axis, often in parameterized quantum circuits for variational algorithms.

## `apply_ry_gate(self, qubit_index, angle)`
- **Purpose**: Applies a rotation around the Y-axis to a specified qubit with an optional parameter for optimization.
- **Parameters**:
- `qubit_index` (int): Index of the qubit.
- `angle` (str or float): Angle in radians for the rotation. Can be a static value or a parameter name for optimization.
- **Usage**: Used to rotate a qubit around the Y-axis in parameterized circuits, aiding in the creation of complex quantum states.

## `apply_rz_gate(self, qubit_index, angle)`
- **Purpose**: Applies a rotation around the Z-axis to a specified qubit with an optional parameter for optimization.
- **Parameters**:
- `qubit_index` (int): Index of the qubit.
- `angle` (str or float): Angle in radians for the rotation. Can be a static value or a parameter name for optimization.
- **Usage**: Utilized in parameterized quantum circuits to modify the phase of a qubit state during optimization.

## `execute_circuit(self, parameter_values=None)`
- **Purpose**: Executes the quantum circuit with the ability to bind specific parameter values if provided.
- **Parameters**:
- `parameter_values` (dict, optional): A dictionary where keys are parameter names and values are the numerical values to bind.
- **Usage**: Enables the execution of parameterized circuits by binding parameter values, facilitating optimization processes.

## `bind_parameters(self, parameter_values)`
- **Purpose**: Binds numerical values to the parameters of the quantum circuit, allowing for dynamic updates during optimization.
- **Parameters**:
- `parameter_values` (dict): A dictionary with parameter names as keys and numerical values to bind.
- **Usage**: Essential for optimization loops where parameters are adjusted based on cost function evaluations.

## `_handle_parameter(self, param_name)`
- **Purpose**: Internal function to manage parameter registration.
- **Parameters**:
- `param_name` (str): The name of the parameter to handle.
- **Usage**: Automatically invoked when applying parameterized gates to keep track of parameters efficiently.

144 changes: 144 additions & 0 deletions examples/Optimization_Example.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
{
"nbformat": 4,
"nbformat_minor": 2,
"cells": [
{
"cell_type": "code",
"source": [
"# pip install git+https://github.com/apache/mahout.git@main"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "lNDTZhztd2dp",
"outputId": "ea3b9e41-43a8-44e7-9daf-e62e71d93143"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Quantum Circuit Parameterization and Optimization\n",
"\n",
"In this notebook, we'll explore how to create parameterized quantum circuits using the QuMat framework. This feature allows us to bind values to parameters at execution time, paving the way for optimization tasks in quantum computing.\n",
"\n",
"## Overview\n",
"\n",
"1. **Parameter Handling**: We will use symbols to represent parameters in quantum gates, allowing these parameters to be updated during optimization.\n",
"2. **Circuit Execution with Binding**: We will bind parameter values to a circuit prior to its execution, a critical step in parameter optimization routines.\n",
"3. **Simple Optimization Loop**: We'll implement a basic optimization loop that updates parameters based on a cost function.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 1: Setting Up\n",
"\n",
"We start by setting up the backend configuration and initializing the QuMat framework. This framework interfaces with quantum computing libraries like Qiskit or Cirq to manage the underlying quantum computations.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from qumat.qumat import QuMat\n",
"\n",
"# Configure the backend to use Qiskit with a simulator\n",
"backend_config = {\n",
" 'backend_name': 'qiskit',\n",
" 'backend_options': {'simulator_type': 'qasm_simulator', 'shots': 1024}\n",
"}\n",
"\n",
"# Create an instance of QuMat\n",
"qumat_instance = QuMat(backend_config)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 2: Creating a Parameterized Quantum Circuit\n",
"\n",
"We create a simple quantum circuit with one qubit and apply parameterized RX, RY, and RZ gates. The parameters will be defined symbolically, allowing them to be replaced with actual values during execution.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Create a quantum circuit with 1 qubit\n",
"qumat_instance.create_empty_circuit(1)\n",
"\n",
"# Apply parameterized RX, RY, and RZ gates\n",
"qumat_instance.apply_rx_gate(0, 'theta')\n",
"qumat_instance.apply_ry_gate(0, 'phi')\n",
"qumat_instance.apply_rz_gate(0, 'lambda')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 3: Running the Optimization Loop\n",
"\n",
"We'll iterate over a simple optimization loop, updating the parameter values for each iteration. This example does not feature a sophisticated cost function, but in practical scenarios, you'd compute and leverage a cost function to guide these updates.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Example optimization loop\n",
"\n",
"# Initial parameter values\n",
"current_parameters = {'theta': 0, 'phi': 0, 'lambda': 0}\n",
"\n",
"# Define a simple placeholder cost function\n",
"def your_cost_function():\n",
" # Placeholder: replace with a real function in actual applications\n",
" return 0\n",
"\n",
"# Run the optimization loop\n",
"for iteration in range(10): # Reduced iteration count for brevity\n",
" cost = your_cost_function() # Evaluate the cost function\n",
"\n",
" # Update parameter(s) based on some optimization logic\n",
" # This is a placeholder update mechanism\n",
" current_parameters['theta'] += 0.1\n",
" current_parameters['phi'] += 0.1\n",
" current_parameters['lambda'] += 0.1\n",
"\n",
" # Execute the circuit with the updated parameters\n",
" result = qumat_instance.execute_circuit(parameter_values=current_parameters)\n",
"\n",
" print(f\"Iteration {iteration}, Result: {result}, Parameters: {current_parameters}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"This notebook demonstrates how to effectively handle parameters within quantum circuits using the QuMat framework. Although the optimization loop here uses a placeholder mechanism, it highlights how parameterized circuits can be used in iterative optimization processes, often encountered in variational quantum algorithms.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
}
}
}
2 changes: 1 addition & 1 deletion examples/Simple_Example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
{
"cell_type": "code",
"source": [
"!pip install git+https://github.com/apache/mahout.git@main"
"# pip install git+https://github.com/apache/mahout.git@main"
],
"metadata": {
"colab": {
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ qiskit = "^0.45.1"
qiskit-aer = "^0.13.2"
cirq = "^1.3.0"
amazon-braket-sdk = "^1.10.0"
sympy = "^1.9"

[tool.poetry.dev-dependencies]
pytest = "^8.1.1"
Expand Down
22 changes: 18 additions & 4 deletions qumat/amazon_braket_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# limitations under the License.
#
from braket.aws import AwsQuantumSimulator, AwsDevice
from braket.circuits import Circuit
from braket.circuits import Circuit, FreeParameter

def initialize_backend(backend_config):
backend_options = backend_config['backend_options']
Expand Down Expand Up @@ -70,13 +70,27 @@ def draw_circuit(circuit):
print(circuit)

def apply_rx_gate(circuit, qubit_index, angle):
circuit.rx(qubit_index, angle)
if isinstance(angle, (int, float)):
circuit.rx(qubit_index, angle)
else:
param = FreeParameter(angle)
circuit.rx(qubit_index, param)


def apply_ry_gate(circuit, qubit_index, angle):
circuit.ry(qubit_index, angle)
if isinstance(angle, (int, float)):
circuit.ry(qubit_index, angle)
else:
param = FreeParameter(angle)
circuit.ry(qubit_index, param)


def apply_rz_gate(circuit, qubit_index, angle):
circuit.rz(qubit_index, angle)
if isinstance(angle, (int, float)):
circuit.rz(qubit_index, angle)
else:
param = FreeParameter(angle)
circuit.rz(qubit_index, param)

def apply_u_gate(circuit, qubit_index, theta, phi, lambd):
circuit.rx(qubit_index, theta)
Expand Down
29 changes: 22 additions & 7 deletions qumat/cirq_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.
#
import cirq
import sympy

def initialize_backend(backend_config):
# Assuming 'simulator_type' specifies the type of simulator in Cirq
Expand Down Expand Up @@ -64,27 +65,41 @@ def apply_pauli_z_gate(circuit, qubit_index):
qubit = cirq.LineQubit(qubit_index)
circuit.append(cirq.Z(qubit))


def execute_circuit(circuit, backend, backend_config):
# This is a simplified example. You'll need to adjust this based on how you're handling backend configuration.
circuit.append(cirq.measure(*circuit.all_qubits(), key='result'))
# Ensure measurement is added to capture the results
if not circuit.has_measurements():
circuit.append(cirq.measure(*circuit.all_qubits(), key='result'))
simulator = cirq.Simulator()
result = simulator.run(circuit, repetitions=backend_config['backend_options'].get('shots', 1))
return result.histogram(key='result')
parameter_values = backend_config.get('parameter_values', None)
if parameter_values:
# Convert parameter_values to applicable resolvers
res = [cirq.ParamResolver(parameter_values)]
results = simulator.run_sweep(circuit, repetitions=backend_config['backend_options'].get('shots', 1), params=res)
return [result.histogram(key='result') for result in results]
else:
result = simulator.run(circuit, repetitions=backend_config['backend_options'].get('shots', 1))
return [result.histogram(key='result')]

def draw_circuit(circuit):
print(circuit)

def apply_rx_gate(circuit, qubit_index, angle):
param = sympy.Symbol(angle) if isinstance(angle, str) else angle
qubit = cirq.LineQubit(qubit_index)
circuit.append(cirq.rx(angle).on(qubit))
circuit.append(cirq.rx(param).on(qubit))


def apply_ry_gate(circuit, qubit_index, angle):
param = sympy.Symbol(angle) if isinstance(angle, str) else angle
qubit = cirq.LineQubit(qubit_index)
circuit.append(cirq.ry(angle).on(qubit))
circuit.append(cirq.ry(param).on(qubit))


def apply_rz_gate(circuit, qubit_index, angle):
param = sympy.Symbol(angle) if isinstance(angle, str) else angle
qubit = cirq.LineQubit(qubit_index)
circuit.append(cirq.rz(angle).on(qubit))
circuit.append(cirq.rz(param).on(qubit))

def apply_u_gate(circuit, qubit_index, theta, phi, lambd):
qubit = cirq.LineQubit(qubit_index)
Expand Down
36 changes: 24 additions & 12 deletions qumat/qiskit_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,24 @@ def apply_pauli_z_gate(circuit, qubit_index):
circuit.z(qubit_index)

def execute_circuit(circuit, backend, backend_config):
# Transpile the circuit
circuit.measure_all()
transpiled_circuit = qiskit.compiler.transpile(circuit, backend)

# Execute the transpiled circuit on the backend
job = qiskit.execute(transpiled_circuit, backend,
shots=backend_config['backend_options']['shots'])
result = job.result()
return result.get_counts(transpiled_circuit)
# Add measurements if they are not already present
if not circuit.cregs:
circuit.measure_all()

# Ensure the circuit is parameterized properly
if circuit.parameters:
# Parse the global parameter configuration
parameter_bindings = {param: backend_config['parameter_values'][str(param)] for param in circuit.parameters}
transpiled_circuit = qiskit.transpile(circuit, backend)
qobj = qiskit.assemble(transpiled_circuit, parameter_binds=[parameter_bindings], shots=backend_config['backend_options']['shots'])
job = backend.run(qobj)
result = job.result()
return result.get_counts()
else:
transpiled_circuit = qiskit.transpile(circuit, backend)
job = qiskit.execute(transpiled_circuit, backend, shots=backend_config['backend_options']['shots'])
result = job.result()
return result.get_counts()

# placeholder method for use in the testing suite
def get_final_state_vector(circuit, backend, backend_config):
Expand All @@ -92,13 +101,16 @@ def draw_circuit(circuit):
print(circuit.draw())

def apply_rx_gate(circuit, qubit_index, angle):
circuit.rx(angle, qubit_index)
param = qiskit.circuit.Parameter(angle) if isinstance(angle, str) else angle
circuit.rx(param, qubit_index)

def apply_ry_gate(circuit, qubit_index, angle):
circuit.ry(angle, qubit_index)
param = qiskit.circuit.Parameter(angle) if isinstance(angle, str) else angle
circuit.ry(param, qubit_index)

def apply_rz_gate(circuit, qubit_index, angle):
circuit.rz(angle, qubit_index)
param = qiskit.circuit.Parameter(angle) if isinstance(angle, str) else angle
circuit.rz(param, qubit_index)

def apply_u_gate(circuit, qubit_index, theta, phi, lambd):
# Apply the U gate directly with specified parameters
Expand Down
Loading