def rule_op(V, R, r, d, totalistic=False, hamiltonian=False): """ Operator for rule R, activation V, and neighborhood radius r, and dimension d. hamiltonian flag for simultaion type (hamiltonian=analog, unitary=digital) """ N = 2 * r * d OP = np.zeros((2 ** (N + 1), 2 ** (N + 1)), dtype=complex) if totalistic: R2 = mx.dec_to_bin(R, N + 1)[::-1] else: R2 = mx.dec_to_bin(R, 2 ** N)[::-1] for elnum, Rel in enumerate(R2): if totalistic: K = elnum * [1] + (N - elnum) * [0] hoods = list(set([perm for perm in permutations(K, N)])) hoods = map(list, hoods) else: hoods = [mx.dec_to_bin(elnum, N)] for hood in hoods: OP += rule_element(V, Rel, hood, hamiltonian=hamiltonian) if hamiltonian: assert mx.isherm(OP) else: # unitaty assert mx.isU(OP) return OP
def boundary_rule_ops(V, R, r, d, BC_conf, totalistic=False, hamiltonian=False): """ Special operators for boundaries (of which there are 2r). BC_conf is a string "b0b1...br...b2r" where each bj is either 0 or 1. Visiually, BC_conf represents the fixed boundaries from left to right: |b0>|b1>...|br> |psi>|b2r-r>|b2r-r+1>...|b2r>. """ BC_conf = np.array([int(s) for s in BC_conf]) OPs = [] N = 2 * r * d if totalistic: R2 = mx.dec_to_bin(R, N + 1)[::-1] else: R2 = mx.dec_to_bin(R, 2 ** N)[::-1] indsj = [np.arange(r), np.array([r]), np.arange(r+1, 2*r+1)] dj = 2*r + 1 if d == 2: indsk = indsj dk = 2*r + 1 elif d ==1: indsk = [[0]] dk =1 OPs = [] for J, js in enumerate(indsj): for K, ks in enumerate(indsk): OPs_region = [] for j in js: OPs_row = [] for k in ks: mask = make_mask(j, k, dj, dk, r, d, BC_type="1") clip = np.logical_not(mask) if np.sum(clip) > 0: dim = np.sum(mask)+1 OP = np.zeros((2 ** dim, 2 ** dim), dtype=complex) for elnum, Rel in enumerate(R2): if totalistic: tot = elnum * [1] + (N - elnum) * [0] hoods = list(set([perm for perm in permutations(tot, N)])) hoods = map(np.array, hoods) else: hoods = np.array([mx.dec_to_bin(elnum, N)]) for hood in hoods: if np.all(BC_conf[clip] == hood[clip]): OP += rule_element( V, Rel, hood[mask], hamiltonian=hamiltonian, ) if hamiltonian: assert mx.isherm(OP) else: # unitaty assert mx.isU(OP) OPs_row.append(OP) OPs_region.append(OPs_row) if len(OPs_region[0]) > 0: OPs.append(OPs_region) return OPs
def boundary_rule_ops(V, R, r, BC_conf, totalistic=False, hamiltonian=False): """ Special operators for boundaries (of which there are 2r). BC_conf is a string "b0b1...br...b2r" where each bj is either 0 or 1. Visiually, BC_conf represents the fixed boundaries from left to right: |b0>|b1>...|br> |psi>|b2r-r>|b2r-r+1>...|b2r>. """ # split BC configuration into left and reverse-right boundaries BC_conf = [BC_conf[:r], BC_conf[r::][::-1]] N = 2 * r OPs = [] for j, BC in enumerate(BC_conf): for e in range(r): dim = r + 1 + e if j == 0: # left boundary lslice = slice(r - e, r) rslice = slice(r, N) cslice = slice(0, r - e) elif j == 1: # right boundary lslice = slice(0, r) rslice = slice(r, r + e) cslice = slice(r + e, N) OP = np.zeros((2**dim, 2**dim), dtype=complex) if totalistic: R2 = mx.dec_to_bin(R, N + 1)[::-1] for elnum, Rel in enumerate(R2): K = elnum * [1] + (N - elnum) * [0] hoods = list(set([perm for perm in permutations(K, N)])) hoods = map(list, hoods) for hood in hoods: if BC[e:r] == hood[cslice]: OP += rule_element( V, Rel, hood, lslice=lslice, rslice=rslice, hamiltonian=hamiltonian, ) else: # non-totalistic R2 = mx.dec_to_bin(R, 2**N)[::-1] for elnum, Rel in enumerate(R2): hood = mx.dec_to_bin(elnum, N) if BC[e:r] == hood[cslice]: OP += rule_element( V, Rel, hood, lslice=lslice, rslice=rslice, hamiltonian=hamiltonian, ) if hamiltonian: assert mx.isherm(OP) else: # unitaty assert mx.isU(OP) OPs.append(OP) return OPs[:r], OPs[r:][::-1]
def rule_unitaries(V, R, r, BC, L, dt, totalistic=False, hamiltonian=False, trotter=True): """ Calculate qca unitiary activation V, rule R, radius r, bounary condition BC, size L, and time step dt. """ BC_type, *BC_conf = BC.split("-") BC_conf = "".join(BC_conf) if BC_type == "1": BC_conf = [int(bc) for bc in BC_conf] if L is None: L = 2 * r + 1 bulk = rule_op(V, R, r, totalistic=totalistic, hamiltonian=hamiltonian) lUs, rUs = boundary_rule_ops(V, R, r, BC_conf, totalistic=totalistic, hamiltonian=hamiltonian) if hamiltonian: if trotter: bulk = expm(-1j * bulk * dt) rUs = [expm(-1j * H * dt) for H in rUs] lUs = [expm(-1j * H * dt) for H in lUs] else: # not trotter: H = np.zeros((2**L, 2**L), dtype=complex) for j in range(r, L - r): ln = j - r rn = L - 2 * r - 1 - ln left = np.eye(2**ln) right = np.eye(2**rn) H += mx.listkron([left, bulk, right]) # boundaries for j, (lU, rU) in enumerate(zip(lUs, rUs[::-1])): end = np.eye(2**(L - r - 1 - j)) H += mx.listkron([end, rU]) H += mx.listkron([lU, end]) U = expm(-1j * H * dt) assert mx.isU(U) return U if BC_type == "0": return bulk else: # BC_type == "1" return lUs, bulk, rUs
def make_U(V, r, S): N = 2 * r Sb = mx.dec_to_bin(S, 2**N)[::-1] U = np.zeros((2**(N + 1), 2**(N + 1)), dtype=complex) for sig, s in enumerate(Sb): sigb = mx.dec_to_bin(sig, N) Vmat = get_V(V, s) ops = ([mx.ops[str(op)] for op in sigb[0:r]] + [Vmat] + [mx.ops[str(op)] for op in sigb[r:2 * r + 1]]) U += mx.listkron(ops) if not mx.isU(U): raise AssertionError return U