def to_quimb_tensor(g: BaseGraph) -> 'qtn.TensorNetwork': """Converts tensor network representing the given :func:`pyzx.graph.Graph`. Pretty printing: to_tensor(g).draw(color = ['V', 'H']) Args: g: graph to be converted.""" if qu is None: raise ImportError("quimb must be installed to use this function.") # copying a graph guarantees consecutive indices, which are needed for the tensor net g = g.copy() # only Z spiders are handled below to_gh(g) tensors = [] # Here we have phase tensors corresponding to Z-spiders with only one output and no input. for v in g.vertices(): if g.type(v) == VertexType.Z and g.phase(v) != 0: tensors.append( qtn.Tensor(data=[1, np.exp(1j * np.pi * g.phase(v))], inds=(f'{v}', ), tags=("V", ))) # Hadamard or Kronecker tensors, one for each edge of the diagram. for i, edge in enumerate(g.edges()): x, y = edge isHadamard = g.edge_type(edge) == EdgeType.HADAMARD t = qtn.Tensor(data=qu.hadamard() if isHadamard else np.array([1, 0, 0, 1]).reshape(2, 2), inds=(f'{x}', f'{y}'), tags=("H", ) if isHadamard else ("N", )) tensors.append(t) # TODO: This is not taking care of all the stuff that can be in g.scalar # In particular, it doesn't check g.scalar.phasenodes # TODO: This will give the wrong tensor when g.scalar.is_zero == True. # Grab the float factor and exponent from the scalar scalar_float = np.exp(1j * np.pi * g.scalar.phase) * g.scalar.floatfactor for node in g.scalar.phasenodes: # Each node is a Fraction scalar_float *= 1 + np.exp(1j * np.pi * node) scalar_exp = math.log10(math.sqrt(2)) * g.scalar.power2 # If the TN is empty, create a single 0-tensor with scalar factor, otherwise # multiply the scalar into one of the tensors. if len(tensors) == 0: tensors.append(qtn.Tensor(data=scalar_float, inds=(), tags=("S", ))) else: tensors[0].modify(data=tensors[0].data * scalar_float) network = qtn.TensorNetwork(tensors) # the exponent can be very large, so distribute it evenly through the TN network.exponent = scalar_exp network.distribute_exponent() return network
def test_simple(self): n = 10 d_psi = qu.computational_state('0' * n) t_psi = Dense1D(d_psi) assert set(t_psi.outer_inds()) == {'k{}'.format(i) for i in range(n)} assert set(t_psi.tags) == {'I{}'.format(i) for i in range(n)} for i in range(n): assert t_psi.H @ t_psi.gate(qu.pauli('Z'), i) == pytest.approx(1) for i in range(n): t_psi.gate_(qu.hadamard(), i) assert len(t_psi.tensors) == n + 1 # should have '++++++++++' assert t_psi.H @ t_psi == pytest.approx(1) for i in range(n): assert t_psi.H @ t_psi.gate(qu.pauli('X'), i) == pytest.approx(1)
def apply_U3(psi, theta, phi, lamda, i, **gate_opts): mtags = _merge_tags({'U3'}, gate_opts) psi.gate_(qu.U_gate(theta, phi, lamda), int(i), tags=mtags, **gate_opts) def apply_swap(psi, i, j, **gate_opts): itag, jtag = map(psi.site_tag, (i, j)) psi.reindex_({itag: jtag, jtag: itag}) APPLY_GATES = { 'RX': apply_Rx, 'RY': apply_Ry, 'RZ': apply_Rz, 'U3': apply_U3, 'H': build_gate_1(qu.hadamard(), tags='H'), 'X': build_gate_1(qu.pauli('X'), tags='X'), 'Y': build_gate_1(qu.pauli('Y'), tags='Y'), 'Z': build_gate_1(qu.pauli('Z'), tags='Z'), 'S': build_gate_1(qu.S_gate(), tags='S'), 'T': build_gate_1(qu.T_gate(), tags='T'), 'X_1_2': build_gate_1(qu.Rx(math.pi / 2), tags='X_1/2'), 'Y_1_2': build_gate_1(qu.Ry(math.pi / 2), tags='Y_1/2'), 'Z_1_2': build_gate_1(qu.Rz(math.pi / 2), tags='Z_1/2'), 'IDEN': lambda *args, **kwargs: None, 'CX': build_gate_2(qu.cX(), tags='CX'), 'CY': build_gate_2(qu.cY(), tags='CY'), 'CZ': build_gate_2(qu.cZ(), tags='CZ'), 'CNOT': build_gate_2(qu.CNOT(), tags='CNOT'), 'SWAP': apply_swap, }