Skip to content

Commit

Permalink
precommit fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ermalrrapaj committed May 9, 2024
1 parent 123f798 commit a9591bc
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 83 deletions.
2 changes: 1 addition & 1 deletion bqskit/ir/gates/composed/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

from bqskit.ir.gates.composed.controlled import ControlledGate
from bqskit.ir.gates.composed.daggergate import DaggerGate
from bqskit.ir.gates.composed.powergate import PowerGate
from bqskit.ir.gates.composed.embedded import EmbeddedGate
from bqskit.ir.gates.composed.frozenparam import FrozenParameterGate
from bqskit.ir.gates.composed.powergate import PowerGate
from bqskit.ir.gates.composed.tagged import TaggedGate
from bqskit.ir.gates.composed.vlg import VariableLocationGate

Expand Down
117 changes: 57 additions & 60 deletions bqskit/ir/gates/composed/powergate.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
"""This module implements the DaggerGate Class."""
from __future__ import annotations

import numpy as np
import numpy.typing as npt
import re

import numpy as np
import numpy.typing as npt

from bqskit.ir.gate import Gate
from bqskit.ir.gates.composed.daggergate import DaggerGate
from bqskit.ir.gates.composedgate import ComposedGate
from bqskit.ir.gates.constant.unitary import ConstantUnitaryGate
from bqskit.ir.gates.constant.identity import IdentityGate
from bqskit.ir.gates.constant.unitary import ConstantUnitaryGate
from bqskit.qis.unitary.differentiable import DifferentiableUnitary
from bqskit.ir.gates.composed.daggergate import DaggerGate
from bqskit.qis.unitary.unitary import RealVector
from bqskit.utils.typing import is_integer
from bqskit.qis.unitary.unitarymatrix import UnitaryMatrix
from bqskit.utils.docs import building_docs
from bqskit.utils.typing import is_integer


class PowerGate(
ComposedGate,
Expand All @@ -39,34 +40,35 @@ def __init__(self, gate: Gate, power: int = 1) -> None:
Args:
gate (Gate): The Gate to conjugate transpose.
power (integer): The power index for the PowerGate
power (integer): The power index for the PowerGate
"""
if not isinstance(gate, Gate):
raise TypeError('Expected gate object, got %s' % type(gate))

if not is_integer(power):
raise TypeError(
f'Expected integer for num_controls, got {type(power)}.',)

f'Expected integer for num_controls, got {type(power)}.',
)

self.gate = gate
self.power =power
self.power = power
self._name = 'Power(%s)' % gate.name
self._num_params = gate.num_params
self._num_qudits = gate.num_qudits
self._radixes = gate.radixes

# If input is a constant gate, we can cache the unitary.
if self.num_params == 0 and not building_docs():
self.utry = np.linalg.matrix_power(self.gate.get_unitary(),self.power)
self.utry = np.linalg.matrix_power(
self.gate.get_unitary(), self.power,
)

def get_unitary(self, params: RealVector = []) -> UnitaryMatrix:
"""Return the unitary for this gate, see :class:`Unitary` for more."""
if hasattr(self, 'utry'):
return self.utry

return np.linalg.matrix_power(self.gate.get_unitary(params),self.power)



return np.linalg.matrix_power(self.gate.get_unitary(params), self.power)

def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]:
"""
Expand All @@ -82,10 +84,8 @@ def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]:
if hasattr(self, 'utry'):
return np.array([])

_, grad = self.get_unitary_and_grad(params)
_, grad = self.get_unitary_and_grad(params)
return grad



