def J(): I = jnp.array([[1, 0], [0, 1]]) X = jnp.array([[0, 1], [1, 0]]) I_f = jnp.kron(I, I) X_f = jnp.kron(X, X) J = jnp.array((1 / jnp.sqrt(2)) * (I_f + 1j * X_f)) return J
def gate_expand_1toN(U, N, target): """ Create a JAX array representing a 1-qubit gate acting on a system with N qubits. Parameters ---------- U : 2 X 2 unitary matrix The one-qubit gate N : integer The number of qubits in the target space. target : integer The index of the target qubit. Returns ------- gate : (2 ** N) X (2 ** N) unitary matrix Quantum object representation of N-qubit gate. """ if N < 1: raise ValueError("integer N must be larger or equal to 1") if target >= N: raise ValueError("target must be integer < integer N") return jnp.kron(jnp.kron(jnp.eye(2**target), U), jnp.eye(2**(N - target - 1)))
def circuit(params): thetax, thetay, thetaz = params layer0 = jnp.kron(basis(2, 0), basis(2, 0)) layer1 = jnp.kron(ry(jnp.pi / 4), ry(jnp.pi / 4)) layer2 = jnp.kron(rx(thetax), jnp.eye(2)) layer3 = jnp.kron(ry(thetay), rz(thetaz)) layers = [layer1, cnot(), layer2, cnot(), layer3] unitary = reduce(lambda x, y: jnp.dot(x, y), layers) return jnp.dot(unitary, layer0)
def kernel_to_state_space(self, R=None): F_t, L_t, Qc_t, H_t, Pinf_t = self.temporal_kernel.kernel_to_state_space( ) Kzz = self.spatial_kernel(self.z.value, self.z.value) F = np.kron(np.eye(self.M), F_t) Qc = None L = None H = self.measurement_model() Pinf = np.kron(Kzz, Pinf_t) return F, L, Qc, H, Pinf
def ising_hamiltonian(n_qubits, g, h): """ Construct the hamiltonian matrix of Ising model. Args: n_qubits: int, Number of qubits g: float, Transverse magnetic field h: float, Longitudinal magnetic field """ ham_matrix = 0 # Nearest-neighbor interaction spin_coupling = jnp.kron(PauliBasis[3], PauliBasis[3]) for i in range(n_qubits - 1): ham_matrix -= jnp.kron(jnp.kron(jnp.eye(2**i), spin_coupling), jnp.eye(2**(n_qubits - 2 - i))) ham_matrix -= jnp.kron(jnp.kron(PauliBasis[3], jnp.eye(2**(n_qubits - 2))), PauliBasis[3]) # Periodic B.C # Transverse magnetic field for i in range(n_qubits): ham_matrix -= g * jnp.kron(jnp.kron(jnp.eye(2**i), PauliBasis[1]), jnp.eye(2**(n_qubits - 1 - i))) # Longitudinal magnetic field for i in range(n_qubits): ham_matrix -= h * jnp.kron(jnp.kron(jnp.eye(2**i), PauliBasis[3]), jnp.eye(2**(n_qubits - 1 - i))) return ham_matrix
def circuit(params): thetax, thetay, thetaz = params layer0 = jnp.array([[1], [0], [0], [0]]) layer1 = J() layer2 = jnp.kron(rx(thetax), rx(thetax)) layer3 = jnp.kron(ry(thetay), ry(thetay)) layer4 = jnp.kron(rz(thetaz), rz(thetaz)) layer5 = J_dag() layers = [layer5, layer4, layer3, layer2, layer1, layer0] q_state_out = reduce(lambda x, y: jnp.dot(x, y), layers) return q_state_out
def circuit(params): thetax, thetay, thetaz = params layer0 = jnp.kron(basis(2, 0), basis(2, 0)) layer1 = J() layer2 = jnp.kron(rx(thetax), rx(thetax)) layer3 = jnp.kron(ry(thetay), ry(thetay)) layer4 = jnp.kron(rz(thetaz), rz(thetaz)) layer5 = J_dag() layers = [layer5, layer4, layer3, layer2, layer1] unitary = reduce(lambda x, y: jnp.dot(x, y), layers) #unitary = np.dot(layer5,np.dot(layer4, np.dot(layer3, np.dot(layer2, layer1)))) return jnp.dot(unitary, layer0)
def wavelet_tensors(request): """Returns the Hamiltonian and MERA tensors for the D=2 wavelet MERA. From Evenbly & White, Phys. Rev. Lett. 116, 140403 (2016). """ D = 2 h = simple_mera.ham_ising() E = np.array([[1, 0], [0, 1]]) X = np.array([[0, 1], [1, 0]]) Y = np.array([[0, -1j], [1j, 0]]) Z = np.array([[1, 0], [0, -1]]) wmat_un = np.real((np.sqrt(3) + np.sqrt(2)) / 4 * np.kron(E, E) + (np.sqrt(3) - np.sqrt(2)) / 4 * np.kron(Z, Z) + 1.j * (1 + np.sqrt(2)) / 4 * np.kron(X, Y) + 1.j * (1 - np.sqrt(2)) / 4 * np.kron(Y, X)) umat = np.real((np.sqrt(3) + 2) / 4 * np.kron(E, E) + (np.sqrt(3) - 2) / 4 * np.kron(Z, Z) + 1.j / 4 * np.kron(X, Y) + 1.j / 4 * np.kron(Y, X)) w = np.reshape(wmat_un, (D, D, D, D))[:, 0, :, :] u = np.reshape(umat, (D, D, D, D)) w = np.transpose(w, [1, 2, 0]) u = np.transpose(u, [2, 3, 0, 1]) return tuple(x.astype(np.complex128) for x in (h, w, u))
def initialize_Cinv(self, params): df = self.df Cinvs = [ smoothness_kernel([ params[i], ], df[i])[1] for i in range(len(df)) ] if len(Cinvs) == 1: self.Cinv = Cinvs[0] elif len(Cinvs) == 2: self.Cinv = jnp.kron(*Cinvs) else: self.Cinv = jnp.kron(Cinvs[0], jnp.kron(Cinvs[1], Cinvs[2]))
def __init__(self, tau_s=[3, 5, 100], NMDAratio=0.4, *args): """ tau_s = [tau_AMPA, tau_GABA, tau_NMDA] or [tau_AMPA, tau_GABA] decay time-consants for synaptic currents of different receptor types. NMDAratio: scalar ratio of E synaptic weights that are NMDA-type (model assumes this fraction is constant in all weights) Good values: tau_AMPA = 4, tau_GABA= 5 #in ms NMDAratio = 0.3-0.4 """ if len(args) != 0: super(_SSN_AMPAGABA, self).__init__(*args) #super(_SSN_Base, _SSN_Base).__init__(*args) self.tau_s = np.squeeze(np.asarray(tau_s)) self.tau_AMPA = tau_s[0] self.tau_GABA = tau_s[1] assert self.tau_s.size <= 3 and self.tau_s.ndim == 1 if self.tau_s.size == 3 and NMDAratio > 0: self.tau_NMDA = tau_s[2] self.NMDAratio = NMDAratio else: self.tau_s = self.tau_s[:2] self.NMDAratio = 0 self.num_rcpt = self.tau_s.size self.tau_s_vec = np.kron(self.tau_s, np.ones(self.N)) W_AMPA = (1 - self.NMDAratio) * np.hstack( (self.W[:, :self.Ne], np.zeros((self.N, self.Ni)))) W_GABA = np.hstack((np.zeros((self.N, self.Ne)), self.W[:, self.Ne:])) Wrcpt = [W_AMPA, W_GABA] if self.NMDAratio > 0: W_NMDA = self.NMDAratio / (1 - self.NMDAratio) * W_AMPA Wrcpt.append(W_NMDA) self.Wrcpt = np.vstack(Wrcpt) # shape = (self.num_rcpt*self.N, self.N)
def make_oracle(n, f): """ Form Quantum Oracle Matrix. Creates the unitary matrix representing an oracle from arbitrary function f : {0, 1} ^ n -> {0, 1} ^ n The matrix Uf, acts on (i.e. takes as input) two n-qbit registers. Its action on the first and second registers, when fully concentrated in the two computational basis states ∣x⟩∣y⟩ is given by Uf∣x⟩∣y⟩ = ∣x⟩∣y ⊕ f(x)⟩ That is, the first register remain unchanged and the second register becomes the bitwise xor of its previous CBS (i.e. y) with the function, f, applied to the CBS of the first register, (i.e. x). The gate extends linearly to register states that are not fully concentrated in any CBS. As described by A Michael Loceff, Course in Quantum Computing (page 358) http://lapastillaroja.net/wp-content/uploads/2016/09/Intro_to_QC_Vol_1_Loceff.pdf We form this by letting the columns of the matrix Uf be the outputs for each computational basis state for the two registers. :param f: Callable function mapping {0, 1} ^ n -> {0, 1} ^ n :param n: Number of Qbits. :return: Unitary matrix that acts on two n-Qbit registers. """ return np.array( [[np.kron(cbs(n, x), cbs(n, y | f(x))) for x in range(2**n)] for y in range(2**n)], dtype=dtype).T
def V1_cost(self, W): # Weight factor, determining the weigth of the penalty weight = self.scheme[1] # Calculating the quadrupole matrix using the occupied orbitals. q_loc = jnp.einsum('ji,abjk,ki->iab', W, self.quadrupole_matrix_elem, W, optimize=True) # Calculating the trace of the quadrupole matrices. q_trace = jnp.einsum('iaa->i', q_loc, optimize=True) / 3. # Making the quadrupole traceless, we do this by constructing an # array of identity matrices and filling them with their corresponding # traces. q_loc_traceless = q_loc - jnp.kron(q_trace, jnp.eye( (3))).transpose().reshape(self.N_occ, 3, 3) # Calculating the matrix norm of the quadrupole matrix of # every occupied orbital. penalty = jnp.sum(q_loc_traceless**2) return self.FB_cost(W) - weight * penalty
def make_eE_noiseCov(ssn, noise_pars, LFPrange): # setting up e_E and e_I: the projection/measurement vectors for # representing the "LFP" measurement (e_E good for LFP interpretation, but e_I ?) # eE = np.zeros(ssn.N) # # eE[LFPrange] =1/len(LFPrange) # index_update(eE, LFPrange, 1/len(LFPrange)) # eI = np.zeros(ssn.N) # eI[ssn.Ne + LFPrange] =1/len(LFPrange) eE = np.hstack((np.array([i in LFPrange for i in range(ssn.Ne)], dtype=np.float32), np.zeros(ssn.Ni))) # the script assumes independent noise to E and I, and spatially uniform magnitude of noise noiseCov = np.hstack((noise_pars.stdevE**2 * np.ones(ssn.Ne), noise_pars.stdevI**2 * np.ones(ssn.Ni))) OriVec = ssn.topos_vec if noise_pars.corr_length > 0 and OriVec.size > 1: #assumes one E and one I at every topos dOri = np.abs(OriVec) L = OriVec.size * np.diff(OriVec[:2]) dOri[dOri > L / 2] = L - dOri[dOri > L / 2] # distance on circle/periodic B.C. SpatialFilt = toeplitz( np.exp(-(dOri**2) / (2 * noise_pars.corr_length**2)) / np.sqrt(2 * pi) / noise_pars.corr_length * L / ssn.Ne) sigTau1Sprd1 = 0.394 # roughly the std of spatially and temporally filtered noise when the white seed is randn(ssn.Nthetas,Nt)/sqrt(dt) and corr_time=corr_length = 1 (ms or angle, respectively) SpatialFilt = SpatialFilt * np.sqrt( noise_pars.corr_length / 2) / sigTau1Sprd1 # for the sake of output SpatialFilt = np.kron(np.eye(2), SpatialFilt) # 2 for E/I else: SpatialFilt = np.array(1) return eE, noiseCov, SpatialFilt # , eI
def concatenate_and_update(mu1, mu2, a, y, elo_functions, elo_params): """Combines mu1 and mu2 into a concatenated vector mu and uses this to calculate updated means mu1' and mu2'. Args: mu1: The winner's mean prior to the match. mu2: The loser's mean prior to the match. a: The vector such that a^T [mu1, mu2] = mu_delta. y: The observed outcomes. elo_functions: The functions required to compute the update elo_params: The parameters required for the update Returns: A Tuple with three elements: the first two contain the new means, the last the log likelihood of the result. """ mu = jnp.concatenate([mu1, mu2]) cov_full = jnp.kron(jnp.eye(2), elo_params.theta['cov_mat']) new_mu, lik = calculate_update(mu, cov_full, a, y, elo_functions, elo_params) new_mu1, new_mu2 = jnp.split(new_mu, 2) return new_mu1, new_mu2, lik
def kernel_to_state_space(self, R=None): F_mat = np.array([[-1.0 / self.lengthscale]]) L_mat = np.array([[1.0]]) Qc_mat = np.array([[2.0 * self.variance / self.lengthscale]]) H_mat = np.array([[1.0]]) Pinf_mat = np.array([[self.variance]]) F_cos = np.array([[0.0, -self.radial_frequency], [self.radial_frequency, 0.0]]) H_cos = np.array([[1.0, 0.0]]) # F = (-1/l -ω # ω -1/l) F = np.kron(F_mat, np.eye(2)) + F_cos L = np.kron(L_mat, np.eye(2)) Qc = np.kron(np.eye(2), Qc_mat) H = np.kron(H_mat, H_cos) Pinf = np.kron(Pinf_mat, np.eye(2)) return F, L, Qc, H, Pinf
def stationary_covariance(self): """ Compute the covariance of the stationary state distribution. Since the latent components are independent under the prior, this is a block-diagonal matrix """ Pinf_time = self.temporal_kernel.stationary_covariance() Pinf = np.kron(np.eye(self.M), Pinf_time) return Pinf
def measurement_model(self): """ Compute the spatial conditional, i.e. the measurement model projecting the state x(t) to function space f(t, R) = H x(t) """ H_time = self.temporal_kernel.measurement_model() H = np.kron(np.eye(self.M), H_time) return H
def kinetic(k, d_qtm_nk, nd_qtm): vly, = d_qtm_nk G = jnp.array(nd_qtm) K1, K2 = np.array([[1. / 3., 2. / 3.], [2. / 3., 1. / 3.]]) K1, K2 = jnp.where(vly == 1, (K2, K1), (K1, K2)) k1 = (k + G - K1) @ bvec k2 = (k + G - K2) @ bvec res = jnp.kron((sigma[0] + sigma[3]) / 2, -vF * k1[0] * vly * sigma[1] - vF * k1[1] * sigma[2]) + jnp.kron((sigma[0] - sigma[3]) / 2, -vF * k2[0] * vly * sigma[1] - vF * k2[1] * sigma[2]) return res
def state_transition(self, dt): """ Calculation of the discrete-time state transition matrix A = expm(FΔt) for the spatio-temporal prior. :param dt: step size(s), Δtₙ = tₙ - tₙ₋₁ [scalar] :return: state transition matrix A """ A_time = self.temporal_kernel.state_transition(dt) A = np.kron(np.eye(self.M), A_time) return A
def combine_matrices(a, b, Pia, Pib, check=True): # this combines INDEPENDENT transition matrices Pia and Pib grid = mat_combine(a, b) Pi = np.kron(Pia, Pib) if check: assert (all(abs(np.sum(Pi, axis=1) - 1) < 1e-5)) return grid, Pi
def SYK_hamiltonian(rng, n_qubits): """ Construct the hamiltonian matrix of Ising model. Args: n_qubits: int, Number of qubits g: float, Transverse magnetic field h: float, Longitudinal magnetic field """ # Construct the gamma matrices for SO(2 * n_qubits) Clifford algebra gamma_matrices, n_gamma = [], 2 * n_qubits for k in range(n_gamma): temp = jnp.eye(1) for j in range(k // 2): temp = jnp.kron(temp, PauliBasis[3]) if k % 2 == 0: temp = jnp.kron(temp, PauliBasis[1]) else: temp = jnp.kron(temp, PauliBasis[2]) for i in range(int(n_gamma / 2) - (k // 2) - 1): temp = jnp.kron(temp, PauliBasis[0]) gamma_matrices.append(temp) # Number of SYK4 interaction terms n_terms = int(factorial(n_gamma) / factorial(4) / factorial(n_gamma - 4)) # SYK4 random coupling couplings = jax.random.normal( key=rng, shape=(n_terms, ), dtype=jnp.float64) * jnp.sqrt(6 / (n_gamma**3)) ham_matrix = 0 for idx, (x, y, w, z) in enumerate(combinations(range(n_gamma), 4)): ham_matrix += (couplings[idx] / 4) * jnp.linalg.multi_dot([ gamma_matrices[x], gamma_matrices[y], gamma_matrices[w], gamma_matrices[z] ]) return ham_matrix
def __call__(self, inputs: Array) -> Array: """ Applies a masked linear transformation to the inputs. Args: inputs: input data with dimensions (batch, length, features). Returns: The transformed data. """ if inputs.ndim == 2: is_single_input = True inputs = jnp.expand_dims(inputs, axis=0) else: is_single_input = False batch, size, in_features = inputs.shape inputs = inputs.reshape((batch, size * in_features)) if self.use_bias: bias = self.param( "bias", self.bias_init, (size, self.features), self.param_dtype ) else: bias = None mask = jnp.ones((size, size), dtype=self.param_dtype) mask = jnp.triu(mask, self.exclusive) mask = jnp.kron( mask, jnp.ones((in_features, self.features), dtype=self.param_dtype) ) kernel = self.param( "kernel", wrap_kernel_init(self.kernel_init, mask), (size * in_features, size * self.features), self.param_dtype, ) inputs, mask, kernel, bias = promote_dtype( inputs, mask, kernel, bias, dtype=None ) y = lax.dot(inputs, mask * kernel, precision=self.precision) y = y.reshape((batch, size, self.features)) if is_single_input: y = y.squeeze(axis=0) if self.use_bias: y = y + bias return y
def circuit(params): """Returns the state evolved by the parametrized circuit Args: params (list): rotation angles for x, y, and z rotations respectively. Returns: :obj:`jnp.array`: state evolved by a parametrized circuit """ thetax, thetay, thetaz = params layer0 = jnp.kron(basis(2, 0), basis(2, 0)) layer1 = jnp.kron(ry(jnp.pi / 4), ry(jnp.pi / 4)) layer2 = jnp.kron(rx(thetax), jnp.eye(2)) layer3 = jnp.kron(ry(thetay), rz(thetaz)) layers = [layer1, cnot(), layer2, cnot(), layer3] unitary = reduce(lambda x, y: jnp.dot(x, y), layers) return jnp.dot(unitary, layer0)
def hm(self, k, d_qtm_nk): data = tuple( map(lambda nd_qtm: self.kinetic(k, d_qtm_nk, nd_qtm), list(self.nd_basis.keys()))) diag = jnp.kron( jnp.eye(self.num_nd_basis, dtype=jnp.complex64), jnp.ones((self.num_sub, self.num_sub), dtype=jnp.complex64)) T = jnp.repeat( jnp.hstack(data).reshape( (1, -1)), self.num_nd_basis, axis=0).reshape( (self.num_nd_bands, self.num_nd_bands)) * diag return jnp.asarray(T + self.V[d_qtm_nk])
def build_H_one_body(sites, L, H=None, sx=True, sy=True, sz=True): Sx = np.array([[0., 1.], [1., 0.]]) Sy = np.array([[0., -1j], [1j, 0.]]) Sz = np.array([[1., 0.], [0., -1.]]) # S = [Sx, Sy, Sz] if H is None: H = np.zeros((2**L, 2**L), dtype=np.complex64) else: pass for i, V in sites: print("building", i) if sx: hx = np.kron(np.eye(2**(i - 1)), Sx) hx = np.kron(hx, np.eye(2**(L - i))) H = H + V * hx if sy: hy = np.kron(np.eye(2**(i - 1)), Sy) hy = np.kron(hy, np.eye(2**(L - i))) H = H + V * hy if sz: hz = np.kron(np.eye(2**(i - 1)), Sz) hz = np.kron(hz, np.eye(2**(L - i))) H = H + V * hz return H
def gate_expand_2toN(U, N, control=None, target=None): """ Create a Qobj representing a two-qubit gate that act on a system with N qubits. Parameters ---------- U : 4 X 4 unitary matrix The two-qubit gate N : integer The number of qubits in the target space. control : integer The index of the control qubit. target : integer The index of the target qubit. Returns ------- gate : (2 ** N) X (2 ** N) unitary matrix Quantum object representation of N-qubit gate. """ if control is None or target is None: raise ValueError("Specify value of control and target") if N < 2: raise ValueError("integer N must be larger or equal to 2") if control >= N or target >= N: raise ValueError("control and not target must be integer < integer N") if control == target: raise ValueError("target and not control cannot be equal") p = list(range(N)) if target == 0 and control == 1: p[control], p[target] = p[target], p[control] elif target == 0: p[1], p[target] = p[target], p[1] p[1], p[control] = p[control], p[1] else: p[1], p[target] = p[target], p[1] p[0], p[control] = p[control], p[0] matrix = jnp.kron(U, jnp.eye(2**(N - 2))) matrix = matrix.reshape(*([2 for _ in range(2 * N)])) matrix = matrix.transpose(p + [i + N for i in p]) matrix = matrix.reshape(2**N, 2**N) return matrix
def gate(self, params): # start with identity scalar and tensor-product all the gates mat = 1 for g in self.gates: g = g.gate(params) mat = jnp.kron(mat, g) # if we have interleaved gates (i.e. a gate acting on qudit #1 and #3 but not #2) # then we need to un-permute the indices if self.permuted != self.regInfo.unpermuted: # reshape 2D unitary into qudit indices mat = mat.reshape(self.regInfo.shape) mat = jnp.moveaxis(mat, self.permuted, self.regInfo.unpermuted) mat = mat.reshape((self.regInfo.dim, self.regInfo.dim)) return mat
def hadamard(n): """Hadamard operator for m qbits. :param n: Number of qbits :return: Matrix operator of size 2^m """ if n < 0: raise ValueError(f'Invalid number of dimensions: {n}') if n == 0: return np.array(1, dtype=dtype) if n == 1: return np.array([[1, 1], [1, -1]], dtype=dtype) / np.sqrt(2) else: return np.kron(hadamard(1), hadamard(n - 1))
def update_C_prior(self, params): """ Using kronecker product to construct high-dimensional prior covariance. Given RF dims = [t, y, x], the prior covariance: C = kron(Ct, kron(Cy, Cx)) Cinv = kron(Ctinv, kron(Cyinv, Cxinv)) """ n_hp_time = self.n_hp_time n_hp_space = self.n_hp_space rho = params[1] params_time = params[2: 2 + n_hp_time] # Covariance Matrix in Time C_t, C_t_inv = self.cov1d_time(params_time, self.dims[0]) if len(self.dims) == 1: C, C_inv = rho * C_t, (1 / rho) * C_t_inv elif len(self.dims) == 2: # Covariance Matrix in Space params_space = params[2 + n_hp_time: 2 + n_hp_time + n_hp_space] C_s, C_s_inv = self.cov1d_space(params_space, self.dims[1]) # Build 2D Covariance Matrix C = rho * jnp.kron(C_t, C_s) C_inv = (1 / rho) * jnp.kron(C_t_inv, C_s_inv) elif len(self.dims) == 3: # Covariance Matrix in Space params_spacey = params[2 + n_hp_time: 2 + n_hp_time + n_hp_space] params_spacex = params[2 + n_hp_time + n_hp_space:] C_sy, C_sy_inv = self.cov1d_space(params_spacey, self.dims[1]) C_sx, C_sx_inv = self.cov1d_space(params_spacex, self.dims[2]) C_s = jnp.kron(C_sy, C_sx) C_s_inv = jnp.kron(C_sy_inv, C_sx_inv) # Build 3D Covariance Matrix C = rho * jnp.kron(C_t, C_s) C_inv = (1 / rho) * jnp.kron(C_t_inv, C_s_inv) else: raise NotImplementedError(len(self.dims)) return C, C_inv
def simon(n, f): """Simon's Algorithm. Given function f :: {0, 1} ^ n -> {0, 1} ^ n that is periodic on a, i.e. f(x) = f(x ⊕ a) find a. :param n: Number of Qbits. :param f: Function which takes as input a number in [0, 2^n) and outputs. :return: The period of f. """ x = hadamard(n) @ zero(n) # Data register - top line. y = zero(n) uf = make_oracle(n, f) r = uf @ np.kron(x, y) s = np.kron(hadamard(n), eye(n)) @ r samples = [observe(s) for _ in range(n)]