Ejemplo n.º 1
0
 def test_simple(self):
     p = neel_state(1)
     assert_allclose(p, up())
     p = neel_state(2)
     assert_allclose(p, up() & down())
     p = neel_state(3)
     assert_allclose(p, up() & down() & up())
Ejemplo n.º 2
0
 def test_simple(self):
     Z = qu.pauli('Z')
     P = qu.projector(Z & Z)
     uu = qu.dop(qu.up()) & qu.dop(qu.up())
     dd = qu.dop(qu.down()) & qu.dop(qu.down())
     assert_allclose(P, uu + dd)
     assert qu.expec(P, qu.bell_state('phi+')) == pytest.approx(1.0)
     assert qu.expec(P, qu.bell_state('psi+')) == pytest.approx(0.0)
Ejemplo n.º 3
0
 def test_classically_no_correlated(self, s, qtype, pre_c):
     p = qu.up(qtype=qtype) & qu.up(qtype=qtype)
     c = qu.correlation(p,
                        qu.pauli(s),
                        qu.pauli(s),
                        0,
                        1,
                        precomp_func=pre_c)
     c = c(p) if pre_c else c
     assert_allclose(c, 0.0)
Ejemplo n.º 4
0
 def test_classically_correlated(self, s, ct, pre_c):
     p = 0.5 * ((qu.up(qtype='dop') & qu.up(qtype='dop')) +
                (qu.down(qtype='dop') & qu.down(qtype='dop')))
     c = qu.correlation(p,
                        qu.pauli(s),
                        qu.pauli(s),
                        0,
                        1,
                        precomp_func=pre_c)
     c = c(p) if pre_c else c
     assert_allclose(c, ct)
Ejemplo n.º 5
0
 def test_entangled_permute(self):
     dims = [2, 2, 2]
     a = qu.bell_state(0) & qu.up()
     assert_allclose(qu.mutinf_subsys(a, dims, 0, 1), 2.)
     b = qu.permute(a, dims, [1, 2, 0])
     assert_allclose(qu.mutinf_subsys(b, dims, 0, 1), 0., atol=1e-12)
     assert_allclose(qu.mutinf_subsys(b, dims, 0, 2), 2.)
Ejemplo n.º 6
0
def tensor_expectation_value(circuit: cirq.Circuit,
                             pauli_string: cirq.PauliString,
                             max_ram_gb=16,
                             tol=1e-6) -> float:
    """Compute an expectation value for an operator and a circuit via tensor
    contraction.

    This will give up if it looks like the computation will take too much RAM.
    """
    circuit_sand = circuit_for_expectation_value(
        circuit, pauli_string / pauli_string.coefficient)
    qubits = sorted(circuit_sand.all_qubits())

    tensors, qubit_frontier, _ = circuit_to_tensors(circuit=circuit_sand,
                                                    qubits=qubits)
    end_bras = [
        qtn.Tensor(data=quimb.up().squeeze(),
                   inds=(f'i{qubit_frontier[q]}_q{q}', ),
                   tags={'Q0', 'bra0'}) for q in qubits
    ]
    tn = qtn.TensorNetwork(tensors + end_bras)
    tn.rank_simplify(inplace=True)
    path_info = tn.contract(get='path-info')
    ram_gb = path_info.largest_intermediate * 128 / 8 / 1024 / 1024 / 1024
    if ram_gb > max_ram_gb:
        raise MemoryError("We estimate that this contraction "
                          "will take too much RAM! {} GB".format(ram_gb))
    e_val = tn.contract(inplace=True)
    assert e_val.imag < tol
    assert pauli_string.coefficient.imag < tol
    return e_val.real * pauli_string.coefficient
Ejemplo n.º 7
0
 def test_bell_state(self):
     p = bell_state('psi-')
     assert_allclose(schmidt_gap(p, [2, 2], 0), 0.0)
     p = up() & down()
     assert_allclose(schmidt_gap(p, [2, 2], 0), 1.0)
     p = rand_ket(2**3)
     assert 0 < schmidt_gap(p, [2] * 3, sysa=[0, 1]) < 1.0
