Source code for photonic_circuit_solver.supplementalfuncs

'Adding test and utility functions built by Tarek Razzaz. Almost all functions require additional optional packages beyond just NumPy'

try:
    from sympy import I, pi, sqrt, sin, cos, exp, sign, simplify, latex, Expr, Function, Add, Mul
    from sympy.physics.quantum import qapply
    from sympy.physics.quantum.qubit import Qubit, QubitBra, qubit_to_matrix
    from sympy.physics.quantum.gate import IdentityGate, X, Y, Z, H, S, T, CNOT, CPHASE
except:
    pass
try:
    import networkx as nx
except:
    pass
try:
    import matplotlib.pyplot as plt
except:
    pass
try:
    from IPython.display import clear_output
except:
    pass

[docs] def dephase(state): ''' Removes the global phase from a SymPy statevector by dividing by the phase of the first component (Requires SymPy) :param state: The SymPy statevector to be dephased :type state: Expr :return: state without the global phase :rtype: Expr ''' try: res = qubit_to_matrix(state) glob_phase = 1 for elem in res: if elem != 0: glob_phase = sign(elem) break return state / glob_phase except: return state
[docs] def apply(state, operation = None, dp = False): ''' Simplifies an expression describing a SymPy statevector :param state: The SymPy statevector to be simplified :type state: Expr :param operation: An operation (usually a quantum gate) to apply to the state, defaults to None :type operation: Expr :param dp: A boolean determining if the statevector should be dephased, defaults to False :type dp: bool :return: The simplified version of the SymPy statevector :rtype: Expr ''' if operation is None: if dp: pre_simplified = dephase(qapply((state).doit())) try: return simplify(pre_simplified).expand() except: return pre_simplified.evalf() else: pre_simplified = qapply((state).doit()) try: return simplify(pre_simplified).expand() except: return pre_simplified.evalf() else: if dp: pre_simplified = dephase(qapply((operation * state).doit())) try: return simplify(pre_simplified).expand() except: return pre_simplified.evalf() else: pre_simplified = qapply((operation * state).doit()) try: return simplify(pre_simplified).expand() except: return pre_simplified.evalf()
[docs] def tensor(state1, state2): ''' Computes the tensor product of two SymPy statevectors (not fully tested) (requires SymPy) :param state1: The first SymPy statevector in the tensor product :type state1: Expr :param state2: The second SymPy statevector in the tensor product :type state2: Expr :return: The SymPy statevector representing the tensor product of state1 and state2 :rtype: Expr ''' term1 = apply(state1) term2 = apply(state2) if isinstance(term1, Qubit): if isinstance(term2, Qubit): bits = term1.args + term2.args string = '' for bit in bits: string += str(bit) return Qubit(string) elif isinstance(term2, Add): result = tensor(term1, term2.args[0]) for i in range(1, len(term2.args)): result += tensor(term1, term2.args[i]) return result elif isinstance(term2, Mul) and isinstance(term2.args[-1], Qubit): result = term2.args[0] for i in range(1, len(term2.args) - 1): result *= term2.args[i] return result * tensor(term1, term2.args[-1]) elif isinstance(term1, Add): result = tensor(term1.args[0], term2) for i in range(1, len(term1.args)): result += tensor(term1.args[i], term2) return result elif isinstance(term1, Mul) and isinstance(term1.args[-1], Qubit): result = term1.args[0] for i in range(1, len(term1.args) - 1): result *= term1.args[i] return result * tensor(term1.args[-1], term2) else: raise TypeError("Unexpected input")
[docs] def Rz(target: int, theta: float): ''' Apply an RZ gate via SymPy (requires SymPy) :param theta: Rotation angle :type theta: float :param target: Qubit to apply the RZ gate to :type target: int ''' return cos(theta/2)*IdentityGate(target) - I*sin(theta/2)*Z(target)
[docs] def Rx(target: int, theta: float): ''' Apply an RZ gate via SymPy (requires SymPy) :param theta: Rotation angle :type theta: float :param target: Qubit to apply the RZ gate to :type target: int ''' return cos(theta/2)*IdentityGate(target) - I*sin(theta/2)*X(target)
[docs] def Ry(target: int, theta: float): ''' Apply an RZ gate via SymPy (requires SymPy) :param theta: Rotation angle :type theta: float :param target: Qubit to apply the RZ gate to :type target: int ''' return cos(theta/2)*IdentityGate(target) - I*sin(theta/2)*Y(target)
[docs] class Graph: """ This is a class that encodes graphs, and contains a few convenient functions (most useful methods require optional packages) :param edges: A list of edges describing the graph :type edges: list """ def __init__(self, edges: list[tuple]) -> None: n_qubits = 0 for edge in edges: if edge[0] > n_qubits: n_qubits = edge[0] if edge[1] > n_qubits: n_qubits = edge[1] n_qubits += 1 qubits = range(n_qubits) for edge in edges: if edge[0] not in qubits: raise Exception("Unexpected qubit: " + str(edge[0])) if edge[1] not in qubits: raise Exception("Unexpected qubit: " + str(edge[1])) self.n_qubits = n_qubits self.edges = edges
[docs] def draw(self) -> None: ''' Method to generate an nx graph (requires NetworkX) ''' G = nx.Graph() G.add_nodes_from([i for i in range(self.n_qubits)]) G.add_edges_from(self.edges) return G
[docs] def state(self, progress: bool = False): ''' Uses SymPy to calculate the statevector associated with the graph (requires SymPy) :param progress: A boolean option to continuously print out the progress of calculating the state (useful for large graphs), defaults to False :type progress: bool :return: A SymPy expression representing the statevector :rtype: Expr ''' state = Qubit('0'*self.n_qubits) photons = [] for edge in self.edges: if edge[0] not in photons: photons.append(edge[0]) if edge[1] not in photons: photons.append(edge[1]) for i in range(len(photons)): if progress: clear_output(wait=True) print("Applying Hadamards: " + str(round(100*i/len(photons))) + "%") photon = photons[i] state = apply(state, H(photon)) for i in range(len(self.edges)): edge = self.edges[i] if progress: clear_output(wait=True) print("Applying CZs: " + str(round(100*i/len(self.edges))) + "%") state = apply(state, CPHASE(edge[0], edge[1])) if progress: clear_output(wait=True) return state