Skip to content

Commit

Permalink
Revert to working noncon check
Browse files Browse the repository at this point in the history
  • Loading branch information
TimWeaving committed Nov 23, 2023
1 parent a005135 commit f1caa15
Show file tree
Hide file tree
Showing 2 changed files with 2 additions and 74 deletions.
39 changes: 1 addition & 38 deletions symmer/operators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1104,44 +1104,7 @@ def is_noncontextual(self) -> bool:
if self.n_terms < 4:
# all operators with 3 or less P are noncontextual
return True

to_reduce = np.vstack([np.hstack([self.Z_block, self.X_block]), np.eye(2 * self.n_qubits, dtype=bool)])
cref_matrix = _cref_binary(to_reduce)
Z2_symp = cref_matrix[self.n_terms:, np.all(~cref_matrix[:self.n_terms], axis=0)].T
Z2_terms = PauliwordOp(Z2_symp, np.ones(Z2_symp.shape[0]))

if Z2_terms.n_terms < 1:
remaining = self.cleanup()
if remaining.n_terms > 2 * remaining.n_qubits + 1:
# no symmetry component, therefore operator must be made up of pairwise
# anticommuting pauli operators to be noncontextual. This can have a max size of 2n+1, if larger cannot
# be noncontextual
return False
else:
# for remaining to be noncontextual must be disjoint union of cliques
# we can test for this below
adj_matrix_view = np.ascontiguousarray(remaining.adjacency_matrix).view(
np.dtype((np.void, remaining.adjacency_matrix.dtype.itemsize * remaining.adjacency_matrix.shape[1]))
)
re_order_indices = np.argsort(adj_matrix_view.ravel())
# sort the adj matrix and vector of coefficients accordingly
sorted_terms = remaining.adjacency_matrix[re_order_indices]
# unique terms are those with non-zero entries in the adjacent row difference array
diff_adjacent = np.diff(sorted_terms, axis=0)
mask_unique_terms = np.append(True, np.any(diff_adjacent, axis=1))
clique_mask = sorted_terms[mask_unique_terms]

# check for overlap (array of ones == no overlap)
return np.all(np.sum(clique_mask, axis=0) == 1)
else:
gens = self.generators
if check_adjmat_noncontextual(gens.adjacency_matrix):
return True
from symmer.utils import get_generators_including_xz_products
gens_xyz = get_generators_including_xz_products(self)
return check_adjmat_noncontextual(gens_xyz.adjacency_matrix)


return check_adjmat_noncontextual(self.adjacency_matrix)

def _rotate_by_single_Pword(self,
Pword: "PauliwordOp",
Expand Down
37 changes: 1 addition & 36 deletions symmer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,39 +416,4 @@ def Get_AC_root(power: float, operator: AntiCommutingOp) -> PauliwordOp:

AC_root = (rot.dagger * Ps_root * rot).multiply_by_constant(gamma_l ** power)

return AC_root


def get_generators_including_xz_products(operator: PauliwordOp) -> PauliwordOp:
"""
Given an input operator perform similar process to finding generators (but do not use X,Z terms to generate Y terms)
i.e. the set can be dependent.
Args:
operator:
Returns:
PauliwordOp of generators build of X,Y,Z terms (note can have dependent terms!)
"""
X_only = np.logical_and(operator.X_block, ~operator.Z_block)
Z_only = np.logical_and(~operator.X_block, operator.Z_block)
Y_only = np.logical_and(operator.Z_block, operator.X_block)

XZY_block = np.hstack((np.hstack((X_only, Z_only)), Y_only))

row_red_XYZ = _rref_binary(XZY_block)
non_zero_rows = row_red_XYZ[np.sum(row_red_XYZ, axis=1).astype(bool)]

X_new = non_zero_rows[:, :operator.X_block.shape[1]]
Z_new = non_zero_rows[:, operator.X_block.shape[1]:2 * operator.Z_block.shape[1]]
Y_new = non_zero_rows[:, 2 * operator.Z_block.shape[1]:]

sym_final = np.hstack((np.logical_or(X_new, Y_new),
np.logical_or(Z_new, Y_new)))

# remove duplicates due to same X_new and Z_new positions
xyz_gens = PauliwordOp(sym_final, np.ones(sym_final.shape[0])).cleanup()
xyz_gens.coeff_vec = np.ones_like(xyz_gens.coeff_vec)

return xyz_gens
return AC_root

0 comments on commit f1caa15

Please sign in to comment.