Ejemplo n.º 8
0
 def test_bell_state(self):
     p = qu.bell_state('psi-')
     assert_allclose(qu.schmidt_gap(p, [2, 2], 0), 0.0)
     p = qu.up() & qu.down()
     assert_allclose(qu.schmidt_gap(p, [2, 2], 0), 1.0)
     p = qu.rand_ket(2**3)
     assert 0 < qu.schmidt_gap(p, [2] * 3, sysa=[0, 1]) < 1.0
Ejemplo n.º 9
0
    def test_quevo_multi_compute(self, method, qtype):

        ham = ham_heis(2, cyclic=False)
        p0 = qu(up() & down(), qtype=qtype)

        def some_quantity(t, _):
            return t

        def some_other_quantity(_, pt):
            return logneg(pt)

        evo = QuEvo(p0,
                    ham,
                    method=method,
                    compute={
                        't': some_quantity,
                        'logneg': some_other_quantity
                    })
        manual_lns = []
        for pt in evo.at_times(np.linspace(0, 1, 6)):
            manual_lns.append(logneg(pt))
        ts = evo.results['t']
        lns = evo.results['logneg']
        assert len(lns) >= len(manual_lns)
        # check a specific value of logneg at t=0.8 was computed automatically
        checked = False
        for t, ln in zip(ts, lns):
            if abs(t - 0.8) < 1e-12:
                assert abs(ln - manual_lns[4]) < 1e-12
                checked = True
        assert checked
Ejemplo n.º 10
0
 def test_mutual_information_pure_sub(self):
     a = qu.up() & qu.bell_state(1)
     ixy = qu.mutual_information(a, [2, 2, 2], 0, 1)
     assert_allclose(0.0, ixy, atol=1e-12)
     ixy = qu.mutual_information(a, [2, 2, 2], 0, 2)
     assert_allclose(0.0, ixy, atol=1e-12)
     ixy = qu.mutual_information(a, [2, 2, 2], 2, 1)
     assert_allclose(2.0, ixy, atol=1e-12)
Ejemplo n.º 11
0
 def test_progbar_update_to_integrate(self, capsys):
     ham = ham_heis(2, cyclic=False)
     p0 = up() & down()
     sim = QuEvo(p0, ham, method='integrate', progbar=True)
     sim.update_to(100)
     # check something as been printed
     _, err = capsys.readouterr()
     assert err and "%" in err
Ejemplo n.º 12
0
def circuit_to_tensors(
    circuit: cirq.Circuit,
    qubits: Optional[Sequence[cirq.Qid]] = None,
    initial_state: Union[int, None] = 0,
) -> Tuple[List[qtn.Tensor], Dict['cirq.Qid', int], None]:
    """Given a circuit, construct a tensor network representation.

    Indices are named "i{i}_q{x}" where i is a time index and x is a
    qubit index.

    Args:
        circuit: The circuit containing operations that implement the
            cirq.unitary() protocol.
        qubits: A list of qubits in the circuit.
        initial_state: Either `0` corresponding to the |0..0> state, in
            which case the tensor network will represent the final
            state vector; or `None` in which case the starting indices
            will be left open and the tensor network will represent the
            circuit unitary.
    Returns:
        tensors: A list of quimb Tensor objects
        qubit_frontier: A mapping from qubit to time index at the end of
            the circuit. This can be used to deduce the names of the free
            tensor indices.
        positions: Currently None. May be changed in the future to return
            a suitable mapping for tn.graph()'s `fix` argument. Currently,
            `fix=None` will draw the resulting tensor network using a spring
            layout.
    """
    if qubits is None:
        qubits = sorted(circuit.all_qubits())  # coverage: ignore

    qubit_frontier = {q: 0 for q in qubits}
    positions = None
    tensors: List[qtn.Tensor] = []

    if initial_state == 0:
        for q in qubits:
            tensors += [qtn.Tensor(data=quimb.up().squeeze(), inds=(f'i0_q{q}',), tags={'Q0'})]
    elif initial_state is None:
        # no input tensors, return a network representing the unitary
        pass
    else:
        raise ValueError("Right now, only |0> or `None` " "initial states are supported.")

    for moment in circuit.moments:
        for op in moment.operations:
            assert op.gate._has_unitary_()
            start_inds = [f'i{qubit_frontier[q]}_q{q}' for q in op.qubits]
            for q in op.qubits:
                qubit_frontier[q] += 1
            end_inds = [f'i{qubit_frontier[q]}_q{q}' for q in op.qubits]

            U = cirq.unitary(op).reshape((2,) * 2 * len(op.qubits))
            t = qtn.Tensor(data=U, inds=end_inds + start_inds, tags={f'Q{len(op.qubits)}'})
            tensors.append(t)

    return tensors, qubit_frontier, positions