def get_unitary_and_grad(
self,
Expand All @@ -98,70 +98,67 @@ def get_unitary_and_grad(
"""
if hasattr(self, 'utry'):
return self.utry, np.array([])

if self.power == 0:
return IdentityGate(radixes=self.gate.radixes).get_unitary(), 0*IdentityGate(radixes=self.gate.radixes).get_unitary()
#powers = {0: IdentityGate(radixes=self.gate.radixes).get_unitary()}
#grads = {0: 0*IdentityGate(radixes=self.gate.radixes).get_unitary()}
return IdentityGate(radixes=self.gate.radixes).get_unitary(), 0 * IdentityGate(radixes=self.gate.radixes).get_unitary()

# powers = {0: IdentityGate(radixes=self.gate.radixes).get_unitary()}
# grads = {0: 0*IdentityGate(radixes=self.gate.radixes).get_unitary()}

powers = {}
grads = {}

# decompose the power as sum of powers of 2
indexbin=bin(abs(self.power))[2:]
indices=[len(indexbin)-1-xb.start() for xb in re.finditer('1',indexbin)][::-1]

powers[0], grads[0] = self.gate.get_unitary_and_grad(params)

# avoid doing computations if not needed
if self.power==1:
indexbin = bin(abs(self.power))[2:]
indices = [
len(indexbin) - 1 - xb.start()
for xb in re.finditer('1', indexbin)
][::-1]

powers[0], grads[0] = self.gate.get_unitary_and_grad(params)

# avoid doing computations if not needed
if self.power == 1:
return powers[0], grads[0]





# check if the power is negative, and

# check if the power is negative, and
if np.sign(self.power) == -1:
gate = DaggerGate(self.gate)
powers[0], grads[0] = gate.get_unitary_and_grad(params)
# avoid doing computations if not needed
if abs(self.power)==1:

# avoid doing computations if not needed
if abs(self.power) == 1:
return powers[0], grads[0]



grads[1] = grads[0] @ powers[0] + powers[0] @ grads[0]
powers[1] = powers[0] @ powers[0]
# avoid doing more computations if not needed
if abs(self.power)==2:

# avoid doing more computations if not needed
if abs(self.power) == 2:
return powers[1], grads[1]

# loop over powers of 2
for i in range(2,indices[-1]+1):
powers[i] = powers[i-1] @ powers[i-1]
grads[i] = grads[i-1] @ powers[i-1] + powers[i-1] @ grads[i-1]

for i in range(2, indices[-1] + 1):
powers[i] = powers[i - 1] @ powers[i - 1]
grads[i] = grads[i - 1] @ powers[i - 1] + \
powers[i - 1] @ grads[i - 1]

unitary = powers[indices[0]]
for i in indices[1:]:
unitary = unitary @ powers[indices[i]]
grad = 0*IdentityGate(radixes=self.gate.radixes).get_unitary()

grad = 0 * IdentityGate(radixes=self.gate.radixes).get_unitary()
for i in indices:
grad_tmp = grads[i]
for j in indices:
if j<i:
if j < i:
grad_tmp = powers[j] @ grad_tmp
elif j>i:
elif j > i:
grad_tmp = grad_tmp @ powers[j]
grad = grad + grad_tmp

return unitary, grad



def __eq__(self, other: object) -> bool:
return (
isinstance(other, PowerGate)
Expand Down
59 changes: 37 additions & 22 deletions tests/ir/gates/composed/test_power.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
"""This module tests the PowerGate class."""
from __future__ import annotations

from bqskit.ir.gates import PowerGate, DaggerGate
from bqskit.ir.gates import RXGate, RYGate, RZGate
import numpy as np

from bqskit.ir.gates import DaggerGate
from bqskit.ir.gates import PowerGate
from bqskit.ir.gates import RXGate
from bqskit.ir.gates import RYGate
from bqskit.ir.gates import RZGate


test_power = lambda gate, power, params: np.linalg.matrix_power(
gate.get_unitary([params]), power,
)

test_power = lambda gate, power, params: np.linalg.matrix_power(gate.get_unitary([params]),power)

def square_grad(gate, params):
g, gd = gate.get_unitary_and_grad([params])
return g@gd+gd@g
return g @ gd + gd @ g


def third_power_grad(gate, params):
g, gd = gate.get_unitary_and_grad([params])
return g @ square_grad(gate, params)+ gd @ test_power(gate, 2, params)
return g @ square_grad(gate, params) + gd @ test_power(gate, 2, params)


def quartic_power_grad(gate, params):
g, gd = gate.get_unitary_and_grad([params])
return g@third_power_grad(gate, params)+gd @ test_power(gate, 3, params)
return g @ third_power_grad(gate, params) + gd @ test_power(gate, 3, params)


def power_gate_grads(gate, power, params):
if power == 2:
Expand All @@ -34,47 +44,52 @@ def power_gate_grads(gate, power, params):
elif power == -4:
return quartic_power_grad(DaggerGate(gate), power)

def test(test_gate, indices, params, error) -> None:


def test(test_gate, indices, params, error) -> None:

# test index 1
for param in params:
pgt, pgdt = test_gate.get_unitary_and_grad([param])
pgate = PowerGate(test_gate, 1)
pg, pgd = pgate.get_unitary_and_grad([param])
assert np.sum(abs(pg-pgt))<error
assert np.sum(abs(pgd-pgdt))<error
assert np.sum(abs(pg - pgt)) < error
assert np.sum(abs(pgd - pgdt)) < error

# test index -1
for param in params:
pgt, pgdt = DaggerGate(test_gate).get_unitary_and_grad([param])
pgate = PowerGate(test_gate, -1)
pg, pgd = pgate.get_unitary_and_grad([param])
assert np.sum(abs(pg-pgt))<error
assert np.sum(abs(pgd-pgdt))<error
assert np.sum(abs(pg - pgt)) < error
assert np.sum(abs(pgd - pgdt)) < error

# test other indices
for index in indices:
for param in params:
gate = test_power(test_gate, index, param)
grad = power_gate_grads(test_gate, index, param)

pgate = PowerGate(test_gate, index)
pg, pgd = pgate.get_unitary_and_grad([param])
assert np.sum(abs(pg-gate))<error
assert np.sum(abs(pgd-grad))<error
assert np.sum(abs(pg - gate)) < error
assert np.sum(abs(pgd - grad)) < error


error = 1e-14
params = [-0.7, -0.3, 0.2, 1.4]
indices = [-4 ,-3, -2 , 2, 3, 4]

indices = [-4, -3, -2, 2, 3, 4]


def test_x() -> None:
global error, indices, parames
test(RXGate(), indices, params, error)



def test_y() -> None:
global error, indices, parames
test(RYGate(), indices, params, error)



def test_z() -> None:
global error, indices, parames
test(RZGate(), indices, params, error)
test(RZGate(), indices, params, error)

0 comments on commit a9591bc

Please sign in to comment.