partial measurement

This commit is contained in:
Daniel Tsvetkov 2020-03-27 17:09:10 +01:00
parent 06ac6dc6e8
commit 66cbe6f971

94
lib.py
View File

@ -264,17 +264,28 @@ class State(Vector):
"""If the inner (dot) product is zero, this vector is orthogonal to other""" """If the inner (dot) product is zero, this vector is orthogonal to other"""
return self.inner(other) == 0 return self.inner(other) == 0
def get_prob(self, j): def get_prob_from_measurement_op(self, m_op):
"""pr(j) = |<e_j|self>|^2""" assert type(m_op) is MeasurementOperator
# j_th basis vector return m_op.get_prob(self)
e_j = State([[1] if i == int(j) else [0] for i in range(len(self.m))])
return np.absolute((e_j.inner(self)).m.item(0)) ** 2 def get_computational_basis_vector(self, j):
return Vector([[1] if i == int(j) else [0] for i in range(len(self.m))])
def get_prob(self, j, basis=None):
"""pr(j) = |<e_j|self>|^2
"""
if basis is None:
basis = self.get_computational_basis_vector(j)
m = MeasurementOperator.create_from_basis(basis)
return m.get_prob(self)
def get_fmt_of_element(self): def get_fmt_of_element(self):
return "{:0" + str(int(np.ceil(np.log2(len(self))))) + "b}" return "{:0" + str(int(np.ceil(np.log2(len(self))))) + "b}"
def measure(self): def measure(self, basis=None):
""" """
m_ops: a set of MeasurementOperators.
e.g. for computational basis, the m_ops is [|0><0|, |1><1|]
Measures in the computational basis. Measures in the computational basis.
Irreversable operation. Measuring again will result in the same result Irreversable operation. Measuring again will result in the same result
TODO: Generalize the method so it takes a basis TODO: Generalize the method so it takes a basis
@ -289,12 +300,26 @@ class State(Vector):
""" """
if self.measurement_result: if self.measurement_result:
return self.measurement_result return self.measurement_result
weights = [self.get_prob(j) for j in range(len(self))] weights = []
if not basis:
basis = []
for j in range(len(self)):
# Default is computational basis
e_j = self.get_computational_basis_vector(j)
m = MeasurementOperator.create_from_basis(e_j)
basis.append(m)
for i in range(len(basis)):
m_op = basis[i]
prob = self.get_prob_from_measurement_op(m_op)
weights += [prob]
empty_prob = 1.0 - sum(weights) empty_prob = 1.0 - sum(weights)
format_str = self.get_fmt_of_element() format_str = self.get_fmt_of_element()
choices = [REPR_EMPTY_SET] + [format_str.format(i) for i in range(len(weights))] choices = [REPR_EMPTY_SET] + [format_str.format(i) for i in range(len(weights))]
weights = [empty_prob] + weights weights = [empty_prob] + weights
self.measurement_result = random.choices(choices, weights)[0] self.measurement_result = random.choices(choices, weights)[0]
return self.measurement_result return self.measurement_result
def measure_with_op(self, mo): def measure_with_op(self, mo):
@ -321,19 +346,16 @@ class State(Vector):
# [0, 0, 1, 1, 0, 0, 1, 1] # [0, 0, 1, 1, 0, 0, 1, 1]
partial_measurement_of_qbit = [int(b[qubit_n - 1]) for b in bin_repr] partial_measurement_of_qbit = [int(b[qubit_n - 1]) for b in bin_repr]
# [0, 1, 4, 5] # [0, 1, 4, 5]
indexes_for_p_0 = [i for i, index in enumerate(partial_measurement_of_qbit) if index == 0] weights, choices = defaultdict(list), defaultdict(list)
weights_0 = [self.get_prob(j) for j in indexes_for_p_0] for result in [1, 0]:
choices_0 = [format_str.format(i) for i in indexes_for_p_0] indexes_for_p_0 = [i for i, index in enumerate(partial_measurement_of_qbit) if index == result]
measurement_result = random.choices(choices_0, weights_0)[0][qubit_n - 1] weights[result] = [self.get_prob(j) for j in indexes_for_p_0]
# TODO: Verify if this is the correct collapse to lower dimension after partial measurement choices[result] = [format_str.format(i) for i in indexes_for_p_0]
# https://www.youtube.com/watch?v=MG_9JWsrKtM&list=PL1826E60FD05B44E4&index=16 weights_01 = [sum(weights[0]), sum(weights[1])]
normalization_factor = np.sqrt(np.sum(np.abs([self.m[i][0] for i in indexes_for_p_0]) ** 2)) measurement_result = random.choices([0, 1], weights_01)[0]
# TODO: This can be 0... normalization_factor = np.sqrt(sum([np.abs(i) ** 2 for i in weights[measurement_result]]))
post_measurement_state = [ new_m = weights[measurement_result] / normalization_factor
[self.m[i][0] / normalization_factor] for i in indexes_for_p_0 return str(measurement_result), State(new_m.reshape((len(new_m), 1)))
]
self.m = s('|' + measurement_result + '>') * State(post_measurement_state)
return measurement_result
def pretty_print(self): def pretty_print(self):
format_str = self.get_fmt_of_element() + " | {}" format_str = self.get_fmt_of_element() + " | {}"
@ -808,7 +830,7 @@ class MeasurementOperator(HermitianOperator):
"""Measurement operators are Hermitians: <ψ|M†_m M_m|ψ>""" """Measurement operators are Hermitians: <ψ|M†_m M_m|ψ>"""
@classmethod @classmethod
def create_from_prob(cls, matrix: Matrix, *args, **kwargs): def create_from_basis(cls, matrix: Matrix, *args, **kwargs):
"""returns |M†_m><M_m|""" """returns |M†_m><M_m|"""
return cls(matrix.conjugate_transpose().x(matrix).m, *args, **kwargs) return cls(matrix.conjugate_transpose().x(matrix).m, *args, **kwargs)
@ -821,8 +843,8 @@ class MeasurementOperator(HermitianOperator):
return state_ct.m.dot(self.m.dot(state.m)).item() return state_ct.m.dot(self.m.dot(state.m)).item()
m0 = MeasurementOperator.create_from_prob(_0) m0 = MeasurementOperator.create_from_basis(_0)
m1 = MeasurementOperator.create_from_prob(_1) m1 = MeasurementOperator.create_from_basis(_1)
def test_measurement_ops(): def test_measurement_ops():
@ -930,6 +952,20 @@ def test():
assert np.isclose(_p.get_prob(0), 0.5) # Probability for |+> in 0 is 0.5 assert np.isclose(_p.get_prob(0), 0.5) # Probability for |+> in 0 is 0.5
assert np.isclose(_p.get_prob(1), 0.5) # Probability for |+> in 1 is 0.5 assert np.isclose(_p.get_prob(1), 0.5) # Probability for |+> in 1 is 0.5
assert _0.measure() == '0'
assert _1.measure() == '1'
assert s("|10>").measure() == '10'
assert s("|10>").measure_partial(1) == ("1", _0)
assert s("|10>").measure_partial(2) == ("0", _1)
# Maximally entangled
result, pms = b_phi_p.measure_partial(1)
if result == "0":
assert pms == _0
elif result == "1":
assert pms == _1
# Test measurement operators # Test measurement operators
test_measurement_ops() test_measurement_ops()
@ -1223,9 +1259,9 @@ def test_quantum_processor():
def test_light(): def test_light():
# http://alienryderflex.com/polarizer/ # http://alienryderflex.com/polarizer/
hor_filter = MeasurementOperator.create_from_prob(Matrix(_0.m), name='h') hor_filter = MeasurementOperator.create_from_basis(Matrix(_0.m), name='h')
diag_filter = MeasurementOperator.create_from_prob(Matrix(_p.m), name='d') diag_filter = MeasurementOperator.create_from_basis(Matrix(_p.m), name='d')
ver_filter = MeasurementOperator.create_from_prob(Matrix(_1.m), name='v') ver_filter = MeasurementOperator.create_from_basis(Matrix(_1.m), name='v')
def random_light(): def random_light():
random_pol = Vector([[np.random.uniform(0, 1)], [np.random.uniform(0, 1)]]) random_pol = Vector([[np.random.uniform(0, 1)], [np.random.uniform(0, 1)]])
@ -1261,12 +1297,12 @@ def test_light():
if __name__ == "__main__": if __name__ == "__main__":
test() test()
test_quantum_processor() # test_quantum_processor()
test_light() # test_light()
# Partial measurement - I want to measure just the first qubit... # Partial measurement - I want to measure just the first qubit...
# TODO - makes sense??? # TODO - makes sense???
# m0_ = m0 * I # m0_ = m0 * I
# post_measurement = b_phi_m.measure_with_op(m0_) # s("|10>").measure_partial(1)
# result_0 = post_measurement.measure() # result_0 = post_measurement.measure()
# test_measure_partial() # test_measure_partial()