Ejemplo n.º 13
0
 def test_evo_at_times(self):
     ham = qu.ham_heis(2, cyclic=False)
     p0 = qu.up() & qu.down()
     sim = qu.Evolution(p0, ham, method='solve')
     ts = np.linspace(0, 10)
     for t, pt in zip(ts, sim.at_times(ts)):
         x = cos(t)
         y = qu.expec(pt, qu.ikron(qu.pauli('z'), [2, 2], 0))
         assert_allclose(x, y, atol=1e-15)
Ejemplo n.º 14
0
 def test_quevo_at_times(self):
     ham = ham_heis(2, cyclic=False)
     p0 = up() & down()
     sim = QuEvo(p0, ham, method='solve')
     ts = np.linspace(0, 10)
     for t, pt in zip(ts, sim.at_times(ts)):
         x = cos(t)
         y = expec(pt, eyepad(pauli('z'), [2, 2], 0))
         assert_allclose(x, y, atol=1e-15)
Ejemplo n.º 15
0
 def test_progbar_at_times_solve(self, capsys):
     ham = qu.ham_heis(2, cyclic=False)
     p0 = qu.up() & qu.down()
     sim = qu.Evolution(p0, ham, method='solve', progbar=True)
     for _ in sim.at_times(np.linspace(0, 100, 11)):
         pass
     # check something as been printed
     _, err = capsys.readouterr()
     assert err and "%" in err
Ejemplo n.º 16
0
 def test_progbar_at_times_expm(self, capsys):
     ham = ham_heis(2, cyclic=False)
     p0 = up() & down()
     sim = QuEvo(p0, ham, method='expm', progbar=True)
     for _ in sim.at_times(np.linspace(0, 100, 11)):
         pass
     # check something as been printed
     _, err = capsys.readouterr()
     assert err and "%" in err
Ejemplo n.º 17
0
    def test_quevo_compute_callback(self, qtype, method):
        ham = ham_heis(2, cyclic=False)
        p0 = qu(up() & down(), qtype=qtype)

        def some_quantity(t, pt):
            return t, logneg(pt)

        evo = QuEvo(p0, ham, method=method, compute=some_quantity)
        manual_lns = []
        for pt in evo.at_times(np.linspace(0, 1, 6)):
            manual_lns.append(logneg(pt))
        ts, lns = zip(*evo.results)
        assert len(lns) >= len(manual_lns)
        # check a specific value of logneg at t=0.8 was computed automatically
        checked = False
        for t, ln in zip(ts, lns):
            if abs(t - 0.8) < 1e-12:
                assert abs(ln - manual_lns[4]) < 1e-12
                checked = True
        assert checked
