QNLP  v1.0
HammingDistance.py
Go to the documentation of this file.
1 """
2 This set of classes offers an approach to calculating the Hamming distance and encoding
3 into the state phase using PYthon. Pybind11 wrapped C++ methods are used where sensible.
4 """
5 
6 from abc import ABC, abstractmethod
7 
8 from PyQNLPSimulator import DCMatrix
9 from PyQNLPSimulator import GateOps
10 import numpy as np
11 
13  """
14  This class is used to encode the Hamming distance between two
15  qubit registers into the phase.
16  """
17  def __init__(self, num_bits, simulator):
18  self.num_bits = num_bits
19  self.sim = simulator
20 
21  def _UMatrix(self, theta):
22  npmat = np.array([[np.exp(1j*theta), 0], [0, 1]])
23  return DCMatrix(npmat.flatten())
24 
25  def _UMatrixPowMin2(self,theta):
26  npmat = np.linalg.matrix_power([[np.exp(1j*theta), 0], [0, 1]], -2 )
27  return DCMatrix(npmat.flatten())
28 
29  @abstractmethod
30  def calc(self):
31  pass
32 
33 class HammingDistanceExpITheta(HammingDistance):
34  """
35  Default approach taken by Trugenberger, and others to encode
36  Hamming distance into the state phase. Results can be skewed
37  by high degeneracy in encoded patterns.
38  """
39  def __init__(self, num_bits, simulator):
40  super().__init__(num_bits, simulator)
41 
42  def _step1(self, reg_mem, reg_aux, pattern):
43  self.sim.encodeToRegister(pattern, reg_aux[0:-2], len(reg_mem))
44 
45  def _step2(self, reg_mem, reg_aux):
46  for i in range(len(reg_aux)-2):
47  self.sim.applyGateCX(reg_aux[i], reg_mem[i])
48  self.sim.applyGateX(reg_mem[i])
49 
50  def _step3(self, reg_mem, reg_aux):
51  theta = np.pi / (2.0 * len(reg_mem))
52 
53  UMat = self._UMatrix(theta)
54  CUMatPowMin2 = self._UMatrixPowMin2(theta)
55 
56  for j in range(len(reg_mem)):
57  self.sim.applyGateU(UMat, reg_mem[j], "U")
58  for i in range(len(reg_mem)):
59  self.sim.applyGateCU(CUMatPowMin2, reg_aux[-2], reg_mem[i], "U")
60 
61  def _step4(self, reg_mem, reg_aux):
62  for i in range(len(reg_aux)-2):
63  self.sim.applyGateX(reg_mem[i])
64  self.sim.applyGateCX(reg_aux[i], reg_mem[i])
65 
66  def _step5(self, reg_aux):
67  self.sim.collapseToBasisZ(reg_aux[-2], False)
68 
69  def encode_hamming(self, reg_mem, reg_aux, pattern):
70  self.sim.applyGateH(reg_aux[-2])
71  self._step1(reg_mem, reg_aux, pattern)
72  self._step2(reg_mem, reg_aux)
73  self._step3(reg_mem, reg_aux)
74  self._step4(reg_mem, reg_aux)
75  self.sim.applyGateH(reg_aux[-2])
76  self._step5(reg_aux)
77 
78  def hamming_aux_overwrite(self, reg_mem, reg_aux):
79  for i in range(len(reg_mem)):
80  self.sim.applyGateX(reg_aux[i])
81  self.sim.applyGateCCX(reg_mem[i], reg_aux[i], reg_aux[-1])
82  self.sim.applyGateX(reg_aux[i])
83  self.sim.applyGateCSwap(reg_mem[i], reg_aux[i], reg_aux[-1])
84 
85  def calc(self, reg_mem, reg_aux, pattern):
86  self.encode_hamming(reg_mem, reg_aux, pattern)
87 
89  """
90  Intermediate routine to overwrite the data in the aux register
91  with its Hamming dsiatnce to the data in the memory regsiter.
92  """
93  def __init__(self, num_bits, simulator):
94  super().__init__(num_bits,simulator)
95 
96  def _encodePattern(self, reg_mem, reg_aux, pattern):
97  self.sim.encodeToRegister(pattern, reg_aux[0:-2], len(reg_mem))
98 
99  def _overwriteAux(self, reg_mem, reg_aux, pattern):
100  for idx, val in enumerate(reg_mem):
101  self.sim.applyGateX(reg_aux[idx])
102  self.sim.applyGateCCX(reg_mem[idx], reg_aux[idx], reg_aux[-2])
103  self.sim.applyGateX(reg_aux[idx])
104  self.sim.applyGateCSwap(reg_mem[idx], reg_aux[idx], reg_aux[-2])
105 
106  def calc(self, reg_mem, reg_aux, pattern):
107  self._encodePattern(reg_mem, reg_aux, pattern)
108  self._overwriteAux(reg_mem, reg_aux, pattern)
109 
111  """
112  Class attempt alternative for state amplitude weighting by
113  Hamming distance. Uses oracle-based pattern, applying the
114  appropriate rotation angle to the state determined by matching
115  the number of set bits to a pre-defined set of values.
116  """
117 
118  def __init__(self, num_bits, simulator):
119  super().__init__(num_bits, simulator)
120  self.gops = GateOps(simulator)
121  self.val_range = np.arange(-1.0, 1, 2/num_bits)
122 
123  def calc(self, reg_mem, reg_aux, pattern):
124  self._encodePattern(reg_mem, reg_aux, pattern)
125  self._overwriteAux(reg_mem, reg_aux, pattern)
126  self.sim.groupQubits(reg_aux, False)
127  omap = self._oracle_angle_map()
128  for k,v in omap.items():
129  self.sim.addUToCache(v, "RY_{}".format(k))
130  self.sim.applyOracleU(k, self._oracle_angle_map()[k], reg_aux[0:-2], reg_aux[-2], "RY_{}".format(k))
131 
132  def _angle_matrices(self):
133  vals = []
134  for i in self.val_range:
135  vals.append( DCMatrix( np.asarray( self.gops.RY(np.arccos(i)) ).flatten() ) )
136 
137  return vals
138 
139  def _oracle_angle_map(self):
140  val_map = {}
141  mats = self._angle_matrices()
142  for i in range(0, self.num_bits):
143  val_map.update({ 2**i - 1 : mats[i]})
144  return val_map
145 
147  if isinstance(vals, int):
148  return (2**vals) - 1
149  elif isinstance(vals, list):
150  return [(2**val) -1 for val in vals]
151  else:
152  return None
def _overwriteAux(self, reg_mem, reg_aux, pattern)
def calc(self, reg_mem, reg_aux, pattern)
def _step1(self, reg_mem, reg_aux, pattern)
def encode_hamming(self, reg_mem, reg_aux, pattern)
def _encodePattern(self, reg_mem, reg_aux, pattern)
def __init__(self, num_bits, simulator)
def calc(self, reg_mem, reg_aux, pattern)