def __init__(self, paths: PhotonicPaths, state: QubitWaveFunction = None): """ :param paths: A dictionary containing all photonic paths, where each path contains dictionaries with modes :param qubit_state: A State in qubit representation -> A dictionary with integers (BitStrings) as keys """ self._paths = paths self._state = state if state is None: self._state = QubitWaveFunction()
def test_trace_out_xy(theta): a = numpy.sin(theta) b = numpy.cos(theta) state = QubitWaveFunction.from_array([a,b]) H1 = QubitHamiltonian.from_string("1.0*X(0)*X(1)*X(100)") H2 = QubitHamiltonian.from_string("1.0*X(0)*X(100)") factor = a.conjugate()*b + b.conjugate()*a assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) factor *= factor H1 = QubitHamiltonian.from_string("1.0*X(0)*X(1)*X(5)*X(100)") assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) H1 = QubitHamiltonian.from_string("1.0*X(0)*Y(1)*X(100)") H2 = QubitHamiltonian.from_string("1.0*X(0)*X(100)") factor = -1.0j*(a.conjugate()*b - b.conjugate()*a) assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) factor *= factor H1 = QubitHamiltonian.from_string("1.0*X(0)*Y(1)*Y(5)*X(100)") assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) H1 = QubitHamiltonian.from_string("1.0*X(0)*X(1)*X(100)") H2 = QubitHamiltonian.from_string("1.0*X(0)*X(100)") factor = a.conjugate()*b + b.conjugate()*a assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) factor *= -1.0j*(a.conjugate()*b - b.conjugate()*a) H1 = QubitHamiltonian.from_string("1.0*X(0)*X(1)*Y(5)*X(100)") assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3)
def test_notation(silent=True): qpm = 2 S = 2 # State |-201>_(abc) represented with 2 qubits per mode # in Qubits # |01 00 00 00 00 | 00 00 01 00 00 | 00 00 00 01 00 > test1 = BitString.from_binary(binary='010000000000000100000000000100') paths = PhotonicPaths(path_names=['a', 'b', 'c'], S=S, qpm=qpm) wfn1 = QubitWaveFunction.from_int(i=test1) test1x = PhotonicStateVector(paths=paths, state=wfn1) test1y = PhotonicStateVector.from_string( paths=paths, string='1.0|10000>_a|00100>_b|00010>_c') if not silent: print("got = ", test1x) print("expected = ", test1y) assert (test1x == test1y) # Do a 332 State: # Photonic notation: (one photon in each mode and I added -2 and +2 modes) # |1-11>_a+|000>_b+|-111>_c # Qudit Notation: With modes -2 ... 2 # | 0 0 0 1 0 >_a | 0 1 0 0 0 >_b | 0 0 0 1 0 >_c # + | 0 0 1 0 0 >_a | 0 0 1 0 0 >_b | 0 0 1 0 0 >_c # + | 0 1 0 0 0 >_a | 0 0 0 1 0 >_b | 0 0 0 1 0 >_c # Qubit notation: (using 2 qubits for each mode) # | 00 00 00 01 00 >_a | 00 01 00 00 00 >_b | 00 00 00 01 00 >_c # + | 00 00 01 00 00 >_a | 00 00 01 00 00 >_b | 00 00 01 00 00 >_c # + | 00 01 00 00 00 >_a | 00 00 00 01 00 >_b | 00 00 00 01 00 >_c string = '1.0|00010>_a|01000>b|00010>_c+1.0|00100>_a|00100>_b|00100>_c+1.0|01000>_a|00010>_b|00010>_c' test_332x = PhotonicStateVector.from_string(paths=paths, string=string) q1 = BitString.from_binary('000000010000010000000000000100') q2 = BitString.from_binary('000001000000000100000000010000') q3 = BitString.from_binary('000100000000000001000000000100') wfn = QubitWaveFunction() for q in [q1, q2, q3]: wfn += QubitWaveFunction.from_int(i=q) test_332y = PhotonicStateVector(paths=paths, state=wfn) if not silent: print("got = ", test_332y) print("expected = ", test_332x) assert (test_332x == test_332y)
def test_projectors(qubits): real = numpy.random.uniform(0.0, 1.0, 2**qubits) imag = numpy.random.uniform(0.0, 1.0, 2**qubits) array = real + 1.j * imag wfn = QubitWaveFunction.from_array(arr=array) P = paulis.Projector(wfn=wfn.normalize()) assert (P.is_hermitian()) assert (wfn.apply_qubitoperator(P) == wfn) PM = P.to_matrix() assert ((PM.dot(PM) == PM).all)
def add_basis_state(self, state: Dict[str, Dict[int, int]], coeff=1): if isinstance(state, str): state = self.string_to_basis_state(string=state) qubit_string = BitString.from_int(integer=0, nbits=self.n_qubits) for p, v in state.items(): for m, occ in v.items(): mode = self.get_mode(p, m) occ = BitString.from_int(integer=occ, nbits=mode.n_qubits) for i, q in enumerate(mode.qubits): qubit_string[q] = occ[i] self._state += QubitWaveFunction.from_int(i=qubit_string, coeff=coeff)
def test_simple_trace_out(): H1 = QubitHamiltonian.from_string("1.0*Z(0)*Z(1)") H2 = QubitHamiltonian.from_string("1.0*Z(0)") assert H2 == H1.trace_out_qubits(qubits=[1], states=None) H1 = QubitHamiltonian.from_string("1.0*Z(0)*Z(1)X(100)") H2 = QubitHamiltonian.from_string("1.0*Z(1)X(100)") assert H2 == H1.trace_out_qubits(qubits=[0], states=None) H1 = QubitHamiltonian.from_string("1.0*Z(0)*Z(1)X(100)") H2 = QubitHamiltonian.from_string("-1.0*Z(0)X(100)") assert H2 == H1.trace_out_qubits(qubits=[1], states=[QubitWaveFunction.from_string("1.0*|1>")]) H1 = QubitHamiltonian.from_string("1.0*Z(0)*Z(1)X(100)") H2 = QubitHamiltonian.from_string("-1.0*Z(1)X(100)") assert H2 == H1.trace_out_qubits(qubits=[0], states=[QubitWaveFunction.from_string("1.0*|1>")]) H1 = QubitHamiltonian.from_string("1.0*X(0)*Z(1)*Z(5)*X(100)Y(50)") H2 = QubitHamiltonian.from_string("1.0*X(0)X(100)Y(50)") assert H2 == H1.trace_out_qubits(qubits=[1,5], states=[QubitWaveFunction.from_string("1.0*|1>")]*2) H1 = QubitHamiltonian.from_string("1.0*X(0)*Z(1)*X(100)Y(50)") H2 = QubitHamiltonian.from_string("-1.0*X(0)X(100)Y(50)") assert H2 == H1.trace_out_qubits(qubits=[1,5], states=[QubitWaveFunction.from_string("1.0*|1>")]*2)
def reference_state(self, reference_orbitals: list = None, n_qubits: int = None) -> BitString: """Does a really lazy workaround ... but it works :return: Hartree-Fock Reference as binary-number Parameters ---------- reference_orbitals: list: give list of doubly occupied orbitals default is None which leads to automatic list of the first n_electron/2 orbitals Returns ------- """ if reference_orbitals is None: reference_orbitals = [i for i in range(self.n_electrons // 2)] spin_orbitals = sorted([2 * i for i in reference_orbitals] + [2 * i + 1 for i in reference_orbitals]) if n_qubits is None: n_qubits = 2 * self.n_orbitals string = "" for i in spin_orbitals: string += str(i) + "^ " fop = openfermion.FermionOperator(string, 1.0) op = QubitHamiltonian(qubit_hamiltonian=self.transformation(fop)) from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction wfn = QubitWaveFunction.from_int(0, n_qubits=n_qubits) wfn = wfn.apply_qubitoperator(operator=op) assert (len(wfn.keys()) == 1) keys = [k for k in wfn.keys()] return keys[-1]
def test_ketbra_random(n_qubits): ket = numpy.random.uniform(0.0, 1.0, 2**n_qubits) bra = QubitWaveFunction.from_int(0, n_qubits=n_qubits) operator = paulis.KetBra(ket=ket, bra=bra) result = operator * bra assert result == QubitWaveFunction.from_array(ket)
def test_ketbra(): ket = QubitWaveFunction.from_string("1.0*|00> + 1.0*|11>").normalize() operator = paulis.KetBra(ket=ket, bra="|00>") result = operator * QubitWaveFunction.from_int(0, n_qubits=2) assert (result == ket)
class PhotonicStateVector: """ Take the simulator result and keep track of the photonic modes """ @classmethod def from_string(cls, paths: PhotonicPaths, string: str): """ Terms are splitted by '+' so complex values like (x+iy)|...> will not work, you need to add Real and imaginary separately Or improve this consructor :-) :param paths: :param string: :return: """ string = string.lstrip('+') result = cls(paths) terms = string.split('+') for term in terms: tmp = term.split("|") if tmp[0] == '': coeff = 1.0 else: coeff = complex(tmp[0]) basis_state = cls.string_to_basis_state(string=term) result.add_basis_state(state=basis_state, coeff=coeff) return result @classmethod def string_to_basis_state(cls, string: str) -> Dict[str, Dict[int, int]]: """ :param string: basis state in the form |x>_a|y>_b|z>_c ... :return: Dictionary in the form {path: {mode:occ}} """ basis_state = dict() tmp = string.split("|") for i in range(1, len(tmp)): bs = tmp[i].rstrip().lstrip() path = bs[-1] occs = bs.split(">")[0] M = len(occs) S = (M - 1) // 2 modes = dict() for j, m in enumerate(range(-S, S + 1)): modes[m] = int(occs[j]) basis_state[path] = modes return basis_state def add_basis_state(self, state: Dict[str, Dict[int, int]], coeff=1): if isinstance(state, str): state = self.string_to_basis_state(string=state) qubit_string = BitString.from_int(integer=0, nbits=self.n_qubits) for p, v in state.items(): for m, occ in v.items(): mode = self.get_mode(p, m) occ = BitString.from_int(integer=occ, nbits=mode.n_qubits) for i, q in enumerate(mode.qubits): qubit_string[q] = occ[i] self._state += QubitWaveFunction.from_int(i=qubit_string, coeff=coeff) def get_qubit_key(self, state): qubit_string = BitString.from_int(integer=0, nbits=self.n_qubits) for p, v in state.items(): for m, occ in v.items(): mode = self.get_mode(p, m) occ = BitString.from_int(integer=occ, nbits=mode.n_qubits) for i, q in enumerate(mode.qubits): qubit_string[q] = occ[i] return qubit_string def get_basis_state(self, string: str): """ :param string: basis state in the form |x>_a|y>_b|z>_c ... :return: the coefficient/count_number of that basis state in the full state """ basis_state = self.string_to_basis_state(string=string) key = self.get_qubit_key(basis_state) if key in self._state.keys(): return self._state[key] else: return 0 @property def numbering(self): return BitNumbering.MSB @property def n_modes(self): if self._paths is None: return 0 for k, v in self._paths.items(): return len(v) @property def n_paths(self): return len(self._paths) @property def n_qubits(self): result = 0 for p in self._paths.values(): for m in p.values(): result += m.n_qubits return result @property def state(self): return self._state @property def paths(self): return self._paths def get_mode(self, path, mode): """ :param path: Name of the path :param mode: Name of the mode :return: indices of qubits which represent the corresponding mode in the corresponding path """ return self._paths[path][mode] def __init__(self, paths: PhotonicPaths, state: QubitWaveFunction = None): """ :param paths: A dictionary containing all photonic paths, where each path contains dictionaries with modes :param qubit_state: A State in qubit representation -> A dictionary with integers (BitStrings) as keys """ self._paths = paths self._state = state if state is None: self._state = QubitWaveFunction() def normalize(self): self._state = self._state.normalize() return self def __repr__(self): threshold = 1.e-3 result = "" nqubits = self.n_qubits for i, s in self._state.items(): i.bits = nqubits if isclose(s, 0.0, atol=threshold): continue result += number_to_string(number=s) result += self.interpret_bitstring(i=i) return result def interpret_bitstring(self, i: BitString) -> str: return self._paths.interpret_bitstring(i=i) def __eq__(self, other): return self._paths == other._paths and self._state == other._state def __rmul__(self, other): return PhotonicStateVector(paths=self._paths, state=other * deepcopy(self._state)) def __iadd__(self, other): assert (self.paths == other.paths) self._state += other.state return self def inner(self, other): return self._state.inner(other._state) def plot(self, title: str = None, label: str = None, filename: str = None): from matplotlib import pyplot as plt if title is not None: plt.title(title) plt.ylabel("counts") plt.xlabel("state") values = [] names = [] for k, v in self._state.items(): values.append(v) names.append(self.interpret_bitstring(i=k)) plt.bar(names, values, label=label) if label is not None: plt.legend() if filename is not None: plt.savefig(filename, dpi=None, facecolor='w', edgecolor='w', orientation='landscape', papertype=None, format=None, transparent=False, bbox_inches='tight', pad_inches=0.1, metadata=None) with open(filename + "_data", 'a+') as f: f.write("names \t\t values\n") for i, v in enumerate(values): f.write(str(names[i]) + "\t\t" + str(values[i]) + "\n") f.write("end\n") else: plt.show()