Ejemplo n.º 18
0
def tensor_expectation_value(circuit: cirq.Circuit,
                             pauli_string: cirq.PauliString,
                             max_ram_gb=16,
                             tol=1e-6) -> float:
    """Compute an expectation value for an operator and a circuit via tensor
    contraction.

    This will give up if it looks like the computation will take too much RAM.
    """
    circuit_sand = circuit_for_expectation_value(
        circuit, pauli_string / pauli_string.coefficient)
    qubits = sorted(circuit_sand.all_qubits())

    tensors, qubit_frontier, _ = circuit_to_tensors(circuit=circuit_sand,
                                                    qubits=qubits)
    end_bras = [
        qtn.Tensor(data=quimb.up().squeeze(),
                   inds=(f'i{qubit_frontier[q]}_q{q}', ),
                   tags={'Q0', 'bra0'}) for q in qubits
    ]
    tn = qtn.TensorNetwork(tensors + end_bras)
    if QUIMB_VERSION[0] < (1, 3):
        # coverage: ignore
        warnings.warn(f'quimb version {QUIMB_VERSION[1]} detected. Please use '
                      f'quimb>=1.3 for optimal performance in '
                      '`tensor_expectation_value`. '
                      'See https://github.com/quantumlib/Cirq/issues/3263')
    else:
        tn.rank_simplify(inplace=True)
    path_info = tn.contract(get='path-info')
    ram_gb = path_info.largest_intermediate * 128 / 8 / 1024 / 1024 / 1024
    if ram_gb > max_ram_gb:
        raise MemoryError(
            f"We estimate that this contraction will take too much RAM! {ram_gb} GB"
        )
    e_val = tn.contract(inplace=True)
    assert e_val.imag < tol
    assert pauli_string.coefficient.imag < tol
    return e_val.real * pauli_string.coefficient
Ejemplo n.º 19
0
    def test_evo_multi_compute(self, method, qtype):

        ham = qu.ham_heis(2, cyclic=False)
        p0 = qu.qu(qu.up() & qu.down(), qtype=qtype)

        def some_quantity(t, _):
            return t

        def some_other_quantity(_, pt):
            return qu.logneg(pt)

        # check that hamiltonian gets accepted without error for all methods
        def some_other_quantity_accepting_ham(t, pt, H):
            return qu.logneg(pt)

        compute = {
            't': some_quantity,
            'logneg': some_other_quantity,
            'logneg_ham': some_other_quantity_accepting_ham
        }

        evo = qu.Evolution(p0, ham, method=method, compute=compute)
        manual_lns = []
        for pt in evo.at_times(np.linspace(0, 1, 6)):
            manual_lns.append(qu.logneg(pt))
        ts = evo.results['t']
        lns = evo.results['logneg']
        lns_ham = evo.results['logneg_ham']
        assert len(lns) >= len(manual_lns)
        # check a specific value of logneg at t=0.8 was computed automatically
        checked = False
        for t, ln, ln_ham in zip(ts, lns, lns_ham):
            if abs(t - 0.8) < 1e-12:
                assert abs(ln - manual_lns[4]) < 1e-12
                # check that accepting hamiltonian didn't mess it up
                assert ln == ln_ham
                checked = True
        assert checked
Ejemplo n.º 20
0
class TestDecomp:
    @pytest.mark.parametrize("qtype", ['ket', 'dop'])
    def test_pauli_decomp_singlet(self, qtype):
        p = qu.singlet(qtype=qtype)
        names_cffs = qu.pauli_decomp(p, mode='cp')
        assert_allclose(names_cffs['II'], 0.25)
        assert_allclose(names_cffs['ZZ'], -0.25)
        assert_allclose(names_cffs['YY'], -0.25)
        assert_allclose(names_cffs['ZZ'], -0.25)
        for name in itertools.permutations('IXYZ', 2):
            assert_allclose(names_cffs["".join(name)], 0.0)

    def test_pauli_reconstruct(self):
        p1 = qu.rand_rho(4)
        names_cffs = qu.pauli_decomp(p1, mode='c')
        pr = sum(
            qu.kron(*(qu.pauli(s) for s in name)) * names_cffs["".join(name)]
            for name in itertools.product('IXYZ', repeat=2))
        assert_allclose(pr, p1)

    @pytest.mark.parametrize("state, out", [(qu.up() & qu.down(), {
        0: 0.5,
        1: 0.5,
        2: 0,
        3: 0
    }), (qu.down() & qu.down(), {
        0: 0,
        1: 0,
        2: 0.5,
        3: 0.5
    }), (qu.singlet() & qu.singlet(), {
        '00': 1.0,
        '23': 0.0
    })])
    def test_bell_decomp(self, state, out):
        names_cffs = qu.bell_decomp(state, mode='c')
        for key in out:
            assert_allclose(names_cffs[str(key)], out[key])
