TODO quantum circuit
This commit is contained in:
parent
fae34da41a
commit
a09bdb949c
@ -236,11 +236,7 @@ class UnitaryOperator(LinearOperator, UnitaryMatrix):
|
|||||||
LinearOperator.__init__(self, func=self.operator_func, *args, **kwargs)
|
LinearOperator.__init__(self, func=self.operator_func, *args, **kwargs)
|
||||||
|
|
||||||
def operator_func(self, other):
|
def operator_func(self, other):
|
||||||
if not hasattr(other, "m"):
|
return State(np.dot(self.m, other.m))
|
||||||
other_m = other
|
|
||||||
else:
|
|
||||||
other_m = other.m
|
|
||||||
return State(np.dot(self.m, other_m))
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.name:
|
if self.name:
|
||||||
@ -248,6 +244,68 @@ class UnitaryOperator(LinearOperator, UnitaryMatrix):
|
|||||||
return str(self.m)
|
return str(self.m)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO - How to add a CNOT gate to the Quantum Processor?
|
||||||
|
# Imagine if I have to act on a 3-qubit computer and CNOT(q1, q3)
|
||||||
|
#
|
||||||
|
# Decomposed CNOT :
|
||||||
|
# reverse engineered from
|
||||||
|
# https://quantumcomputing.stackexchange.com/questions/4252/how-to-derive-the-cnot-matrix-for-a-3-qbit-system-where-the-control-target-qbi
|
||||||
|
#
|
||||||
|
# CNOT(q1, I, q2):
|
||||||
|
# |0><0| x I_2 x I_2 + |1><1| x I_2 x X
|
||||||
|
# np.kron(np.kron(np.outer(_0.m, _0.m), np.eye(2)), np.eye(2)) + np.kron(np.kron(np.outer(_1.m, _1.m), np.eye(2)), X.m)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# CNOT(q1, q2):
|
||||||
|
# |0><0| x I + |1><1| x X
|
||||||
|
# np.kron(np.outer(_0.m, _0.m), np.eye(2)) + np.kron(np.outer(_1.m, _1.m), X.m)
|
||||||
|
# _0.x(_0) * Matrix(I.m) + _1.x(_1) * Matrix(X.m)
|
||||||
|
|
||||||
|
class TwoQubitPartial(object):
|
||||||
|
def __init__(self, rpr):
|
||||||
|
self.rpr = rpr
|
||||||
|
self.operator = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str("-{}-".format(self.rpr))
|
||||||
|
|
||||||
|
|
||||||
|
C_ = TwoQubitPartial("C")
|
||||||
|
x_ = TwoQubitPartial("x")
|
||||||
|
|
||||||
|
|
||||||
|
class TwoQubitOperator(UnitaryOperator):
|
||||||
|
def __init__(self, m: ListOrNdarray, A: TwoQubitPartial, B: TwoQubitPartial,
|
||||||
|
A_p: UnitaryOperator, B_p: UnitaryOperator, *args, **kwargs):
|
||||||
|
super().__init__(m, *args, **kwargs)
|
||||||
|
A.operator, B.operator = self, self
|
||||||
|
self.A = A
|
||||||
|
self.B = B
|
||||||
|
self.A_p = A_p
|
||||||
|
self.B_p = B_p
|
||||||
|
|
||||||
|
def verify_step(self, step):
|
||||||
|
if not (step.count(self.A) == 1 and step.count(self.B) == 1):
|
||||||
|
raise RuntimeError("Both CONTROL and TARGET need to be defined in the same step exactly once")
|
||||||
|
|
||||||
|
def compose(self, step, state):
|
||||||
|
# TODO: Hacky way to do CNOT
|
||||||
|
# Should generalize for a 2-Qubit gate
|
||||||
|
# _0.x(_0) * Matrix(I.m) + _1.x(_1) * Matrix(X.m)
|
||||||
|
outer_0, outer_1 = [], []
|
||||||
|
for s in step:
|
||||||
|
if s == self.A:
|
||||||
|
outer_0.append(_0.x(_0))
|
||||||
|
outer_1.append(_1.x(_1))
|
||||||
|
elif s == self.B:
|
||||||
|
outer_0.append(Matrix(self.A_p.m))
|
||||||
|
outer_1.append(Matrix(self.B_p.m))
|
||||||
|
else:
|
||||||
|
outer_0.append(Matrix(s.m))
|
||||||
|
outer_1.append(Matrix(s.m))
|
||||||
|
return reduce((lambda x, y: x * y), outer_0) + reduce((lambda x, y: x * y), outer_1)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Define States and Operators
|
Define States and Operators
|
||||||
"""
|
"""
|
||||||
@ -290,69 +348,6 @@ H = UnitaryOperator([[1 / np.sqrt(2), 1 / np.sqrt(2)],
|
|||||||
[1 / np.sqrt(2), -1 / np.sqrt(2)], ],
|
[1 / np.sqrt(2), -1 / np.sqrt(2)], ],
|
||||||
name="H")
|
name="H")
|
||||||
|
|
||||||
|
|
||||||
# TODO - How to add a CNOT gate to the Quantum Processor?
|
|
||||||
# Imagine if I have to act on a 3-qubit computer and CNOT(q1, q3)
|
|
||||||
#
|
|
||||||
# Decomposed CNOT :
|
|
||||||
# reverse engineered from
|
|
||||||
# https://quantumcomputing.stackexchange.com/questions/4252/how-to-derive-the-cnot-matrix-for-a-3-qbit-system-where-the-control-target-qbi
|
|
||||||
#
|
|
||||||
# CNOT(q1, I, q2):
|
|
||||||
# |0><0| x I_2 x I_2 + |1><1| x I_2 x X
|
|
||||||
# np.kron(np.kron(np.outer(_0.m, _0.m), np.eye(2)), np.eye(2)) + np.kron(np.kron(np.outer(_1.m, _1.m), np.eye(2)), X.m)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# CNOT(q1, q2):
|
|
||||||
# |0><0| x I + |1><1| x X
|
|
||||||
# np.kron(np.outer(_0.m, _0.m), np.eye(2)) + np.kron(np.outer(_1.m, _1.m), X.m)
|
|
||||||
# _0.x(_0) * Matrix(I.m) + _1.x(_1) * Matrix(X.m)
|
|
||||||
|
|
||||||
class TwoQubitPartial(object):
|
|
||||||
def __init__(self, rpr):
|
|
||||||
self.rpr = rpr
|
|
||||||
self.operator = None
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return str("-{}-".format(self.rpr))
|
|
||||||
|
|
||||||
|
|
||||||
C_ = TwoQubitPartial("C")
|
|
||||||
x_ = TwoQubitPartial("x")
|
|
||||||
|
|
||||||
|
|
||||||
class TwoQubitOperator(UnitaryOperator):
|
|
||||||
def __init__(self, m: ListOrNdarray, A: TwoQubitPartial, B: TwoQubitPartial,
|
|
||||||
A_p: UnitaryOperator, B_p:UnitaryOperator, *args, **kwargs):
|
|
||||||
super().__init__(m, *args, **kwargs)
|
|
||||||
A.operator, B.operator = self, self
|
|
||||||
self.A = A
|
|
||||||
self.B = B
|
|
||||||
self.A_p = A_p
|
|
||||||
self.B_p = B_p
|
|
||||||
|
|
||||||
def verify_step(self, step):
|
|
||||||
if not (step.count(self.A) == 1 and step.count(self.B) == 1):
|
|
||||||
raise RuntimeError("Both CONTROL and TARGET need to be defined in the same step exactly once")
|
|
||||||
|
|
||||||
def compose(self, step, state):
|
|
||||||
# TODO: Hacky way to do CNOT
|
|
||||||
# Should generalize for a 2-Qubit gate
|
|
||||||
# _0.x(_0) * Matrix(I.m) + _1.x(_1) * Matrix(X.m)
|
|
||||||
outer_0, outer_1 = [], []
|
|
||||||
for s in step:
|
|
||||||
if s == self.A:
|
|
||||||
outer_0.append(_0.x(_0))
|
|
||||||
outer_1.append(_1.x(_1))
|
|
||||||
elif s == self.B:
|
|
||||||
outer_0.append(Matrix(self.A_p.m))
|
|
||||||
outer_1.append(Matrix(self.B_p.m))
|
|
||||||
else:
|
|
||||||
outer_0.append(Matrix(s.m))
|
|
||||||
outer_1.append(Matrix(s.m))
|
|
||||||
return reduce((lambda x, y: x * y), outer_0) + reduce((lambda x, y: x * y), outer_1)
|
|
||||||
|
|
||||||
|
|
||||||
CNOT = TwoQubitOperator([[1, 0, 0, 0],
|
CNOT = TwoQubitOperator([[1, 0, 0, 0],
|
||||||
[0, 1, 0, 0],
|
[0, 1, 0, 0],
|
||||||
[0, 0, 0, 1],
|
[0, 0, 0, 1],
|
||||||
@ -513,18 +508,11 @@ def naive_load_test(N):
|
|||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
class QuantumProcessor(object):
|
class QuantumCircuit(object):
|
||||||
HALT_STATE = "HALT"
|
|
||||||
RUNNING_STATE = "RUNNING"
|
|
||||||
|
|
||||||
def __init__(self, n_qubits: int):
|
def __init__(self, n_qubits: int):
|
||||||
self.n_qubits = n_qubits
|
self.n_qubits = n_qubits
|
||||||
self.steps = [[_0 for _ in range(n_qubits)], ]
|
self.steps = [[_0 for _ in range(n_qubits)], ]
|
||||||
self._called_add_row = 0
|
self._called_add_row = 0
|
||||||
self.current_step = 0
|
|
||||||
self.current_quantum_state = None
|
|
||||||
self.current_state = self.HALT_STATE
|
|
||||||
self.reset()
|
|
||||||
|
|
||||||
def add_row_step(self, row: int, step: int, qbit_state):
|
def add_row_step(self, row: int, step: int, qbit_state):
|
||||||
if len(self.steps) <= step:
|
if len(self.steps) <= step:
|
||||||
@ -553,39 +541,14 @@ class QuantumProcessor(object):
|
|||||||
for row_data in rows_data:
|
for row_data in rows_data:
|
||||||
self.add_row(row_data)
|
self.add_row(row_data)
|
||||||
|
|
||||||
def compose_quantum_state(self, step):
|
def get_next_step(self):
|
||||||
if any([type(s) is TwoQubitPartial for s in step]):
|
running_step = self.steps[self.c_step]
|
||||||
two_qubit_gates = filter(lambda s: type(s) is TwoQubitPartial, step)
|
|
||||||
state = []
|
|
||||||
for two_qubit_gate in two_qubit_gates:
|
|
||||||
two_qubit_gate.operator.verify_step(step)
|
|
||||||
state = two_qubit_gate.operator.compose(step, state)
|
|
||||||
return state
|
|
||||||
return reduce((lambda x, y: x * y), step)
|
|
||||||
|
|
||||||
def step(self):
|
|
||||||
if self.current_step == 0 and self.current_state == self.HALT_STATE:
|
|
||||||
self.current_state = self.RUNNING_STATE
|
|
||||||
if self.current_step >= len(self.steps):
|
|
||||||
self.current_state = self.HALT_STATE
|
|
||||||
raise RuntimeWarning("Halted")
|
|
||||||
running_step = self.steps[self.current_step]
|
|
||||||
step_quantum_state = self.compose_quantum_state(running_step)
|
step_quantum_state = self.compose_quantum_state(running_step)
|
||||||
if not self.current_quantum_state:
|
if not self.c_q_state:
|
||||||
self.current_quantum_state = step_quantum_state
|
self.c_q_state = step_quantum_state
|
||||||
else:
|
else:
|
||||||
self.current_quantum_state = State((step_quantum_state | self.current_quantum_state).m)
|
self.c_q_state = State((step_quantum_state | self.c_q_state).m)
|
||||||
self.current_step += 1
|
self.c_step += 1
|
||||||
|
|
||||||
def run(self):
|
|
||||||
for _ in self.steps:
|
|
||||||
self.step()
|
|
||||||
self.current_state = self.HALT_STATE
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self.current_step = 0
|
|
||||||
self.current_quantum_state = None
|
|
||||||
self.current_state = self.HALT_STATE
|
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
print("=" * 3 * len(self.steps))
|
print("=" * 3 * len(self.steps))
|
||||||
@ -597,10 +560,56 @@ class QuantumProcessor(object):
|
|||||||
print(line)
|
print(line)
|
||||||
print("=" * 3 * len(self.steps))
|
print("=" * 3 * len(self.steps))
|
||||||
|
|
||||||
|
|
||||||
|
class QuantumProcessor(object):
|
||||||
|
HALT_STATE = "HALT"
|
||||||
|
RUNNING_STATE = "RUNNING"
|
||||||
|
|
||||||
|
def __init__(self, circuit: QuantumCircuit):
|
||||||
|
self.circuit = circuit
|
||||||
|
self.c_step = 0
|
||||||
|
self.c_q_state = None
|
||||||
|
self.c_state = self.HALT_STATE
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def compose_quantum_state(self, step):
|
||||||
|
if any([type(s) is TwoQubitPartial for s in step]):
|
||||||
|
two_qubit_gates = filter(lambda s: type(s) is TwoQubitPartial, step)
|
||||||
|
state = []
|
||||||
|
for two_qubit_gate in two_qubit_gates:
|
||||||
|
two_qubit_gate.operator.verify_step(step)
|
||||||
|
state = two_qubit_gate.operator.compose(step, state)
|
||||||
|
return state
|
||||||
|
return reduce((lambda x, y: x * y), step)
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
if self.c_step == 0 and self.c_state == self.HALT_STATE:
|
||||||
|
self.c_state = self.RUNNING_STATE
|
||||||
|
if self.c_step >= len(self.circuit.steps):
|
||||||
|
self.c_state = self.HALT_STATE
|
||||||
|
raise RuntimeWarning("Halted")
|
||||||
|
running_step = self.circuit.get_next_step()
|
||||||
|
step_quantum_state = self.compose_quantum_state(running_step)
|
||||||
|
if not self.c_q_state:
|
||||||
|
self.c_q_state = step_quantum_state
|
||||||
|
else:
|
||||||
|
self.c_q_state = State((step_quantum_state | self.c_q_state).m)
|
||||||
|
self.c_step += 1
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for _ in self.steps:
|
||||||
|
self.step()
|
||||||
|
self.current_state = self.HALT_STATE
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.c_step = 0
|
||||||
|
self.c_q_state = None
|
||||||
|
self.c_state = self.HALT_STATE
|
||||||
|
|
||||||
def measure(self):
|
def measure(self):
|
||||||
if self.current_state != self.HALT_STATE:
|
if self.c_state != self.HALT_STATE:
|
||||||
raise RuntimeError("Processor is still running")
|
raise RuntimeError("Processor is still running")
|
||||||
return self.current_quantum_state.measure()
|
return self.c_q_state.measure()
|
||||||
|
|
||||||
def run_n(self, n: int):
|
def run_n(self, n: int):
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
|
Loading…
Reference in New Issue
Block a user