Skip to content

Commit

Permalink
Merge pull request #472 from rawkintrevo/468-469
Browse files Browse the repository at this point in the history
MAHOUT-468 &  MAHOUT-469 Add parameter object and bind at execution
  • Loading branch information
andrewmusselman authored Nov 14, 2024
2 parents c8f1341 + a10bab4 commit c722868
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 27 deletions.
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

0 comments on commit c722868

Please sign in to comment.