def test_mppovm_sample( method, n_samples, nr_sites, startsite, local_dim, rgen): """Check that probability estimates from samples are reasonable accurate""" rank = 3 eps = 1e-10 mps = factory.random_mps(nr_sites, local_dim, rank, rgen) mps.canonicalize() local_x = povm.x_povm(local_dim) local_y = povm.y_povm(local_dim) xx = povm.MPPovm.from_local_povm(local_x, 2) y = povm.MPPovm.from_local_povm(local_y, 1) mpp = mp.chain([xx, povm.MPPovm.eye([local_dim]), y]) \ .embed(nr_sites, startsite, local_dim) pmf_exact = mpp.pmf_as_array(mps, 'mps', eps) if n_samples > 100: n_gr = 5 elif local_dim == 3: n_gr = 2 else: n_gr = 3 samples = mpp.sample(rgen, mps, n_samples, method, n_gr, 'mps', eps=eps) pmf_est = mpp.est_pmf(samples) assert abs(pmf_est.sum() - 1.0) <= eps assert abs(pmf_exact - pmf_est).max() <= 3 / n_samples**0.5
def test_reductions_mps(nr_sites, local_dim, rank, width, rgen): mps = factory.random_mps(nr_sites, local_dim, rank, randstate=rgen) mpo = mp.localouter(mps, mps.conj()) pmps_reds = mm.reductions_mps_as_mpo(mps, width) mpo_reds = mm.reductions_mpo(mpo, width) for red1, red2 in zip(pmps_reds, mpo_reds): assert_array_almost_equal(red1.to_array(), red2.to_array())
def test_reductions_mps(nr_sites, local_dim, rank, width, rgen): mps = factory.random_mps(nr_sites, local_dim, rank, randstate=rgen) mpo = mp.localouter(mps, mps.conj()) pmps_reds = mm.reductions_mps_as_mpo(mps, width) mpo_reds = mm.reductions_mpo(mpo, width) for red1, red2 in zip(pmps_reds, mpo_reds): assert_array_almost_equal(red1.to_array(), red2.to_array())
def test_mppovm_expectation_pure(nr_sites, width, local_dim, rank, rgen): paulis = povm.pauli_povm(local_dim) mppaulis = povm.MPPovm.from_local_povm(paulis, width) psi = factory.random_mps(nr_sites, local_dim, rank, randstate=rgen) rho = mpsmpo.mps_to_mpo(psi) expect_psi = list(mppaulis.expectations(psi)) expect_rho = list(mppaulis.expectations(rho)) assert len(expect_psi) == len(expect_rho) for e_rho, e_psi in zip(expect_rho, expect_psi): assert_array_almost_equal(e_rho.to_array(), e_psi.to_array())
def test_mps_to_mpo(nr_sites, local_dim, rank, rgen): mps = factory.random_mps(nr_sites, local_dim, rank, randstate=rgen) # Instead of calling the two functions, we call mps_to_mpo(), # which does exactly that: # mps_as_puri = mp.mps_as_local_purification_mps(mps) # mpo = mp.pmps_to_mpo(mps_as_puri) mpo = mm.mps_to_mpo(mps) # This is also a test of mp.mps_as_local_purification_mps() in the # following sense: Local purifications are representations of # mixed states. Therefore, compare mps and mps_as_puri by # converting them to mixed states. state = mps.to_array() state = np.outer(state, state.conj()) state.shape = (local_dim,) * (2 * nr_sites) state2 = mpo.to_array_global() assert_array_almost_equal(state, state2)
def test_mps_to_mpo(nr_sites, local_dim, rank, rgen): mps = factory.random_mps(nr_sites, local_dim, rank, randstate=rgen) # Instead of calling the two functions, we call mps_to_mpo(), # which does exactly that: # mps_as_puri = mp.mps_as_local_purification_mps(mps) # mpo = mp.pmps_to_mpo(mps_as_puri) mpo = mm.mps_to_mpo(mps) # This is also a test of mp.mps_as_local_purification_mps() in the # following sense: Local purifications are representations of # mixed states. Therefore, compare mps and mps_as_puri by # converting them to mixed states. state = mps.to_array() state = np.outer(state, state.conj()) state.shape = (local_dim,) * (2 * nr_sites) state2 = mpo.to_array_global() assert_array_almost_equal(state, state2)
def test_mppovmlist_pack_unpack_samples( method, n_samples, nr_sites, local_dim, rank, measure_width, rgen, eps=1e-10): """Check that packing and unpacking samples does not change them""" mps = factory.random_mps(nr_sites, local_dim, rank, rgen) mps.canonicalize() s_povm = povm.pauli_mpp(measure_width, local_dim).block(nr_sites) samples = tuple(s_povm.sample( rgen, mps, n_samples, method, mode='mps', pack=False, eps=eps)) packed = tuple(s_povm.pack_samples(samples)) unpacked = tuple(s_povm.unpack_samples(packed)) assert all(s.dtype == np.uint8 for s in samples) assert all(s.dtype == np.uint8 for s in unpacked) assert all((s == u).all() for s, u in zip(samples, unpacked))
def test_mppovm_est_pmf_from( method, n_samples, nr_sites, startsite, local_dim, rgen): """Check that probability estimates from samples are reasonable accurate""" rank = 3 eps = 1e-10 mps = factory.random_mps(nr_sites, local_dim, rank, rgen) mps.canonicalize() lx = povm.x_povm(local_dim) ly = povm.y_povm(local_dim) lp = povm.pauli_povm(local_dim) x = povm.MPPovm.from_local_povm(lx, 1) y = povm.MPPovm.from_local_povm(ly, 1) pauli = povm.MPPovm.from_local_povm(lp, 1) xy = mp.chain((x, y)) mpp = mp.chain((xy,) * (nr_sites // 2)) if (nr_sites % 2) == 1: mpp = mp.chain((mpp, x)) small_mpp = mp.chain((pauli, povm.MPPovm.eye([local_dim]), pauli, pauli)) \ .embed(nr_sites, startsite, local_dim) x_given = np.arange(len(lp)) < len(lx) y_given = ((np.arange(len(lp)) >= len(lx)) & (np.arange(len(lp)) < len(lx) + len(ly))) given_sites = [x_given if ((startsite + i) % 2) == 0 else y_given for i in (0, 2, 3)] given_expected = np.einsum('i, j, k -> ijk', *given_sites) pmf_exact = small_mpp.pmf_as_array(mps, 'mps', eps) if n_samples > 100: n_gr = 5 elif local_dim == 3: n_gr = 2 else: n_gr = 3 samples = mpp.sample(rgen, mps, n_samples, method, n_gr, 'mps', eps=eps) est_pmf, est_n_samples = small_mpp.est_pmf_from(mpp, samples) # In this case, we use all the samples from `mpp`. assert est_n_samples == n_samples given = ~np.isnan(est_pmf) assert (given == given_expected).all() assert abs(pmf_exact[given].sum() - est_pmf[given].sum()) <= eps assert abs(pmf_exact[given] - est_pmf[given]).max() <= 1 / n_samples**0.5
def test_mppovmlist_est_pmf_from( method, n_samples, nr_sites, local_dim, rank, measure_width, local_width, nonuniform, splitpauli, rgen, eps=1e-10): """Verify that estimated probabilities from MPPovmList.est_pmf_from() are reasonable accurate """ mps = factory.random_mps(nr_sites, local_dim, rank, rgen) mps.canonicalize() x, y = (povm.MPPovm.from_local_povm(p, 1) for p in povm.pauli_parts(local_dim)[:2]) # POVM list with global support g_povm = povm.pauli_mpps(measure_width, local_dim).repeat(nr_sites) if nonuniform: add_povm = mp.chain((nr_sites - 1) * (x,) + (y,)) g_povm = povm.MPPovmList(g_povm.mpps + (add_povm,)) # POVM list with local support l_povm = povm.pauli_mpps if splitpauli else povm.pauli_mpp l_povm = l_povm(local_width, local_dim).block(nr_sites) samples = tuple(g_povm.sample( rgen, mps, n_samples, method, mode='mps', eps=eps)) est_prob, n_samples = zip(*l_povm.est_pmf_from(g_povm, samples, eps)) exact_prob = tuple(l_povm.pmf_as_array(mps, 'mps', eps)) # Consistency check on n_samples: All entries should be equal # unless `nonuniform` is True. all_n_sam = np.concatenate(n_samples) assert (not (all_n_sam == all_n_sam[0]).all()) == nonuniform for n_sam, est, exact, mpp in zip( n_samples, est_prob, exact_prob, l_povm.mpps): assert est.shape == mpp.nsoutdims assert est.shape == exact.shape assert n_sam.shape == exact.shape # Compare against exact probabilities assert (abs(est - exact) / (3 / n_sam**0.5)).max() <= 1
def test_mppovmlist_est_lfun_from( method, n_samples, nr_sites, local_dim, rank, measure_width, local_width, nonuniform, function, povm_combo, rgen, eps=1e-10): """Verify that estimated probabilities from MPPovmList.est_pmf_from() are reasonable accurate .. todo:: This test is too long and should be split into several smaller tests. Also, some of the testing done here is redundant. """ mps = factory.random_mps(nr_sites, local_dim, rank, rgen) mps.canonicalize() sample_povm, fun_povm = povm_combo estimation_impossible = (sample_povm == "all-y" and fun_povm in {"local-x", "pauli"}) fromself = sample_povm == fun_povm and measure_width == local_width # s_povm: POVM used to obtain samples s_povm = _get_povm(sample_povm, nr_sites, local_dim, measure_width) # f_povm: POVM on which a linear function is defined if fromself: f_povm = s_povm else: f_povm = _get_povm(fun_povm, nr_sites, local_dim, local_width) if function == 'rand': def coeff(x): return rgen.rand(*x) elif function == 'randn': def coeff(x): return rgen.randn(*x) elif function == 'ones': def coeff(x): return np.ones(x) elif function == 'signs': def coeff(x): return rgen.choice([1., -1.], x) else: raise ValueError('Unknown function {!r}'.format(function)) # More POVMs in s_povm means more samples. Consider this in the tests. n_samples_eff = n_samples * len(s_povm.mpps) # We divide the coefficients by len(f_povm.mpps) to make the # estimated value have approximately same magnitude, independently # of len(f_povm.mpps). coeff = [coeff(mpp.nsoutdims) / len(f_povm.mpps) for mpp in f_povm.mpps] samples = tuple(s_povm.sample( rgen, mps, n_samples, method, mode='mps', eps=eps)) exact_prob = tuple(f_povm.pmf_as_array(mps, 'mps', eps)) # Compute exact estimate directly exact_est1, exact_var1 = f_povm.lfun([c.ravel() for c in coeff], None, mps, 'mps', eps) # Compute exact estimate and variance using the other POVM exact_est2, exact_var2 = f_povm.lfun_from(s_povm, coeff, mps, 'mps', eps=eps) if estimation_impossible: assert np.isnan(exact_est2) assert np.isnan(exact_var2) else: # Estimates must agree. assert abs(exact_est1 - exact_est2) <= eps if fromself: # Variances can be different unless f_povm and s_povm are the same. assert abs(exact_var1 - exact_var2) <= eps est, var = f_povm.est_lfun_from(s_povm, coeff, samples, eps) if fromself: # In this case, est_lfun() and est_lfun_from() must give exactly # the same result. est2, var2 = f_povm.est_lfun([c.ravel() for c in coeff], None, samples, eps) assert abs(est - est2) <= eps assert abs(var - var2) <= eps # We use est_pmf() to test est_pmf_from() # again. MPPovmList.est_pmf() just aggregates results from # MPPovm.est_pmf(). pmf1 = f_povm.est_pmf(samples, normalized=True, eps=eps) pmf2, _ = zip(*f_povm.est_pmf_from(s_povm, samples, eps=eps)) assert all(abs(p1 - p2).max() <= eps for p1, p2 in zip(pmf1, pmf2)) # The final estimator is based on the samples for # `s_povm`. Therefore, it is correct to use `n_samples_eff` below # (and not the "effective samples" for the `f_povm` probability # estimation returned by :func:`f_povm.est_pmf_from()`). exact_est = sum(np.inner(c.flat, p.flat) for c, p in zip(coeff, exact_prob)) if estimation_impossible: assert np.isnan(est) else: assert abs(exact_est - exact_est2) <= eps bound = 20 if fun_povm == 'all-y' else 6 assert abs(est - exact_est) <= bound / n_samples_eff**0.5 if function == 'ones': assert abs(exact_est - 1) <= eps assert abs(est - exact_est) <= eps # The code below will only work for small systems. Probably # nr_sites = 16 will work, but let's stay safe. assert nr_sites <= 8, "Larger systems will require a lot of memory" # Use the estimator from `f_povm._estfun_from_estimator()` to # compute the exact variance of the estimate. We can assume that # estimator is mostly correct because we have checked that it # produces accurate estimates (for large numbers of samples) # above. # # FIXME: Drop the exact variance computation here and use # exact_var2 from above. # # Convert from matching functions + coefficients to coefficients # for each probability. n_samples2 = [s.shape[0] for s in samples] _, est_coeff, est_funs = f_povm._lfun_estimator(s_povm, coeff, n_samples2, eps) est_p_coeff = [np.zeros(mpp.nsoutdims, float) for mpp in s_povm.mpps] for fun_coeff, funs, p_coeff, mpp in zip( est_coeff, est_funs, est_p_coeff, s_povm.mpps): out = np.unravel_index(range(np.prod(mpp.nsoutdims)), mpp.nsoutdims) out = np.array(out).T.copy() for c, fun in zip(fun_coeff, funs): match = fun(out) p_coeff.flat[match] += c exact_prob = tuple(s_povm.pmf_as_array(mps, 'mps', eps)) exact_p_cov = (np.diag(p.flat) - np.outer(p.flat, p.flat) for p in exact_prob) exact_var = sum(np.inner(c.flat, np.dot(cov, c.flat)) for c, cov in zip(est_p_coeff, exact_p_cov)) if estimation_impossible: assert np.isnan(var) else: assert abs(exact_var - exact_var2) <= eps if fromself: # `f_povm` and `s_povm` are equal. We must obtain exactly the # same result without using the matching functions from above: exact_prob = tuple(f_povm.pmf_as_array(mps, 'mps', eps)) exact_p_cov = (np.diag(p.flat) - np.outer(p.flat, p.flat) for p in exact_prob) exact_var2 = sum(np.inner(c.flat, np.dot(cov, c.flat)) for c, cov in zip(coeff, exact_p_cov)) assert abs(exact_var - exact_var2) <= eps # Convert variance to variance of the estimator (=average) exact_var /= n_samples if sample_povm == 'pauli': bound = 6 elif fun_povm == 'all-y': bound = 10 else: bound = 1 assert n_samples * abs(var - exact_var) <= bound / n_samples_eff**0.5 if function == 'ones': assert abs(exact_var) <= eps assert abs(var - exact_var) <= eps
def test_mppovm_est( method, n_samples, nr_sites, startsite, local_dim, rgen): """Check that estimates from .est_pmf() and .est_lfun() are reasonably accurate """ rank = 3 eps = 1e-10 mps = factory.random_mps(nr_sites, local_dim, rank, rgen) mps.canonicalize() local_x = povm.x_povm(local_dim) local_y = povm.y_povm(local_dim) xx = povm.MPPovm.from_local_povm(local_x, 2) y = povm.MPPovm.from_local_povm(local_y, 1) mpp = mp.chain([xx, povm.MPPovm.eye([local_dim]), y]) \ .embed(nr_sites, startsite, local_dim) p_exact = mpp.pmf_as_array(mps, 'mps', eps) p_exact = project_pmf(p_exact, eps, eps) cov_p_exact = np.diag(p_exact.flat) - np.outer(p_exact.flat, p_exact.flat) samples = mpp.sample(rgen, mps, n_samples, method, 4, 'mps', eps=eps) p_est = mpp.est_pmf(samples) ept, cov = mpp.est_lfun(None, None, samples, None, eps) ept_ex, single_cov_ex = mpp.lfun(None, None, mps, 'mps', eps) # The two exact values must match assert abs(ept_ex - p_exact.ravel()).max() <= eps # The two exact values must match assert abs(cov_p_exact - single_cov_ex).max() <= eps # The two estimates must match. This verifies that we have chosen # our estimator will be unbiased. (There are many other things we # might want to know about our estimator.) assert (ept == p_est.ravel()).all() # The estimate must be close to the true value assert abs(p_exact - p_est).max() <= 3 / n_samples**0.5 cov_ex = cov_p_exact / n_samples # The covariances of the sample means (which we estimate here) # decrease by 1/n_samples, so we multiply with n_samples before # comparing to the rule-of-thumb for the estimation error. assert abs(cov - cov_ex).max() * n_samples <= 1 / n_samples**0.5 funs = [] nsoutdims = mpp.nsoutdims out = np.unravel_index(range(np.prod(nsoutdims)), nsoutdims) out = np.array(out).T[:, None, :].copy() for ind in range(np.prod(nsoutdims)): funs.append(lambda s, ind=ind: (s == out[ind]).all(1)) # All probabilities sum to one, and we can estimate that well. coeff = np.ones(len(funs), dtype=float) # Test with dummy weights weights = np.ones(n_samples, dtype=float) sum_ept, sum_var = mpp.est_lfun(coeff, funs, samples, weights, eps) assert abs(sum_ept - 1.0) <= eps assert sum_var <= eps # Check a sum of probabilities with varying signs. coeff = ((-1)**rgen.choice(2, len(funs))).astype(float) sum_ept, sum_var = mpp.est_lfun(coeff, funs, samples, None, eps) ex_sum = np.inner(coeff, p_exact.flat) ex_var = np.inner(coeff, np.dot(cov_ex, coeff)) assert abs(sum_ept - ex_sum) <= 5 / n_samples**0.5 assert abs(sum_var - ex_var) * n_samples <= 5 / n_samples**0.5 # Convert samples to counts and test again counts = mpp.est_pmf(samples, normalize=False, eps=eps) assert counts.sum() == n_samples count_samples = np.array(np.unravel_index(range(np.prod(mpp.nsoutdims)), mpp.nsoutdims)).T weights = counts.ravel() sum_ept2, sum_var2 = mpp.est_lfun(coeff, funs, count_samples, weights, eps) assert abs(sum_ept - sum_ept2) <= eps assert abs(sum_var - sum_var2) <= eps