Ejemplo n.º 21
0
 def test_up(self):
     p = up(qtype='dop')
     assert_allclose(tr(p @ pauli('z')), 1.0)
Ejemplo n.º 22
0
 def test_permute_ket(self):
     a = qu.up() & qu.plus() & qu.yplus()
     b = qu.permute(a, [2, 2, 2], [2, 0, 1])
     assert_allclose(b, qu.yplus() & qu.up() & qu.plus())
Ejemplo n.º 23
0
 def test_pure(self):
     rho = qu.up(qtype='dop')
     psi = qu.purify(rho)
     assert abs(qu.concurrence(psi)) < 1e-14
Ejemplo n.º 24
0
 def test_swap_qubits(self, sparse):
     a = up() & down()
     s = swap(2, sparse=sparse)
     assert_allclose(s @ a, down() & up())
Ejemplo n.º 25
0
 def test_distinguishable(self, uqtype, dqtype):
     assert qu.trace_distance(qu.up(qtype=uqtype),
                              qu.down(qtype=dqtype)) > 1 - 1e-10
Ejemplo n.º 26
0
 def test_classically_no_correlated(self, dir, qtype, pre_c):
     p = up(qtype=qtype) & up(qtype=qtype)
     c = correlation(p, pauli(dir), pauli(dir), 0, 1, precomp_func=pre_c)
     c = c(p) if pre_c else c
     assert_allclose(c, 0.0)
Ejemplo n.º 27
0
 def test_classically_correlated(self, dir, ct, pre_c):
     p = 0.5 * ((up(qtype='dop') & up(qtype='dop')) +
                (down(qtype='dop') & down(qtype='dop')))
     c = correlation(p, pauli(dir), pauli(dir), 0, 1, precomp_func=pre_c)
     c = c(p) if pre_c else c
     assert_allclose(c, ct)
