inline TR sysperm(const T1& rho1, const arma::uvec& sys, const arma::uvec& dim) { const auto& p = as_Mat(rho1); const arma::uword n = dim.n_elem; bool checkV = true; if (p.n_cols == 1) checkV = false; #ifndef QICLIB_NO_DEBUG if (p.n_elem == 0) throw Exception("qic::sysperm", Exception::type::ZERO_SIZE); if (checkV) if (p.n_rows != p.n_cols) throw Exception("qic::sysperm", Exception::type::MATRIX_NOT_SQUARE_OR_CVECTOR); if (dim.n_elem == 0 || arma::any(dim == 0)) throw Exception("qic::sysperm", Exception::type::INVALID_DIMS); if (arma::prod(dim) != p.n_rows) throw Exception("qic::sysperm", Exception::type::DIMS_MISMATCH_MATRIX); if (n != sys.n_elem || arma::any(sys == 0) || arma::any(sys > n) || sys.n_elem != arma::unique(sys).eval().n_elem) throw Exception("qic::sysperm", Exception::type::PERM_INVALID); #endif arma::uword product[_internal::MAXQDIT]; product[n-1] = 1; for (arma::sword i = n - 2; i >= 0; --i) product[i] = product[i + 1] * dim.at(i + 1); arma::uword productr[_internal::MAXQDIT]; productr[n-1] = 1; for (arma::sword i = n - 2; i >= 0; --i) productr[i] = productr[i + 1] * dim.at(sys.at(i + 1) - 1); if (checkV) { arma::Mat<trait::eT<T1> > p_r(p.n_rows, p.n_cols, arma::fill::zeros); const arma::uword loop_no = 2 * n; constexpr auto loop_no_buffer = 2 * _internal::MAXQDIT + 1; arma::uword loop_counter[loop_no_buffer] = {0}; arma::uword MAX[loop_no_buffer]; for (arma::uword i = 0; i < n; ++i) { MAX[i] = dim.at(i); MAX[i + n] = dim.at(i); } MAX[loop_no] = 2; arma::uword p1 = 0; while (loop_counter[loop_no] == 0) { arma::uword I(0), J(0), K(0), L(0); for (arma::uword i = 0; i < n; ++i) { I += product[i] * loop_counter[i]; J += product[i] * loop_counter[i + n]; K += productr[i] * loop_counter[sys.at(i) - 1]; L += productr[i] * loop_counter[sys.at(i) + n - 1]; } p_r.at(K, L) = p.at(I, J); ++loop_counter[0]; while (loop_counter[p1] == MAX[p1]) { loop_counter[p1] = 0; loop_counter[++p1]++; if (loop_counter[p1] != MAX[p1]) p1 = 0; } } return p_r; } else { arma::Col<trait::eT<T1> > p_r(p.n_rows, arma::fill::zeros); const arma::uword loop_no = n; constexpr auto loop_no_buffer = _internal::MAXQDIT + 1; arma::uword loop_counter[loop_no_buffer] = {0}; arma::uword MAX[loop_no_buffer]; for (arma::uword i = 0; i < n; ++i) MAX[i] = dim.at(i); MAX[loop_no] = 2; for (arma::uword i = 0; i < loop_no + 1; ++i) loop_counter[i] = 0; arma::uword p1 = 0; while (loop_counter[loop_no] == 0) { arma::uword I(0), K(0); for (arma::uword i = 0; i < n; ++i) { I += product[i] * loop_counter[i]; K += productr[i] * loop_counter[sys.at(i) - 1]; } p_r.at(K) = p.at(I); ++loop_counter[0]; while (loop_counter[p1] == MAX[p1]) { loop_counter[p1] = 0; loop_counter[++p1]++; if (loop_counter[p1] != MAX[p1]) p1 = 0; } } return p_r; } }
inline TR TrX(const T1& rho1, arma::uvec sys, arma::uvec dim) { const auto& p = as_Mat(rho1); bool checkV = true; if (p.n_cols == 1) checkV = false; #ifndef QICLIB_NO_DEBUG if (p.n_elem == 0) throw Exception("qic::TrX", Exception::type::ZERO_SIZE); if (checkV) if (p.n_rows != p.n_cols) throw Exception("qic::TrX", Exception::type::MATRIX_NOT_SQUARE_OR_CVECTOR); if (dim.n_elem == 0 || arma::any(dim == 0)) throw Exception("qic::TrX", Exception::type::INVALID_DIMS); if (arma::prod(dim) != p.n_rows) throw Exception("qic::TrX", Exception::type::DIMS_MISMATCH_MATRIX); if (dim.n_elem < sys.n_elem || arma::any(sys == 0) || arma::any(sys > dim.n_elem) || sys.n_elem != arma::find_unique(sys, false).eval().n_elem) throw Exception("qic::TrX", Exception::type::INVALID_SUBSYS); #endif if (sys.n_elem == dim.n_elem) return {arma::trace(p)}; _internal::dim_collapse_sys(dim, sys); const arma::uword n = dim.n_elem; const arma::uword m = sys.n_elem; arma::uvec keep(n - m); arma::uword keep_count(0); for (arma::uword run = 0; run < n; ++run) { if (!arma::any(sys == run + 1)) { keep.at(keep_count) = run + 1; ++keep_count; } } arma::uword dimtrace = arma::prod(dim(sys - 1)); arma::uword dimkeep = p.n_rows / dimtrace; arma::uvec product(n, arma::fill::ones); for (arma::sword i = n - 2; i > -1; --i) product.at(i) = product.at(i + 1) * dim.at(i + 1); arma::uvec productr(n - m, arma::fill::ones); for (arma::sword i = n - m - 2; i > -1; --i) productr.at(i) = productr.at(i + 1) * dim.at(keep.at(i + 1) - 1); arma::Mat<trait::eT<T1> > tr_p(dimkeep, dimkeep, arma::fill::zeros); const arma::uword loop_no = 2 * n; arma::uword* loop_counter = new arma::uword[loop_no + 1]; arma::uword* MAX = new arma::uword[loop_no + 1]; for (arma::uword i = 0; i < n; ++i) { MAX[i] = dim.at(i); if (arma::any(sys == (i + 1))) MAX[i + n] = 1; else MAX[i + n] = dim.at(i); } MAX[loop_no] = 2; for (arma::uword i = 0; i < loop_no + 1; ++i) loop_counter[i] = 0; arma::uword p1 = 0; while (loop_counter[loop_no] == 0) { arma::uword I(0), J(0), K(0), L(0), n_to_k(0); for (arma::uword i = 0; i < n; ++i) { if (arma::any(sys == i + 1)) { I += product.at(i) * loop_counter[i]; J += product.at(i) * loop_counter[i]; } else { I += product.at(i) * loop_counter[i]; J += product.at(i) * loop_counter[i + n]; } if (arma::any(keep == i + 1)) { K += productr.at(n_to_k) * loop_counter[i]; L += productr.at(n_to_k) * loop_counter[i + n]; ++n_to_k; } } tr_p.at(K, L) += checkV ? p.at(I, J) : p.at(I) * std::conj(p.at(J)); ++loop_counter[0]; while (loop_counter[p1] == MAX[p1]) { loop_counter[p1] = 0; loop_counter[++p1]++; if (loop_counter[p1] != MAX[p1]) p1 = 0; } } delete[] loop_counter; delete[] MAX; return tr_p; }
TR discord3_reg(const T1& rho1, arma::uword nodal, arma::uvec dim) { const auto& rho = as_Mat(rho1); arma::uword party_no = dim.n_elem; arma::uword dim1 = arma::prod(dim); #ifndef QICLIB_NO_DEBUG if (rho.n_elem == 0) throw Exception("qic::discord3_reg", Exception::type::ZERO_SIZE); if (rho.n_rows != rho.n_cols) throw Exception("qic::discord3_reg", Exception::type::MATRIX_NOT_SQUARE); if (any(dim == 0)) throw Exception("qic::discord3_reg", Exception::type::INVALID_DIMS); if (dim1 != rho.n_rows) throw Exception("qic::discord3_reg", Exception::type::DIMS_MISMATCH_MATRIX); if (nodal <= 0 || nodal > party_no) throw Exception("qic::discord3_reg", "Invalid measured party index"); if (dim(nodal - 1) != 3) throw Exception("qic::discord3_reg", "Measured party is not qutrit"); #endif arma::uvec party = arma::zeros<arma::uvec>(party_no); for (arma::uword i = 0; i < party_no; ++i) party.at(i) = i + 1; arma::uvec rest = party; rest.shed_row(nodal - 1); auto rho_A = TrX(rho, rest, dim); auto rho_B = TrX(rho, {nodal}, dim); auto S_A = entropy(rho_A); auto S_B = entropy(rho_B); auto S_A_B = entropy(rho); auto I1 = S_A + S_B - S_A_B; dim1 /= 3; arma::uword dim2(1); for (arma::uword i = 0; i < nodal - 1; ++i) dim2 *= dim.at(i); arma::uword dim3(1); for (arma::uword i = nodal; i < party_no; ++i) dim3 *= dim.at(i); arma::Mat<trait::pT<T1> > eye2 = arma::eye<arma::Mat<trait::pT<T1> > >(dim1, dim1); arma::Mat<trait::pT<T1> > eye3 = arma::eye<arma::Mat<trait::pT<T1> > >(dim2, dim2); arma::Mat<trait::pT<T1> > eye4 = arma::eye<arma::Mat<trait::pT<T1> > >(dim3, dim3); typename arma::Col<trait::pT<T1> >::template fixed<3> disc; for (arma::uword i = 0; i < 3; ++i) { arma::Mat<std::complex<trait::pT<T1> > > proj1 = SPM<trait::pT<T1> >::get_instance().proj3.at(0, i + 1); arma::Mat<std::complex<trait::pT<T1> > > proj2 = SPM<trait::pT<T1> >::get_instance().proj3.at(1, i + 1); arma::Mat<std::complex<trait::pT<T1> > > proj3 = SPM<trait::pT<T1> >::get_instance().proj3.at(2, i + 1); if (nodal == 1) { proj1 = kron(proj1, eye2); proj2 = kron(proj2, eye2); proj3 = kron(proj3, eye2); } else if (party_no == nodal) { proj1 = kron(eye2, proj1); proj2 = kron(eye2, proj2); proj3 = kron(eye2, proj3); } else { proj1 = kron(kron(eye3, proj1), eye4); proj2 = kron(kron(eye3, proj2), eye4); proj3 = kron(kron(eye3, proj3), eye4); } arma::Mat<std::complex<trait::pT<T1> > > rho_1 = (proj1 * rho * proj1); arma::Mat<std::complex<trait::pT<T1> > > rho_2 = (proj2 * rho * proj2); arma::Mat<std::complex<trait::pT<T1> > > rho_3 = (proj3 * rho * proj3); trait::pT<T1> p1 = std::real(arma::trace(rho_1)); trait::pT<T1> p2 = std::real(arma::trace(rho_2)); trait::pT<T1> p3 = std::real(arma::trace(rho_3)); trait::pT<T1> S_max = 0.0; if (p1 > _precision::eps<trait::pT<T1> >::value) { rho_1 /= p1; S_max += p1 * entropy(rho_1); } if (p2 > _precision::eps<trait::pT<T1> >::value) { rho_2 /= p2; S_max += p2 * entropy(rho_2); } if (p3 > _precision::eps<trait::pT<T1> >::value) { rho_3 /= p3; S_max += p3 * entropy(rho_3); } disc.at(i) = I1 - (S_B - S_max); } return disc; }