Skip to content

Commit

Permalink
Merge pull request #95 from Zhaoyilunnn/master
Browse files Browse the repository at this point in the history
Add paramshift prototype
  • Loading branch information
beizhansl authored Oct 16, 2023
2 parents 96910f3 + 890a064 commit bed2034
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 11 deletions.
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ cmake_minimum_required(VERSION 3.14...3.22)

project(qfvm LANGUAGES CXX C)

set (CMAKE_BUILD_TYPE Release)
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "Build type not set - defaulting to Release")
set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CUDA_ARCHITECTURES 70;75;80;90)
if(SKBUILD)
Expand Down
7 changes: 4 additions & 3 deletions quafu/algorithms/ansatz.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def _build(self):


class QAOAAnsatz(Ansatz):
"""QAOA circuit"""
"""QAOA Ansatz"""

def __init__(self, hamiltonian: Hamiltonian, num_layers: int = 1):
"""Instantiate a QAOAAnsatz"""
Expand Down Expand Up @@ -93,10 +93,11 @@ def _build(self):
for i in range(self.num):
self.rx(i, self._beta[layer])

def update_params(self, beta: List[float], gamma: List[float]):
def update_params(self, params: List[float]):
"""Update parameters of QAOA circuit"""
# First build parameter list
assert len(beta) == self._num_layers and len(gamma) == self._num_layers
assert len(params) == 2 * self._num_layers
beta, gamma = params[: self._num_layers], params[self._num_layers :]
num_para_gates = len(self.parameterized_gates)
assert num_para_gates % self._num_layers == 0
self._beta, self._gamma = beta, gamma
Expand Down
8 changes: 4 additions & 4 deletions quafu/algorithms/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""Pre-build wrapper to calculate expectation value"""
import numpy as np

from typing import Optional
from typing import List, Optional
from quafu import QuantumCircuit
from quafu.simulators.simulator import simulate
from quafu.tasks.tasks import Task
Expand Down Expand Up @@ -68,7 +68,7 @@ def _run_simulation(self, observables: Hamiltonian):
).real
return expectation

def run(self, observables, *params):
def run(self, observables: Hamiltonian, params: List[float]):
"""Calculate estimation for given observables
Args:
Expand All @@ -83,8 +83,8 @@ def run(self, observables, *params):
"The number of qubits in the observables does not match the circuit"
)

if params[0] is not None:
self._circ.update_params(*params)
if params is not None:
self._circ.update_params(params)

if self._backend == "sim":
return self._run_simulation(observables)
Expand Down
54 changes: 54 additions & 0 deletions quafu/algorithms/optimizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# (C) Copyright 2023 Beijing Academy of Quantum Information Sciences
#
# 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.

import numpy as np
from typing import List
from quafu.algorithms import Estimator
from quafu.algorithms.hamiltonian import Hamiltonian


class ParamShift:
"""Parameter shift rule to calculate gradients"""

def __init__(self, estimator: Estimator) -> None:
self._est = estimator

def __call__(self, obs: Hamiltonian, params: List[float]):
"""Calculate gradients using paramshift.
Args:
estimator (Estimator): estimator to calculate expectation values
params (List[float]): params to optimize
"""
return self._grad(obs, params)

def _gen_param_shift_vals(self, params):
"""Given a param list with n values, replicate to 2*n param list"""
num_vals = len(params)
params = np.array(params)
offsets = np.identity(num_vals)
plus_params = params + offsets * np.pi / 2
minus_params = params - offsets * np.pi / 2
return plus_params.tolist() + minus_params.tolist()

def _grad(self, obs: Hamiltonian, params: List[float]):
shifted_params_lists = self._gen_param_shift_vals(params)

res = np.zeros(len(shifted_params_lists))
for i, shifted_params in enumerate(shifted_params_lists):
res[i] = self._est.run(obs, shifted_params)

n = len(res)
grads = (res[: n // 2] - res[n // 2 :]) / 2
return grads
4 changes: 1 addition & 3 deletions tests/quafu/algorithms/integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ def test_run(self):
ansatz.draw_circuit()

def cost_func(params, ham, estimator: Estimator):
beta = params[:num_layers]
gamma = params[num_layers:]
cost = estimator.run(ham, beta, gamma)
cost = estimator.run(ham, params)
return cost

est = Estimator(ansatz)
Expand Down
41 changes: 41 additions & 0 deletions tests/quafu/algorithms/optimizer_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# (C) Copyright 2023 Beijing Academy of Quantum Information Sciences
#
# 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.

import pytest
import sys
from quafu.algorithms.estimator import Estimator
from quafu.algorithms.hamiltonian import Hamiltonian
from quafu.algorithms.optimizer import ParamShift
from quafu.circuits.quantum_circuit import QuantumCircuit


class TestParamShift:
@pytest.mark.skipif(
sys.platform == "darwin", reason="Avoid error on MacOS arm arch."
)
def test_call(self):
ham = Hamiltonian.from_pauli_list([("ZZ", 1), ("XI", 1)])
circ = QuantumCircuit(2)
# circ.h(0)
# circ.h(1)
circ.rx(0, 0.5)
circ.cnot(0, 1)
circ.ry(1, 0.5)

params = [0.2, 0.6]
estimator = Estimator(circ)
grad = ParamShift(estimator)

grads = grad(ham, params)
print(grads)

0 comments on commit bed2034

Please sign in to comment.