Ejemplo n.º 28
0
def circuit_to_density_matrix_tensors(
    circuit: cirq.Circuit,
    qubits: Optional[Sequence[cirq.Qid]] = None
) -> Tuple[List[qtn.Tensor], Dict['cirq.Qid', int], Dict[Tuple[str, str],
                                                         Tuple[float, float]]]:
    """Given a circuit with mixtures or channels, construct a tensor network
    representation of the density matrix.

    This assumes you start in the |0..0><0..0| state. Indices are named
    "nf{i}_q{x}" and "nb{i}_q{x}" where i is a time index and x is a
    qubit index. nf- and nb- refer to the "forwards" and "backwards"
    copies of the circuit. Kraus indices are named "k{j}" where j is an
    independent "kraus" internal index which you probably never need to access.

    Args:
        circuit: The circuit containing operations that support the
            cirq.unitary() or cirq.kraus() protocols.
        qubits: The qubits in the circuit. The `positions` return argument
            will position qubits according to their index in this list.

    Returns:
        tensors: A list of Quimb Tensor objects
        qubit_frontier: A mapping from qubit to time index at the end of
            the circuit. This can be used to deduce the names of the free
            tensor indices.
        positions: A positions dictionary suitable for passing to tn.graph()'s
            `fix` argument to draw the resulting tensor network similar to a
            quantum circuit.

    Raises:
        ValueError: If an op is encountered that cannot be converted.
    """
    if qubits is None:
        # coverage: ignore
        qubits = sorted(circuit.all_qubits())
    qubits = tuple(qubits)

    qubit_frontier: Dict[cirq.Qid, int] = {q: 0 for q in qubits}
    kraus_frontier = 0
    positions: Dict[Tuple[str, str], Tuple[float, float]] = {}
    tensors: List[qtn.Tensor] = []

    x_scale = 2
    y_scale = 3
    x_nudge = 0.3
    n_qubits = len(qubits)
    yb_offset = (n_qubits + 0.5) * y_scale

    def _positions(_mi, _these_qubits):
        return _add_to_positions(
            positions,
            _mi,
            _these_qubits,
            all_qubits=qubits,
            x_scale=x_scale,
            y_scale=y_scale,
            x_nudge=x_nudge,
            yb_offset=yb_offset,
        )

    # Initialize forwards and backwards qubits into the 0 state, i.e. prepare
    # rho_0 = |0><0|.
    for q in qubits:
        tensors += [
            qtn.Tensor(data=quimb.up().squeeze(),
                       inds=(f'nf0_q{q}', ),
                       tags={'Q0', 'i0f', _qpos_tag(q)}),
            qtn.Tensor(data=quimb.up().squeeze(),
                       inds=(f'nb0_q{q}', ),
                       tags={'Q0', 'i0b', _qpos_tag(q)}),
        ]
        _positions(0, q)

    for mi, moment in enumerate(circuit.moments):
        for op in moment.operations:
            start_inds_f = [f'nf{qubit_frontier[q]}_q{q}' for q in op.qubits]
            start_inds_b = [f'nb{qubit_frontier[q]}_q{q}' for q in op.qubits]
            for q in op.qubits:
                qubit_frontier[q] += 1
            end_inds_f = [f'nf{qubit_frontier[q]}_q{q}' for q in op.qubits]
            end_inds_b = [f'nb{qubit_frontier[q]}_q{q}' for q in op.qubits]

            if cirq.has_unitary(op):
                U = cirq.unitary(op).reshape(
                    (2, ) * 2 * len(op.qubits)).astype(np.complex128)
                tensors.append(
                    qtn.Tensor(
                        data=U,
                        inds=end_inds_f + start_inds_f,
                        tags={
                            f'Q{len(op.qubits)}', f'i{mi + 1}f',
                            _qpos_tag(op.qubits)
                        },
                    ))
                tensors.append(
                    qtn.Tensor(
                        data=np.conj(U),
                        inds=end_inds_b + start_inds_b,
                        tags={
                            f'Q{len(op.qubits)}', f'i{mi + 1}b',
                            _qpos_tag(op.qubits)
                        },
                    ))
            elif cirq.has_kraus(op):
                K = np.asarray(cirq.kraus(op), dtype=np.complex128)
                kraus_inds = [f'k{kraus_frontier}']
                tensors.append(
                    qtn.Tensor(
                        data=K,
                        inds=kraus_inds + end_inds_f + start_inds_f,
                        tags={
                            f'kQ{len(op.qubits)}', f'i{mi + 1}f',
                            _qpos_tag(op.qubits)
                        },
                    ))
                tensors.append(
                    qtn.Tensor(
                        data=np.conj(K),
                        inds=kraus_inds + end_inds_b + start_inds_b,
                        tags={
                            f'kQ{len(op.qubits)}', f'i{mi + 1}b',
                            _qpos_tag(op.qubits)
                        },
                    ))
                kraus_frontier += 1
            else:
                raise ValueError(repr(op))  # coverage: ignore

            _positions(mi + 1, op.qubits)
    return tensors, qubit_frontier, positions
Ejemplo n.º 29
0
 def test_swap_qubits(self, sparse):
     a = qu.up() & qu.down()
     s = qu.swap(2, sparse=sparse)
     assert_allclose(s @ a, qu.down() & qu.up())
Ejemplo n.º 30
0
 def test_n2(self):
     p = perm_state([up(), down()])
     assert_allclose(p, bell_state('psi-'))