diff --git a/example/grover.py b/example/grover.py index 4b91d5b..20ca214 100644 --- a/example/grover.py +++ b/example/grover.py @@ -5,32 +5,32 @@ def oracle(v, target): n = q.sv.num_qubits(v) for i in range(n): if (target >> i) & 1 == 0: - v = q.sv.apply(q.op.x(), v, [i]) + v = q.sv.apply(v, q.op.x(), [i]) assert q.op.is_operator(q.op.cz(n)) assert q.sv.is_state_vector(v) - v = q.sv.apply(q.op.cz(n), v) + v = q.sv.apply(v, q.op.cz(n)) for i in range(n): if (target >> i) & 1 == 0: - v = q.sv.apply(q.op.x(), v, [i]) + v = q.sv.apply(v, q.op.x(), [i]) return v def diffusion(v): n = q.sv.num_qubits(v) for i in range(n): - v = q.sv.apply(q.op.h(), v, [i]) - v = q.sv.apply(q.op.x(), v, [i]) - v = q.sv.apply(q.op.cz(n), v) + v = q.sv.apply(v, q.op.h(), [i]) + v = q.sv.apply(v, q.op.x(), [i]) + v = q.sv.apply(v, q.op.cz(n)) for i in range(n): - v = q.sv.apply(q.op.x(), v, [i]) - v = q.sv.apply(q.op.h(), v, [i]) + v = q.sv.apply(v, q.op.x(), [i]) + v = q.sv.apply(v, q.op.h(), [i]) return v def grover(n, target, iter): v = q.sv.state_vector(n) for i in range(n): - v = q.sv.apply(q.op.h(), v, [i]) + v = q.sv.apply(v, q.op.h(), [i]) for k in range(iter): v = oracle(v, target) v = diffusion(v) diff --git a/example/shimada-2.2.py b/example/shimada-2.2.py index d7d68cb..41b2c0c 100644 --- a/example/shimada-2.2.py +++ b/example/shimada-2.2.py @@ -8,11 +8,11 @@ def main(): print("state vector:", q.sv.vector(v)) print("probabitily:", q.sv.probability(v)) - v = q.sv.apply(q.op.h(), v, [0]) - v = q.sv.apply(q.op.h(), v, [2]) - v = q.sv.apply(q.op.cx(), v, [0, 1]) - v = q.sv.apply(q.op.cz(), v, [1, 2]) - v = q.sv.apply(q.op.h(), v, [2]) + v = q.sv.apply(v, q.op.h(), [0]) + v = q.sv.apply(v, q.op.h(), [2]) + v = q.sv.apply(v, q.op.cx(), [0, 1]) + v = q.sv.apply(v, q.op.cz(), [1, 2]) + v = q.sv.apply(v, q.op.h(), [2]) print("output:") print("state vector:", q.sv.vector(v)) diff --git a/src/qailo/mps/__init__.py b/src/qailo/mps/__init__.py index b400407..d350a6d 100644 --- a/src/qailo/mps/__init__.py +++ b/src/qailo/mps/__init__.py @@ -1,21 +1,20 @@ from .apply import apply from .mps import MPS from .norm import norm -from .num_qubits import num_qubits from .product_state import product_state from .state_vector import state_vector from .svd import compact_svd, tensor_svd -from .type import is_canonical, is_mps +from .type import is_canonical, is_mps, num_qubits __all__ = [ apply, MPS, norm, - num_qubits, product_state, state_vector, compact_svd, tensor_svd, is_canonical, is_mps, + num_qubits, ] diff --git a/src/qailo/mps/apply.py b/src/qailo/mps/apply.py new file mode 100644 index 0000000..d0d0b6c --- /dev/null +++ b/src/qailo/mps/apply.py @@ -0,0 +1,49 @@ +from copy import deepcopy + +from ..operator import type as op +from ..operator.swap import swap +from .type import num_qubits + + +def _swap_tensors(m, s, maxdim=None): + """ + swap neighboring two tensors at s and s+1 + """ + assert s in range(0, num_qubits(m) - 1) + m._apply_two(swap(), s, maxdim=maxdim) + p0, p1 = m.t2q[s], m.t2q[s + 1] + m.q2t[p0], m.q2t[p1] = s + 1, s + m.t2q[s], m.t2q[s + 1] = p1, p0 + + +def _move_qubit(m, p, s, maxdim=None): + if m.q2t[p] != s: + # print(f"moving qubit {p} at {m.q2t[p]} to {s}") + for u in range(m.q2t[p], s): + # print(f"swap tensors {u} and {u+1}") + _swap_tensors(m, u, maxdim=maxdim) + for u in range(m.q2t[p], s, -1): + # print(f"swap tensors {u-1} and {u}") + _swap_tensors(m, u - 1, maxdim=maxdim) + + +def _apply(m, p, qpos, maxdim=None): + assert op.is_operator(p) and len(qpos) == op.num_qubits(p) + if op.num_qubits(p) == 1: + m._apply_one(p, m.q2t[qpos[0]]) + elif op.num_qubits(p) == 2: + tpos = [m.q2t[qpos[0]], m.q2t[qpos[1]]] + assert tpos[0] != tpos[1] + if tpos[0] < tpos[1]: + _move_qubit(m, qpos[1], tpos[0] + 1) + m._apply_two(p, tpos[0], maxdim=maxdim) + else: + _move_qubit(m, qpos[0], tpos[1] + 1) + m._apply_two(p, tpos[1], maxdim=maxdim, reverse=True) + else: + raise ValueError + return m + + +def apply(m, p, qpos, maxdim=None): + return _apply(deepcopy(m), p, qpos, maxdim) diff --git a/src/qailo/mps/norm.py b/src/qailo/mps/norm.py index ae0e987..596c447 100644 --- a/src/qailo/mps/norm.py +++ b/src/qailo/mps/norm.py @@ -1,6 +1,6 @@ import numpy as np -from .num_qubits import num_qubits +from .type import num_qubits def norm(m): diff --git a/src/qailo/mps/num_qubits.py b/src/qailo/mps/num_qubits.py deleted file mode 100644 index c4d8c11..0000000 --- a/src/qailo/mps/num_qubits.py +++ /dev/null @@ -1,2 +0,0 @@ -def num_qubits(m): - return len(m.tensors) diff --git a/src/qailo/mps/state_vector.py b/src/qailo/mps/state_vector.py index e023cc2..5e0001d 100644 --- a/src/qailo/mps/state_vector.py +++ b/src/qailo/mps/state_vector.py @@ -1,8 +1,7 @@ import numpy as np from ..util.strops import letters, replace -from .num_qubits import num_qubits -from .type import is_mps +from .type import is_mps, num_qubits def state_vector(m): diff --git a/src/qailo/mps/type.py b/src/qailo/mps/type.py index 6a1f613..44e4ee0 100644 --- a/src/qailo/mps/type.py +++ b/src/qailo/mps/type.py @@ -4,3 +4,7 @@ def is_canonical(m): def is_mps(m): return hasattr(m, "tensors") + + +def num_qubits(m): + return len(m.tensors) diff --git a/src/qailo/operator/multiply.py b/src/qailo/operator/multiply.py index 765a867..95d0e62 100644 --- a/src/qailo/operator/multiply.py +++ b/src/qailo/operator/multiply.py @@ -4,11 +4,11 @@ from .type import is_operator, num_qubits -def multiply(op, opi, pos=None): - assert is_operator(opi) - assert is_operator(op) - n = num_qubits(opi) - m = num_qubits(op) +def multiply(pin, p, pos=None): + assert is_operator(pin) + assert is_operator(p) + n = num_qubits(pin) + m = num_qubits(p) if pos is None: assert m == n pos = range(n) @@ -21,4 +21,4 @@ def multiply(op, opi, pos=None): for i in range(m): ss_opi = replace(ss_opi, pos[i], ss_op[m + i]) ss_to = replace(ss_to, pos[i], ss_op[i]) - return np.einsum("{},{}->{}".format(ss_opi, ss_op, ss_to), opi, op) + return np.einsum("{},{}->{}".format(ss_opi, ss_op, ss_to), pin, p) diff --git a/src/qailo/state_vector/apply.py b/src/qailo/state_vector/apply.py index b387948..f248b31 100644 --- a/src/qailo/state_vector/apply.py +++ b/src/qailo/state_vector/apply.py @@ -5,8 +5,8 @@ from . import type as sv -def apply(p, v, pos=None): - assert op.is_operator(p) and sv.is_state_vector(v) +def apply(v, p, pos=None): + assert sv.is_state_vector(v) and op.is_operator(p) n = sv.num_qubits(v) m = op.num_qubits(p) if pos is None: diff --git a/test/mps/test_apply.py b/test/mps/test_apply.py index 9f1db59..22e805c 100644 --- a/test/mps/test_apply.py +++ b/test/mps/test_apply.py @@ -7,7 +7,7 @@ def apply(op, m0, m1, v, pos, maxdim=None): m0 = q.mps.apply(m0, op, pos) m1 = q.mps.apply(m1, op, pos, maxdim) - v = q.sv.apply(op, v, pos) + v = q.sv.apply(v, op, pos) return m0, m1, v diff --git a/test/operator/test_controlled.py b/test/operator/test_controlled.py index c5a43d8..cd17622 100644 --- a/test/operator/test_controlled.py +++ b/test/operator/test_controlled.py @@ -14,14 +14,14 @@ def test_cz(): assert q.op.is_hermitian(q.op.cz()) assert q.op.is_unitary(q.op.cz()) # Cz01 = Cz10 - assert q.op.is_close(q.op.cz(), q.op.multiply(q.op.cz(), q.op.identity(2), [1, 0])) + assert q.op.is_close(q.op.cz(), q.op.multiply(q.op.identity(2), q.op.cz(), [1, 0])) def test_ccccx(): for n in range(3, 8): - op = q.op.identity(n) - op = q.op.multiply(q.op.control_begin(), op, [0, 1]) + p = q.op.identity(n) + p = q.op.multiply(p, q.op.control_begin(), [0, 1]) for i in range(1, n - 2): - op = q.op.multiply(q.op.control_propagate(), op, [i, i + 1]) - op = q.op.multiply(q.op.control_end(q.op.x()), op, [n - 2, n - 1]) - assert q.op.is_close(op, q.op.cx(n)) + p = q.op.multiply(p, q.op.control_propagate(), [i, i + 1]) + p = q.op.multiply(p, q.op.control_end(q.op.x()), [n - 2, n - 1]) + assert q.op.is_close(p, q.op.cx(n)) diff --git a/test/operator/test_swap.py b/test/operator/test_swap.py index 9668e7b..00975f2 100644 --- a/test/operator/test_swap.py +++ b/test/operator/test_swap.py @@ -2,19 +2,19 @@ def test_swap(): - op = q.op.swap() - assert q.op.is_hermitian(op) - assert q.op.is_unitary(op) - assert q.op.is_identity(q.op.multiply(op, op, [0, 1])) + p = q.op.swap() + assert q.op.is_hermitian(p) + assert q.op.is_unitary(p) + assert q.op.is_identity(q.op.multiply(p, p, [0, 1])) - op = q.op.cx() - op = q.op.multiply(q.op.cx(), op, [1, 0]) - op = q.op.multiply(q.op.cx(), op, [0, 1]) - assert q.op.is_close(op, q.op.swap()) + p = q.op.cx() + p = q.op.multiply(p, q.op.cx(), [1, 0]) + p = q.op.multiply(p, q.op.cx(), [0, 1]) + assert q.op.is_close(p, q.op.swap()) v = q.sv.state_vector(2) - v = q.sv.apply(q.op.h(), v, [0]) - v = q.sv.apply(q.op.swap(), v, [0, 1]) - v = q.sv.apply(q.op.h(), v, [1]) - v = q.sv.apply(q.op.swap(), v, [0, 1]) + v = q.sv.apply(v, q.op.h(), [0]) + v = q.sv.apply(v, q.op.swap(), [0, 1]) + v = q.sv.apply(v, q.op.h(), [1]) + v = q.sv.apply(v, q.op.swap(), [0, 1]) assert q.op.is_close(v, q.sv.state_vector(2)) diff --git a/test/state_vector/test_fidelity.py b/test/state_vector/test_fidelity.py index 121a90c..b75a39f 100644 --- a/test/state_vector/test_fidelity.py +++ b/test/state_vector/test_fidelity.py @@ -8,7 +8,7 @@ def test_fidelity(): sv0 = q.sv.state_vector(n) sv1 = sv0 for i in range(n): - sv1 = q.sv.apply(q.op.h(), sv1, [i]) + sv1 = q.sv.apply(sv1, q.op.h(), [i]) assert q.sv.fidelity(sv0, sv0) == approx(1) assert q.sv.fidelity(sv1, sv1) == approx(1) assert q.sv.fidelity(sv0, sv1) == approx(1 / np.sqrt(2**n)) diff --git a/test/state_vector/test_pure_state.py b/test/state_vector/test_pure_state.py index ca79bfe..793fe5c 100644 --- a/test/state_vector/test_pure_state.py +++ b/test/state_vector/test_pure_state.py @@ -8,7 +8,7 @@ def test_pure_state(): assert q.op.is_density_matrix(dm) for i in range(n): - sv = q.sv.apply(q.op.h(), sv, [i]) + sv = q.sv.apply(sv, q.op.h(), [i]) print(q.sv.vector(sv)) dm = q.sv.pure_state(sv) print(q.op.matrix(dm))