Skip to content

Commit

Permalink
💄further linting improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
nquetschlich committed Apr 9, 2024
1 parent 7827a22 commit 6525f95
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 70 deletions.
11 changes: 8 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,7 @@ unsafe-fixes = true

[tool.ruff.lint]
extend-select = [
"E", "F", "W", # flake8
"A", # flake8-builtins
"A", # flake8-builtins
"ANN", # flake8-annotations
"ARG", # flake8-unused-arguments
"ASYNC", # flake8-async
Expand All @@ -141,10 +140,12 @@ extend-select = [
"EM", # flake8-errmsg
"EXE", # flake8-executable
"FA", # flake8-future-annotations
"FLY", # flynt
"I", # isort
"ICN", # flake8-import-conventions
"ISC", # flake8-implicit-str-concat
# "N", # flake8-naming
"LOG", # flake8-logging-format
"N", # flake8-naming
"NPY", # numpy
"PERF", # perflint
"PGH", # pygrep-hooks
Expand All @@ -157,9 +158,11 @@ extend-select = [
"RET", # flake8-return
"RSE", # flake8-raise
"RUF", # Ruff-specific
# "S", # flake8-bandit
"SLF", # flake8-self
"SLOT", # flake8-slots
"SIM", # flake8-simplify
# "T20", # flake8-print
"TCH", # flake8-type-checking
"TID", # flake8-tidy-imports
"TRY", # tryceratops
Expand All @@ -169,8 +172,10 @@ extend-select = [
extend-ignore = [
"ANN101", # Missing type annotation for self in method
"ANN102", # Missing type annotation for cls in classmethod
"ISC001", # Conflicts with formatter
"E501", # Line too long (Black is enough)
"PLR", # Design related pylint codes
"S101", # Use of assert detected
]
flake8-unused-arguments.ignore-variadic-names = true
isort.required-imports = ["from __future__ import annotations"]
Expand Down
6 changes: 3 additions & 3 deletions src/mqt/bench/benchmark_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,18 +465,18 @@ def timeout_watcher(
timeout: int,
args: list[Any] | int | tuple[int, str] | str,
) -> bool | QuantumCircuit | Circuit:
class TimeoutException(Exception): # Custom exception class
class TimeoutExceptionError(Exception): # Custom exception class
pass

def timeout_handler(_signum: int, _frame: Any) -> None: # noqa: ANN401
raise TimeoutException
raise TimeoutExceptionError

# Change the behavior of SIGALRM
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout)
try:
res = func(*args) if isinstance(args, tuple | list) else func(args)
except TimeoutException:
except TimeoutExceptionError:
print(
"Calculation/Generation exceeded timeout limit for ",
func.__name__,
Expand Down
34 changes: 17 additions & 17 deletions src/mqt/bench/benchmarks/qiskit_application_optimization/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,19 @@ def generate_instance(


class QuantumOptimizer:
def __init__(self, instance: NDArray[np.float64], n: int, K: int) -> None:
def __init__(self, instance: NDArray[np.float64], n: int, k: int) -> None:
self.instance = instance
self.n = n
self.K = K
self.k = k

def binary_representation(
self, x_sol: NDArray[np.float64]
) -> tuple[NDArray[np.float64], NDArray[np.float64], float, float]:
instance = self.instance
n = self.n
K = self.K
k = self.k

A = np.max(instance) * 100 # A parameter of cost function
a = np.max(instance) * 100 # A parameter of cost function

# Determine the weights w
instance_vec = instance.reshape(n**2)
Expand All @@ -66,12 +66,12 @@ def binary_representation(
w[ii] = w_list[ii]

# Some variables I will use
Id_n = np.eye(n)
Im_n_1 = np.ones([n - 1, n - 1])
Iv_n_1 = np.ones(n)
Iv_n_1[0] = 0
Iv_n = np.ones(n - 1)
neg_Iv_n_1 = np.ones(n) - Iv_n_1
id_n = np.eye(n)
im_n_1 = np.ones([n - 1, n - 1])
iv_n_1 = np.ones(n)
iv_n_1[0] = 0
iv_n = np.ones(n - 1)
neg_iv_n_1 = np.ones(n) - iv_n_1

v = np.zeros([n, n * (n - 1)])
for ii in range(n):
Expand All @@ -86,34 +86,34 @@ def binary_representation(
vn = np.sum(v[1:], axis=0)

# Q defines the interactions between variables
Q = A * (np.kron(Id_n, Im_n_1) + np.dot(v.T, v))
q = a * (np.kron(id_n, im_n_1) + np.dot(v.T, v))

# g defines the contribution from the individual variables
g = w - 2 * A * (np.kron(Iv_n_1, Iv_n) + vn.T) - 2 * A * K * (np.kron(neg_Iv_n_1, Iv_n) + v[0].T)
g = w - 2 * a * (np.kron(iv_n_1, iv_n) + vn.T) - 2 * a * k * (np.kron(neg_iv_n_1, iv_n) + v[0].T)

# c is the constant offset
c = 2 * A * (n - 1) + 2 * A * (K**2)
c = 2 * a * (n - 1) + 2 * a * (k**2)

try:
# Evaluates the cost distance from a binary representation of a path
def fun(x: NDArray[np.float64]) -> float:
return cast(
float,
np.dot(np.around(x), np.dot(Q, np.around(x))) + np.dot(g, np.around(x)) + c,
np.dot(np.around(x), np.dot(q, np.around(x))) + np.dot(g, np.around(x)) + c,
)

cost = fun(x_sol)
except Exception:
cost = 0

return Q, g, cast(float, c), cost
return q, g, cast(float, c), cost

def construct_problem(self, Q: QuadraticExpression, g: LinearExpression, c: float) -> QuadraticProgram:
def construct_problem(self, q: QuadraticExpression, g: LinearExpression, c: float) -> QuadraticProgram:
qp = QuadraticProgram()
for i in range(self.n * (self.n - 1)):
qp.binary_var(str(i))

qp.objective.quadratic = Q
qp.objective.quadratic = q
qp.objective.linear = g
qp.objective.constant = c
return qp
Expand Down
94 changes: 48 additions & 46 deletions src/mqt/bench/benchmarks/shor.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ def _phi_add_gate(angles: NDArray[np.float64] | ParameterVector) -> Gate:
circuit.p(angle, i)
return circuit.to_gate()

def _double_controlled_phi_add_mod_N(
def _double_controlled_phi_add_mod_n(
self,
angles: NDArray[np.float64] | ParameterVector,
c_phi_add_N: Gate,
iphi_add_N: Gate,
c_phi_add_n: Gate,
iphi_add_n: Gate,
qft: Gate,
iqft: Gate,
) -> QuantumCircuit:
Expand All @@ -94,13 +94,13 @@ def _double_controlled_phi_add_mod_N(

circuit.append(cc_phi_add_a, [*ctrl_qreg, *b_qreg])

circuit.append(iphi_add_N, b_qreg)
circuit.append(iphi_add_n, b_qreg)

circuit.append(iqft, b_qreg)
circuit.cx(b_qreg[-1], flag_qreg[0])
circuit.append(qft, b_qreg)

circuit.append(c_phi_add_N, [*flag_qreg, *b_qreg])
circuit.append(c_phi_add_n, [*flag_qreg, *b_qreg])

circuit.append(cc_iphi_add_a, [*ctrl_qreg, *b_qreg])

Expand All @@ -114,130 +114,132 @@ def _double_controlled_phi_add_mod_N(

return circuit

def _controlled_multiple_mod_N(
def _controlled_multiple_mod_n(
self,
n: int,
N: int,
num_bits_necessary: int,
to_be_factored_number: int,
a: int,
c_phi_add_N: Gate,
iphi_add_N: Gate,
c_phi_add_n: Gate,
iphi_add_n: Gate,
qft: Gate,
iqft: Gate,
) -> Instruction:
"""Implements modular multiplication by a as an instruction."""
ctrl_qreg = QuantumRegister(1, "ctrl")
x_qreg = QuantumRegister(n, "x")
b_qreg = QuantumRegister(n + 1, "b")
x_qreg = QuantumRegister(num_bits_necessary, "x")
b_qreg = QuantumRegister(num_bits_necessary + 1, "b")
flag_qreg = QuantumRegister(1, "flag")

circuit = QuantumCircuit(ctrl_qreg, x_qreg, b_qreg, flag_qreg, name="cmult_a_mod_N")

angle_params = ParameterVector("angles", length=n + 1)
modulo_adder = self._double_controlled_phi_add_mod_N(angle_params, c_phi_add_N, iphi_add_N, qft, iqft)
angle_params = ParameterVector("angles", length=num_bits_necessary + 1)
modulo_adder = self._double_controlled_phi_add_mod_n(angle_params, c_phi_add_n, iphi_add_n, qft, iqft)

def append_adder(adder: QuantumCircuit, constant: int, idx: int) -> None:
partial_constant = (pow(2, idx, N) * constant) % N
angles = self._get_angles(partial_constant, n + 1)
partial_constant = (pow(2, idx, to_be_factored_number) * constant) % to_be_factored_number
angles = self._get_angles(partial_constant, num_bits_necessary + 1)
bound = adder.assign_parameters({angle_params: angles})
circuit.append(bound, [*ctrl_qreg, x_qreg[idx], *b_qreg, *flag_qreg])

circuit.append(qft, b_qreg)

# perform controlled addition by a on the aux register in Fourier space
for i in range(n):
for i in range(num_bits_necessary):
append_adder(modulo_adder, a, i)

circuit.append(iqft, b_qreg)

# perform controlled subtraction by a in Fourier space on both the aux and down register
for i in range(n):
for i in range(num_bits_necessary):
circuit.cswap(ctrl_qreg, x_qreg[i], b_qreg[i])

circuit.append(qft, b_qreg)

a_inv = pow(a, -1, mod=N)
a_inv = pow(a, -1, mod=to_be_factored_number)

modulo_adder_inv = modulo_adder.inverse()
for i in reversed(range(n)):
for i in reversed(range(num_bits_necessary)):
append_adder(modulo_adder_inv, a_inv, i)

circuit.append(iqft, b_qreg)

return circuit.to_instruction()

def _power_mod_N(self, n: int, N: int, a: int) -> Instruction:
def _power_mod_n(self, num_bits_necessary: int, to_be_factored_number: int, a: int) -> Instruction:
"""Implements modular exponentiation a^x as an instruction."""
up_qreg = QuantumRegister(2 * n, name="up")
down_qreg = QuantumRegister(n, name="down")
aux_qreg = QuantumRegister(n + 2, name="aux")
up_qreg = QuantumRegister(2 * num_bits_necessary, name="up")
down_qreg = QuantumRegister(num_bits_necessary, name="down")
aux_qreg = QuantumRegister(num_bits_necessary + 2, name="aux")

circuit = QuantumCircuit(up_qreg, down_qreg, aux_qreg, name=f"{a}^x mod {N}")
circuit = QuantumCircuit(up_qreg, down_qreg, aux_qreg, name=f"{a}^x mod {to_be_factored_number}")

qft = QFT(n + 1, do_swaps=False).to_gate()
qft = QFT(num_bits_necessary + 1, do_swaps=False).to_gate()
iqft = qft.inverse()

# Create gates to perform addition/subtraction by N in Fourier Space
phi_add_N = self._phi_add_gate(self._get_angles(N, n + 1))
iphi_add_N = phi_add_N.inverse()
c_phi_add_N = phi_add_N.control(1)
phi_add_n = self._phi_add_gate(self._get_angles(to_be_factored_number, num_bits_necessary + 1))
iphi_add_n = phi_add_n.inverse()
c_phi_add_n = phi_add_n.control(1)

# Apply the multiplication gates as showed in
# the report in order to create the exponentiation
for i in range(2 * n):
partial_a = pow(a, pow(2, i), N)
modulo_multiplier = self._controlled_multiple_mod_N(n, N, partial_a, c_phi_add_N, iphi_add_N, qft, iqft)
for i in range(2 * num_bits_necessary):
partial_a = pow(a, pow(2, i), to_be_factored_number)
modulo_multiplier = self._controlled_multiple_mod_n(
num_bits_necessary, to_be_factored_number, partial_a, c_phi_add_n, iphi_add_n, qft, iqft
)
circuit.append(modulo_multiplier, [up_qreg[i], *down_qreg, *aux_qreg])

return circuit.to_instruction()

@staticmethod
def _validate_input(N: int, a: int) -> None:
def _validate_input(to_be_factored_number: int, a: int) -> None:
"""Check parameters of the algorithm.
Args:
N: The odd integer to be factored, has a min. value of 3.
to_be_factored_number: The odd integer to be factored, has a min. value of 3.
a: Any integer that satisfies 1 < a < N and gcd(a, N) = 1.
Raises:
ValueError: Invalid input
"""
validate_min("N", N, 3)
validate_min("N", to_be_factored_number, 3)
validate_min("a", a, 2)

if N < 1 or N % 2 == 0:
if to_be_factored_number < 1 or to_be_factored_number % 2 == 0:
msg = "The input needs to be an odd integer greater than 1."
raise ValueError(msg)

if a >= N or math.gcd(a, N) != 1:
if a >= to_be_factored_number or math.gcd(a, to_be_factored_number) != 1:
msg = "The integer a needs to satisfy a < N and gcd(a, N) = 1."
raise ValueError(msg)

def construct_circuit(self, N: int, a: int = 2) -> QuantumCircuit:
def construct_circuit(self, to_be_factored_number: int, a: int = 2) -> QuantumCircuit:
"""Construct quantum part of the algorithm.
Args:
N: The odd integer to be factored, has a min. value of 3.
to_be_factored_number: The odd integer to be factored, has a min. value of 3.
a: Any integer that satisfies 1 < a < N and gcd(a, N) = 1.
Returns:
Quantum circuit.
"""
self._validate_input(N, a)
self._validate_input(to_be_factored_number, a)

# Get n value used in Shor's algorithm, to know how many qubits are used
n = N.bit_length()
num_bits_necessary = to_be_factored_number.bit_length()

# quantum register where the sequential QFT is performed
up_qreg = QuantumRegister(2 * n, name="up")
up_qreg = QuantumRegister(2 * num_bits_necessary, name="up")
# quantum register where the multiplications are made
down_qreg = QuantumRegister(n, name="down")
down_qreg = QuantumRegister(num_bits_necessary, name="down")
# auxiliary quantum register used in addition and multiplication
aux_qreg = QuantumRegister(n + 2, name="aux")
aux_qreg = QuantumRegister(num_bits_necessary + 2, name="aux")

# Create Quantum Circuit
circuit = QuantumCircuit(up_qreg, down_qreg, aux_qreg, name=f"Shor(N={N}, a={a})")
circuit = QuantumCircuit(up_qreg, down_qreg, aux_qreg, name=f"Shor(N={to_be_factored_number}, a={a})")

# Create maximal superposition in top register
circuit.h(up_qreg)
Expand All @@ -246,7 +248,7 @@ def construct_circuit(self, N: int, a: int = 2) -> QuantumCircuit:
circuit.x(down_qreg[0])

# Apply modulo exponentiation
modulo_power = self._power_mod_N(n, N, a)
modulo_power = self._power_mod_n(num_bits_necessary, to_be_factored_number, a)
circuit.append(modulo_power, circuit.qubits)

# Apply inverse QFT
Expand Down
2 changes: 1 addition & 1 deletion tests/test_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ def test_calc_supermarq_features() -> None:
assert 0 < regular_features.liveness < 1


def test_BenchmarkGenerator() -> None:
def test_benchmark_generator() -> None:
generator = BenchmarkGenerator(qasm_output_path="test")
assert generator.qasm_output_path == "test"
assert generator.timeout > 0
Expand Down

0 comments on commit 6525f95

Please sign in to comment.