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

Zqs 920 loss function wrapper #33

Merged
merged 8 commits into from
Jan 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Added loss function wrapper.
  • Loading branch information
mstechly committed Jan 19, 2022
commit 761cff71f973095b19f5b499951611cd50f9d412
2 changes: 1 addition & 1 deletion docs/examples/gradient_descent_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

def gradient_descent_optimizer(
init_params: np.ndarray,
loss_function: Callable,
loss_function: Callable[[np.ndarray], float],
n_iters: int,
learning_rate: float = 0.1,
full_gradient_function: Optional[Callable] = None,
Expand Down
100 changes: 51 additions & 49 deletions docs/examples/orqviz_tutorial_qiskit.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"\n",
"| __Software__ | __Version__ |\n",
"| ---------------- | ----------- |\n",
"| qiskit | 0.19.1 |\n",
"| qiskit | 0.34.1 |\n",
"| numpy | 1.21.4 |\n",
"| matplotlib | 3.5.1 |\n",
"\n",
Expand All @@ -38,10 +38,10 @@
"import matplotlib.pyplot as plt\n",
"from functools import partial\n",
"\n",
"from qiskit import *\n",
"from qiskit.opflow import CircuitStateFn, StateFn, Gradient\n",
"from qiskit.circuit import ParameterVector, QuantumCircuit\n",
"from qiskit.quantum_info import SparsePauliOp"
"from qiskit.quantum_info import SparsePauliOp\n",
"from qiskit.compiler import transpile\n",
"from qiskit.opflow.state_fns.circuit_state_fn import CircuitStateFn"
]
},
{
Expand Down Expand Up @@ -152,7 +152,7 @@
"metadata": {},
"outputs": [],
"source": [
"def calculate_energy(transpiled_circuit, param_vector, parameters):\n",
"def calculate_energy(parameters, transpiled_circuit, param_vector):\n",
" \"\"\"\n",
" Function that receives parameters for a quantum circuit, as well as specifications for the circuit depth,\n",
" and returns the energy over a previously defined Hamiltonian H.\n",
Expand Down Expand Up @@ -198,9 +198,11 @@
"metadata": {},
"outputs": [],
"source": [
"from orqviz.loss_function import LossFunctionWrapper\n",
"\n",
"initial_parameters = np.array([ 0.5896798 , -0.53733806])\n",
"\n",
"calculate_energy_wrapper = partial(calculate_energy, transpiled_circuit, param_vector)"
"loss_function = LossFunctionWrapper(calculate_energy, transpiled_circuit=transpiled_circuit, param_vector=param_vector)"
]
},
{
Expand Down Expand Up @@ -229,7 +231,7 @@
}
],
"source": [
"calculate_energy_wrapper(initial_parameters)"
"loss_function(initial_parameters)"
]
},
{
Expand Down Expand Up @@ -282,7 +284,7 @@
"from orqviz.gradients import calculate_full_gradient\n",
"\n",
"def gradient_function(parameters):\n",
" return calculate_full_gradient(parameters, calculate_energy_wrapper, stochastic=False, eps=1e-3)"
" return calculate_full_gradient(parameters, loss_function, stochastic=False, eps=1e-3)"
]
},
{
Expand Down Expand Up @@ -315,7 +317,7 @@
"source": [
"n_iters = 50\n",
"parameter_trajectory, losses = gradient_descent_optimizer(initial_parameters, \n",
" calculate_energy_wrapper, \n",
" loss_function, \n",
" n_iters, \n",
" learning_rate=0.2, \n",
" full_gradient_function=gradient_function)\n",
Expand Down Expand Up @@ -345,7 +347,7 @@
{
"data": {
"text/plain": [
"0.019706586358939626"
"0.019706586358939848"
]
},
"execution_count": 11,
Expand Down Expand Up @@ -410,7 +412,7 @@
"end_points = (-0.5, 1.5)\n",
"\n",
"interpolation_result = perform_1D_interpolation(initial_parameters, final_parameters, \n",
" calculate_energy_wrapper, end_points=end_points)\n",
" loss_function, end_points=end_points)\n",
"\n",
"plot_1D_interpolation_result(interpolation_result, label=\"linear interpolation\", color=\"gray\")\n",
"\n",
Expand Down Expand Up @@ -461,7 +463,7 @@
"dir1 = np.array([1., 0.])\n",
"dir2 = np.array([0., 1.])\n",
"\n",
"scan_2D_result = perform_2D_scan(final_parameters, calculate_energy_wrapper, \n",
"scan_2D_result = perform_2D_scan(final_parameters, loss_function, \n",
" direction_x=dir1, direction_y=dir2, n_steps_x=40)\n",
"\n",
"plot_2D_scan_result(scan_2D_result)"
Expand Down Expand Up @@ -496,7 +498,7 @@
"outputs": [],
"source": [
"pca = get_pca(parameter_trajectory)\n",
"scan_pca_result = perform_2D_pca_scan(pca, calculate_energy_wrapper, n_steps_x=40)"
"scan_pca_result = perform_2D_pca_scan(pca, loss_function, n_steps_x=40)"
]
},
{
Expand Down Expand Up @@ -560,7 +562,7 @@
"metadata": {},
"outputs": [],
"source": [
"scan_pca_result2 = perform_2D_pca_scan(pca, calculate_energy_wrapper, n_steps_x=50, offset=8)"
"scan_pca_result2 = perform_2D_pca_scan(pca, loss_function, n_steps_x=50, offset=8)"
]
},
{
Expand Down Expand Up @@ -622,10 +624,10 @@
"\n",
"initial_parameters2 = np.array([ 1.12840278, -1.85964912, -1.1847599 , 1.27278466])\n",
"\n",
"calculate_energy_wrapper2 = partial(calculate_energy, transpiled_circuit2, param_vector)\n",
"loss_function2 = LossFunctionWrapper(calculate_energy, transpiled_circuit=transpiled_circuit2, param_vector=param_vector)\n",
"\n",
"def gradient_function2(parameters):\n",
" return calculate_full_gradient(parameters, calculate_energy_wrapper2, stochastic=False, eps=1e-3)"
" return calculate_full_gradient(parameters, loss_function2, stochastic=False, eps=1e-3)"
]
},
{
Expand All @@ -647,7 +649,7 @@
"source": [
"n_iters = 50\n",
"parameter_trajectory2, losses2 = gradient_descent_optimizer(initial_parameters2, \n",
" calculate_energy_wrapper2, \n",
" loss_function2, \n",
" n_iters, \n",
" learning_rate=0.2, \n",
" full_gradient_function=gradient_function2)\n",
Expand Down Expand Up @@ -698,7 +700,7 @@
{
"data": {
"text/plain": [
"0.003257722988063705"
"0.003257722988059042"
]
},
"execution_count": 24,
Expand Down Expand Up @@ -728,8 +730,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 32.8 s, sys: 164 ms, total: 33 s\n",
"Wall time: 33 s\n"
"CPU times: user 27.4 s, sys: 456 ms, total: 27.8 s\n",
"Wall time: 28.4 s\n"
]
},
{
Expand All @@ -749,7 +751,7 @@
"%%time\n",
"\n",
"pca2 = get_pca(parameter_trajectory2)\n",
"scan_pca_result3 = perform_2D_pca_scan(pca2, calculate_energy_wrapper2, n_steps_x=50, offset=2)\n",
"scan_pca_result3 = perform_2D_pca_scan(pca2, loss_function2, n_steps_x=50, offset=2)\n",
"\n",
"fig, ax = plt.subplots()\n",
"plot_pca_landscape(scan_pca_result3, pca2, fig=fig, ax=ax)\n",
Expand All @@ -776,8 +778,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 33.3 s, sys: 180 ms, total: 33.4 s\n",
"Wall time: 33.4 s\n"
"CPU times: user 22.3 s, sys: 223 ms, total: 22.5 s\n",
"Wall time: 22.7 s\n"
]
},
{
Expand All @@ -796,7 +798,7 @@
"source": [
"%%time\n",
"\n",
"scan_pca_result4 = perform_2D_pca_scan(pca2, calculate_energy_wrapper2, n_steps_x=50, offset=10)\n",
"scan_pca_result4 = perform_2D_pca_scan(pca2, loss_function2, n_steps_x=50, offset=10)\n",
"\n",
"fig, ax = plt.subplots()\n",
"plot_pca_landscape(scan_pca_result4, pca2, fig=fig, ax=ax)\n",
Expand Down Expand Up @@ -849,15 +851,15 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 646 ms, sys: 4.19 ms, total: 650 ms\n",
"Wall time: 648 ms\n"
"CPU times: user 345 ms, sys: 7.59 ms, total: 352 ms\n",
"Wall time: 351 ms\n"
]
}
],
"source": [
"%%time\n",
"hessian1 = get_Hessian(params=final_parameters, loss_function=calculate_energy_wrapper, eps=1e-3)\n",
"hessian2 = get_Hessian(params=final_parameters2, loss_function=calculate_energy_wrapper2, eps=1e-3)"
"hessian1 = get_Hessian(params=final_parameters, loss_function=loss_function, eps=1e-3)\n",
"hessian2 = get_Hessian(params=final_parameters2, loss_function=loss_function2, eps=1e-3)"
]
},
{
Expand Down Expand Up @@ -929,8 +931,8 @@
"metadata": {},
"outputs": [],
"source": [
"hessian1_eigvec_scans = perform_1D_hessian_eigenvector_scan(hessian1, calculate_energy_wrapper, n_points=31)\n",
"hessian2_eigvec_scans = perform_1D_hessian_eigenvector_scan(hessian2, calculate_energy_wrapper2, n_points=31)"
"hessian1_eigvec_scans = perform_1D_hessian_eigenvector_scan(hessian1, loss_function, n_points=31)\n",
"hessian2_eigvec_scans = perform_1D_hessian_eigenvector_scan(hessian2, loss_function2, n_points=31)"
]
},
{
Expand Down Expand Up @@ -1006,20 +1008,20 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 42.8 s, sys: 369 ms, total: 43.2 s\n",
"Wall time: 42.9 s\n"
"CPU times: user 28.3 s, sys: 298 ms, total: 28.6 s\n",
"Wall time: 28.5 s\n"
]
}
],
"source": [
"%%time\n",
"scale_factor = np.pi # how much to scan in the direction? The eigenvectors are normalized.\n",
"\n",
"scan_hess2_low = perform_2D_scan(parameter_trajectory2[-1], calculate_energy_wrapper2, \n",
"scan_hess2_low = perform_2D_scan(parameter_trajectory2[-1], loss_function2, \n",
" direction_x=hessian2.eigenvectors[0]*scale_factor, \n",
" direction_y=hessian2.eigenvectors[1]*scale_factor,\n",
" n_steps_x=40)\n",
"scan_hess2_high = perform_2D_scan(parameter_trajectory2[-1], calculate_energy_wrapper2, \n",
"scan_hess2_high = perform_2D_scan(parameter_trajectory2[-1], loss_function2, \n",
" direction_x=hessian2.eigenvectors[-1]*scale_factor, \n",
" direction_y=hessian2.eigenvectors[-2]*scale_factor,\n",
" n_steps_x=40)\n",
Expand Down Expand Up @@ -1074,7 +1076,7 @@
"n_iters = 50\n",
"initial_parameters3 = initial_parameters2 + np.array([-1.7, -2.3, -3.1, 2.8])\n",
"\n",
"parameter_trajectory3, losses3 = gradient_descent_optimizer(initial_parameters3, calculate_energy_wrapper2, n_iters, \n",
"parameter_trajectory3, losses3 = gradient_descent_optimizer(initial_parameters3, loss_function2, n_iters, \n",
" learning_rate=0.2, full_gradient_function=gradient_function2)\n",
"final_parameters3 = parameter_trajectory3[-1]\n",
"\n",
Expand Down Expand Up @@ -1115,16 +1117,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 31.1 s, sys: 284 ms, total: 31.4 s\n",
"Wall time: 31.2 s\n"
"CPU times: user 25.6 s, sys: 433 ms, total: 26.1 s\n",
"Wall time: 26.5 s\n"
]
}
],
"source": [
"%%time\n",
"\n",
"pca3 = get_pca(np.append(parameter_trajectory2, parameter_trajectory3, axis=0))\n",
"scan_pca_result5 = perform_2D_pca_scan(pca3, calculate_energy_wrapper2, n_steps_x=50, offset=3)\n",
"scan_pca_result5 = perform_2D_pca_scan(pca3, loss_function2, n_steps_x=50, offset=3)\n",
"\n",
"fig, ax = plt.subplots()\n",
"plot_pca_landscape(scan_pca_result5, pca3, fig=fig, ax=ax)\n",
Expand Down Expand Up @@ -1170,7 +1172,7 @@
"initial_chain = Chain(np.linspace(final_parameters2, final_parameters3, num=10))\n",
"initial_path = ChainPath(initial_chain)\n",
"\n",
"all_chains = run_NEB(initial_chain, calculate_energy_wrapper2, \n",
"all_chains = run_NEB(initial_chain, loss_function2, \n",
" n_iters=100, eps=1e-3, learning_rate=0.1)\n",
"trained_chain = all_chains[-1]\n",
"trained_path = ChainPath(trained_chain)"
Expand All @@ -1196,8 +1198,8 @@
}
],
"source": [
"linear_loss = initial_path.evaluate_points_on_path(50, calculate_energy_wrapper2)\n",
"trained_loss = trained_path.evaluate_points_on_path(50, calculate_energy_wrapper2)\n",
"linear_loss = initial_path.evaluate_points_on_path(50, loss_function2)\n",
"trained_loss = trained_path.evaluate_points_on_path(50, loss_function2)\n",
"\n",
"plt.plot(np.linspace(0,1,50), linear_loss, label=\"Linear Interpolation\")\n",
"plt.plot(np.linspace(0,1,50), trained_loss, label=\"Optimized Path\")\n",
Expand All @@ -1224,7 +1226,7 @@
"outputs": [],
"source": [
"pca_from_chain = get_pca(trained_chain.pivots)\n",
"pca_chain_scan = perform_2D_pca_scan(pca_from_chain, calculate_energy_wrapper2, n_steps_x=40, offset=3)"
"pca_chain_scan = perform_2D_pca_scan(pca_from_chain, loss_function2, n_steps_x=40, offset=3)"
]
},
{
Expand Down Expand Up @@ -1312,8 +1314,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 32.9 s, sys: 313 ms, total: 33.2 s\n",
"Wall time: 33 s\n"
"CPU times: user 22.2 s, sys: 238 ms, total: 22.4 s\n",
"Wall time: 22.3 s\n"
]
}
],
Expand All @@ -1323,7 +1325,7 @@
"wrapped_parameter_trajectory3 = relative_periodic_trajectory_wrap(final_parameters2, parameter_trajectory3)\n",
"\n",
"pca3 = get_pca(np.append(parameter_trajectory2, wrapped_parameter_trajectory3, axis=0))\n",
"scan_pca_result5 = perform_2D_pca_scan(pca3, calculate_energy_wrapper2, n_steps_x=50, offset=3)\n",
"scan_pca_result5 = perform_2D_pca_scan(pca3, loss_function2, n_steps_x=50, offset=3)\n",
"\n",
"fig, ax = plt.subplots()\n",
"plot_pca_landscape(scan_pca_result5, pca3, fig=fig, ax=ax)\n",
Expand Down Expand Up @@ -1370,12 +1372,12 @@
"initial_chain2 = Chain(np.linspace(final_parameters2, wrapped_final_parameters3, num=10))\n",
"initial_path2 = ChainPath(initial_chain2)\n",
"###\n",
"all_chains2 = run_NEB(initial_chain2, calculate_energy_wrapper2, n_iters=100, eps=1e-3, learning_rate=0.1)\n",
"all_chains2 = run_NEB(initial_chain2, loss_function2, n_iters=100, eps=1e-3, learning_rate=0.1)\n",
"trained_chain2 = all_chains2[-1]\n",
"trained_path2 = ChainPath(trained_chain2)\n",
"###\n",
"linear_loss2 = initial_path2.evaluate_points_on_path(50, calculate_energy_wrapper2)\n",
"trained_loss2 = trained_path2.evaluate_points_on_path(50, calculate_energy_wrapper2)\n",
"linear_loss2 = initial_path2.evaluate_points_on_path(50, loss_function2)\n",
"trained_loss2 = trained_path2.evaluate_points_on_path(50, loss_function2)\n",
"###\n",
"plt.plot(np.linspace(0,1,50), linear_loss2, label=\"Linear Interpolation\")\n",
"plt.plot(np.linspace(0,1,50), trained_loss2, label=\"Optimized Path\")\n",
Expand All @@ -1386,7 +1388,7 @@
"plt.show()\n",
"###\n",
"pca_from_chain2 = get_pca(trained_chain2.pivots)\n",
"pca_chain_scan2 = perform_2D_pca_scan(pca_from_chain2, calculate_energy_wrapper2, n_steps_x=40, offset=3)\n",
"pca_chain_scan2 = perform_2D_pca_scan(pca_from_chain2, loss_function2, n_steps_x=40, offset=3)\n",
"###\n",
"fig, ax = plt.subplots()\n",
"plot_pca_landscape(pca_chain_scan2, pca_from_chain2, fig=fig, ax=ax)\n",
Expand Down
40 changes: 40 additions & 0 deletions src/orqviz/loss_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import Callable, Optional
import numpy as np
import time


def _calculate_new_average(
previous_average: Optional[float], count: int, new_value: float
) -> float:
if previous_average is None:
return new_value
else:
return (count * previous_average + new_value) / (count + 1)


class LossFunctionWrapper:
def __init__(self, loss_function: Callable, *args, **kwargs):
def wrapped_loss_function(params):
return loss_function(params, *args, **kwargs)

self.loss_function = wrapped_loss_function
self.call_count = 0
self.average_call_time = None
self.min_value = None

def __call__(self, params: np.ndarray) -> float:
start_time = time.perf_counter()
value = self.loss_function(params)
total_time = time.perf_counter() - start_time
self.average_call_time = _calculate_new_average(
self.average_call_time, self.call_count, total_time
)
self.call_count += 1
if self.min_value is None or value < self.min_value:
self.min_value = value
return value

def reset(self) -> None:
self.call_count = 0
self.average_call_time = None
self.min_value = None
Loading