def test_poorly_conditioned(self): """ Testing against a single poorly-conditioned matrix. Generated as a random 6x6 array, and then divided the first row and column by 1e6 """ mat = np.array([[ 6.6683264e-07, 6.3376014e-07, 4.5754601e-07, 7.1252750e-07, 7.5942456e-07, 5.9028134e-07 ], [ 5.7484930e-07, 6.5606222e-01, 6.5073522e-01, 2.4251825e-01, 1.0735555e-01, 2.6956707e-01 ], [ 6.1098206e-07, 4.0445840e-01, 5.6152644e-01, 5.8278476e-01, 8.0418942e-01, 7.7306821e-01 ], [ 8.5656473e-07, 4.3833014e-01, 5.7838875e-01, 2.4317287e-01, 1.6641302e-01, 3.9590950e-01 ], [ 4.4858020e-07, 1.1731434e-01, 7.3598305e-01, 4.5670520e-01, 5.8185932e-01, 9.4438709e-01 ], [ 4.1073805e-07, 9.6286350e-02, 7.7365790e-01, 1.3120009e-01, 9.3908360e-01, 1.4665616e-01 ]]) phi = expm(mat) out_scipy = expm_scipy(mat) assert_arrays_equal(out_scipy, phi, decimal=8)
def passwordAttack(e, n): N = buildPassTable(e, n) for weakPass in D: ePass = expm(weakPass, e, n) if ePass in N.keys(): print("Match! Name: {}, Password: {}".format( N[ePass][0], weakPass))
def test_expm_one_large_element(self): """ one value slightly swamping the rest """ a = np.array( [[0.1, 0.02, 0.003], [1.875, 0.12, 0.11], [0.1234567, 0, 0.3]], dtype=np.float64) phi = expm(a) out_scipy = expm_scipy(a) assert_arrays_equal(out_scipy, phi, decimal=12)
def test_expm_one_huge_element(self): """ one value swamping the rest """ a = np.array( [[999.875, 0.2, 0.3], [0.1, 0.002, 0.001], [0.01, 0, -0.003]], dtype=np.float64) phi = expm(a) out_scipy = expm_scipy(a) assert_arrays_equal(out_scipy, phi, decimal=12)
def test_expm_nearly_identity(self): """ nearly the identity matrix """ a = np.array( [[1.0012, 0, 0.0003], [0.0075, 0.9876543, 0.0011], [0, 0, 0.9873]], dtype=np.float64) phi = expm(a) out_scipy = expm_scipy(a) assert_arrays_equal(out_scipy, phi, decimal=12)
def initRho(self, trotter_order=2): tau, device, dtype, model = self.params[ -1], self.device, self.dtype, self.model #print("Generate initial rho({}) via Trotter decomp.\n".format(tau)) if trotter_order == 1: # get Hamiltonian H = self.getHamilton() # local trotter gate rho = expm(-tau * H).view(2, 2, 2, 2) rho = torch.einsum('ijkl->ikjl', rho).contiguous().view(4, 4) # svd & truncate the 0 values U, S, V = torch.svd(rho) # trotter gate in form of two tensor contraction hl = (U @ torch.diag(torch.sqrt(S))).view(2, 2, 4) hr = (V @ torch.diag(torch.sqrt(S))).view(2, 2, 4) # local tensor of initial mpo, index order: [l,r,d,u] Ta = torch.tensordot(hr, hl, ([0], [1])).permute(1, 3, 2, 0).contiguous() Tb = torch.tensordot(hr, hl, ([1], [0])).permute(1, 3, 0, 2).contiguous() elif trotter_order == 2: # get Hamiltonian H = self.getHamilton() # local trotter gate rho = expm(-tau * H).view(2, 2, 2, 2) rho = torch.einsum('ijkl->ikjl', rho).contiguous().view(4, 4) # half local trotter gate rho_half = expm(-tau * H / 2).view(2, 2, 2, 2) rho_half = torch.einsum('ijkl->ikjl', rho_half).contiguous().view(4, 4) # svd U, S, V = torch.svd(rho) U2, S2, V2 = torch.svd(rho_half) # trotter gate in form of two tensor contraction hl = (U @ torch.diag(torch.sqrt(S))).view(2, 2, 4) hr = (V @ torch.diag(torch.sqrt(S))).view(2, 2, 4) hl2 = (U2 @ torch.diag(torch.sqrt(S2))).view(2, 2, 4) hr2 = (V2 @ torch.diag(torch.sqrt(S2))).view(2, 2, 4) # local tensor of initial mpo, index order: [l,r,d,u] Ta = torch.einsum('ijk,jlm,lno->okmin', hr2, hl, hr2).contiguous().view(16, 4, 2, 2) Tb = torch.einsum('ijk,jlm,lno->mokin', hl2, hr, hl2).contiguous().view(4, 16, 2, 2) else: raise Exception('only 1st and 2nd trotter are available!') return Ta, Tb
def test_expm_random_non_zero_floats(self): """ arbitrarily large values, and some negatives """ a = np.array([[99.23452, 2.0000234523, 0.0003, 378.2362], [1.00001, 8754.236, 1.1007, 33.333333], [111, 0.00034, 3922.323, -999.333], [-1234.5678, -0.00034, 333.65, 13]], dtype=np.float64) phi = expm(a) out_scipy = expm_scipy(a) assert_arrays_equal(out_scipy, phi, decimal=12)
def compute(self,A,h=1): if not type(A) is np.ndarray: raise TypeError('Pade approximation only supports type(A) == np.ndarray') if self.CppFlag: import expmat as e self.expA = np.zeros_like(A) e.expmc(h*A,self.expA) else: from scipy.linalg import expm self.expA = expm(A) return self.expA
def test_expm_random_sparse(self): """ Testing against a large set of random 2D matricies with defined sparsity """ np.random.seed(7) for size in range(5, 40): for d in [x / 100 for x in range(10, 90)]: mat = sparse.random(size, size, density=d).toarray() phi = expm(mat) out_scipy = expm_scipy(mat) assert_arrays_equal(out_scipy, phi, decimal=7)
def compute(self,A,h=1): if not type(A) is np.ndarray: raise TypeError('Pade approximation only supports type(A) == np.ndarray') try: from expm import expm except ImportError: # use scipy implementation as fallback print "ImportError generated when trying to import function 'expm' from module 'expm'!" print "using scipy.linalg.expm instead" from scipy.linalg import expm self.expA = expm(h*A) return self.expA
def default_expectation_value(ham, t, state, log_file="qmla_log.log", log_identifier="Expecation Value"): """ Default probability calculation: | <state.transpose | e^{-iHt} | state> |**2 Returns the expectation value computed by evolving the input state with the provided Hamiltonian operator. :param np.array ham: Hamiltonian needed for the time-evolution :param float t: Evolution time :param np.array state: Initial state to evolve and measure on :param str log_file: (optional) path of the log file :param str log_identifier: (optional) identifier for the log :return: probability of measuring the input state after Hamiltonian evolution """ probe_bra = state.conj().T u = expm(-1j * ham * t) # h = hexp.LinalgUnitaryEvolvingMatrix( # ham, # evolution_time = t, # ) # u = h.expm() # h = hexp.UnitaryEvolvingMatrix( # ham, # evolution_time = t, # ) # u = h.expm() u_psi = np.dot(u, state) expectation_value = np.dot(probe_bra, u_psi) # in general a complex number prob_of_measuring_input_state = np.abs(expectation_value)**2 # check that probability is reasonable (0 <= P <= 1) ex_val_tol = 1e-9 if prob_of_measuring_input_state > ( 1 + ex_val_tol) or prob_of_measuring_input_state < (0 - ex_val_tol): log_print( [ "prob_of_measuring_input_state > 1 or < 0 (={}) at t={}\n Probe={}" .format(prob_of_measuring_input_state, t, repr(state)) ], log_file=log_file, log_identifier=log_identifier, ) raise NameError("Unphysical expectation value") return prob_of_measuring_input_state
def apply(self,v,A=None,h=1): T = v.dtype nrm = np.linalg.norm(v) v = v/nrm n = np.max(v.shape) from expint.util import arnoldi from scipy.linalg import expm V = np.zeros((n,self.k), dtype=T) H = np.zeros((self.k+1,self.k), dtype=T) V,H = arnoldi(A,v,self.k) result = nrm*np.dot(V,expm(h*H[:self.k,:self.k])[:,0]) result.shape = (max(result.shape),1) return result
def apply(self,v,A=None,h=1): nrm = np.linalg.norm(v) v = v/nrm if self.CppFlag: from expint.lib.carnoldi import arnoldi V,H = arnoldi(A,v,self.k) else: from expint.util import arnoldi V,H = arnoldi(A,v,self.k) from scipy.linalg import expm result = nrm*np.dot(V,expm(h*H[:self.k,:self.k])[:,0]) result.shape = (max(result.shape),1) return result
def buildPassTable(e, n): infile = open('passwords.txt') records = infile.readlines() infile.close() N = {} for record in records: record = record[1:-2] #remove ( and ) (name, salt, spwd) = record.split(',') salt, spwd = eval(salt), eval(spwd) eSalt = expm(salt, e, n) invSalt = inv(eSalt, n) key = (spwd * invSalt) % n N[key] = (name, salt) return N
def make_inversion_gate(num_qubits): r""" returns the inversion gate for the Hahn eco evolution as numpy array :parameter num_qubits: size of the inversion gate required :output : inversion gate """ from scipy import linalg sigma_z = np.array([[1 + 0.j, 0 + 0.j], [0 + 0.j, -1 + 0.j]]) hahn_angle = np.pi / 2 hahn_gate = np.kron(hahn_angle * sigma_z, np.eye(2**(num_qubits - 1))) # inversion_gate = qutip.Qobj(-1.0j * hahn_gate).expm().full() inversion_gate = expm(-1j * hahn_gate) return inversion_gate
def random_initialised_qubit_for_hahn_sequence( hahn_rotation = None ): pauli_y = np.array([ [0,-1j], [1j, 0] ]) spin_qubit = np.array([1,0]) # prepared in |0> if hahn_rotation is None: hahn_rotation = np.random.uniform(-1, 1) hahn_angle = hahn_rotation*(np.pi/2) hahn_gate = expm( -1j * hahn_angle * pauli_y ) spin_qubit = np.dot(hahn_gate, spin_qubit) return spin_qubit
def n_qubit_hahn_evolution( ham, t, state, second_time_evolution_factor=1, pi_rotation="y", precision=1e-10, log_file=None, log_identifier=None, ): r""" n qubits time evolution for Hahn-echo measurement returning measurement probability for input state :param ham: Hamiltonian needed for the time-evolution :type ham: np.array() :param t: Evolution time :type t: float() :param state: Initial state to evolve and measure on :type state: float() :param precision: (optional) chosen precision for expectation value, default 1e-10 :type precision: float() :param log_file: (optional) path of the log file for logging errors :type log_file: str() :param log_identifier: (optional) identifier for the log :type log_identifier: str() :output: probability of measuring input state after time t """ import numpy as np import qmla.model_building_utilities from scipy import linalg num_qubits = int(np.log2(np.shape(ham)[0])) if pi_rotation == "y": from qmla.shared_functionality.hahn_y_gates import ( precomputed_hahn_y_inversion_gates, ) inversion_gate = precomputed_hahn_y_inversion_gates[num_qubits] elif pi_rotation == "z": from qmla.shared_functionality.hahn_inversion_gates import ( precomputed_hahn_z_inversion_gates, ) inversion_gate = precomputed_hahn_z_inversion_gates[num_qubits] # inversion_gate = make_inversion_gate_rotate_y(num_qubits) # want to evolve for t, then apply Hahn inversion gate, # then again evolution for (S * t) # where S = 2 in standard Hahn evolution, # S = 1 for long time dynamics study first_unitary_time_evolution = expm(-1j * ham * t) second_unitary_time_evolution = np.linalg.matrix_power( first_unitary_time_evolution, second_time_evolution_factor) total_evolution = np.dot( second_unitary_time_evolution, np.dot(inversion_gate, first_unitary_time_evolution), ) ev_state = np.dot(total_evolution, state) nm = np.linalg.norm(ev_state) if np.abs(1 - nm) > 1e-5: print( "\n\n[n qubit Hahn]\n norm ev state:", nm, "\nt=", t, "\nprobe=", repr(state), ) print("\nev state:\n", repr(ev_state)) print("\nham:\n", repr(ham)) print("\nHam element[0,2]:\n", ham[0][2]) print("\ntotal evolution:\n", repr(total_evolution)) print("\nfirst unitary:\n", first_unitary_time_evolution) print("\nsecond unitary:\n", second_unitary_time_evolution) print("\ninversion_gate:\n", inversion_gate) density_matrix = np.kron(ev_state, (ev_state.T).conj()) dim_hilbert_space = 2**num_qubits density_matrix = np.reshape(density_matrix, [dim_hilbert_space, dim_hilbert_space]) # qdm = qutip.Qobj(density_matrix, dims=[[2],[2]]) # reduced_matrix_qutip = qdm.ptrace(0).full() # below methods give different results for reduced_matrix # reduced_matrix = qdm.ptrace(0).full() to_trace = list(range(num_qubits - 1)) reduced_matrix = partial_trace(density_matrix, to_trace) density_mtx_initial_state = np.kron(state, state.conj()) density_mtx_initial_state = np.reshape( density_mtx_initial_state, [dim_hilbert_space, dim_hilbert_space]) reduced_density_mtx_initial_state = partial_trace( density_mtx_initial_state, to_trace) projection_onto_initial_den_mtx = np.dot(reduced_density_mtx_initial_state, reduced_matrix) expect_value = np.abs(np.trace(projection_onto_initial_den_mtx)) # expect_value is projection onto |+> # for this case Pr(0) refers to projection onto |-> # so return 1 - expect_value likelihood = 1 - expect_value if likelihood > 1 or likelihood < 0: print("Unphysical expectation value:", likelihood) return likelihood
from expm import expm from scipy.linalg import expm as lexpm num_iterations = 100 t_expm = 0 t_lexpm = 0 for i in range(num_iterations): model_params = { 'FH-hopping-sum_up_1h2_1h3_2h4_3h4_d4': np.random.uniform(0, 1), 'FH-onsite-sum_1_2_3_4_d4': np.random.uniform(0, 1), 'FH-hopping-sum_down_1h2_1h3_2h4_3h4_d4': np.random.uniform(0, 1) } model = sum([ model_params[term] * qmla.model_building_utilities.compute(term) for term in model_params ]) t = np.random.uniform(0, 100) start_expm = time.time() u = expm(-1j * model * t) t_expm += time.time() - start_expm start_lexpm = time.time() u = lexpm(-1j * model * t) t_lexpm += time.time() - start_expm print("Total times taken:\n \texpm={} \n\tlexpm={} \n\tSpeedup={}".format( np.round(t_expm, 2), np.round(t_lexpm, 2), np.round(t_lexpm / t_expm, 2)))
def forward(ctx, A): B = expm(A) ctx.save_for_backward(A, B) return B
def hahn_evolution(ham, t, state, precision=1e-10, log_file=None, log_identifier=None): r""" Hahn echo evolution and expectation value. Returns the expectation value computed by evolving the input state with the Hamiltonian corresponding to the Hahn eco evolution. NB: In this case, the assumption is that the value measured is 1 and the expectation value corresponds to the probability of obtaining 1. :param np.array ham: Hamiltonian needed for the time-evolution :param float t: Evolution time :param np.array state: Initial state to evolve and measure on :param float precision: precision required of the expectation value when using custom exponentiation function. TODO deprecated; remove :param str log_file: (optional) path of the log file :param str log_identifier: (optional) identifier for the log :return: expectation value of the evolved state """ # NOTE this is always projecting onto |+> # okay for experimental data with spins in NV centre import numpy as np from scipy import linalg from qmla.shared_functionality.probe_set_generation import random_probe inversion_gate = np.array([ [0.0 - 1.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [0.0 + 0.0j, 0.0 - 1.0j, 0.0 + 0.0j, 0.0 + 0.0j], [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 1.0j, 0.0 + 0.0j], [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 1.0j], ]) # TODO Hahn gate here does not include pi/2 term # is it meant to???? as below # inversion_gate = inversion_gate * np.pi/2 even_time_split = False if even_time_split: # unitary_time_evolution = h.exp_ham( # ham, # t, # precision=precision # ) unitary_time_evolution = (qutip.Qobj(-1j * ham * t).expm().full() ) # TODO deprecated total_evolution = np.dot( unitary_time_evolution, np.dot(inversion_gate, unitary_time_evolution)) else: first_unitary_time_evolution = expm(-1j * ham * t) second_unitary_time_evolution = np.linalg.matrix_power( first_unitary_time_evolution, 2) total_evolution = np.dot( second_unitary_time_evolution, np.dot(inversion_gate, first_unitary_time_evolution), ) # print("total ev:\n", total_evolution) ev_state = np.dot(total_evolution, state) nm = np.linalg.norm(ev_state) if np.abs(1 - nm) > 1e-5: print("[hahn] norm ev state:", nm, "\t t=", t) raise NameError("Non-unit norm") density_matrix = np.kron(ev_state, (ev_state.T).conj()) density_matrix = np.reshape(density_matrix, [4, 4]) reduced_matrix = partial_trace_out_second_qubit(density_matrix, qubits_to_trace=[1]) plus_state = np.array([1, 1]) / np.sqrt(2) # from 1000 counts - Poissonian noise = 1/sqrt(1000) # should be ~0.03 noise_level = 0.00 random_noise = noise_level * random_probe(1) noisy_plus = plus_state + random_noise norm_factor = np.linalg.norm(noisy_plus) noisy_plus = noisy_plus / norm_factor bra = noisy_plus.conj().T rho_state = np.dot(reduced_matrix, noisy_plus) expect_value = np.abs(np.dot(bra, rho_state)) # print("Hahn. Time=",t, "\t ex = ", expect_value) # print("[Hahn evolution] projecting onto:", repr(bra)) return 1 - expect_value # because we actually project onto |-> in experiment
expA = zeros_like(A) t = time.time() e.expm(A,expA) # call C version with Eigen Pade approx. ctime = min(ctime, time.time()-t) # time scipy version for r in range(Preps): t = time.time() expA2 = expm(A) ptime = min(ptime, time.time()-t) # time new python version for r in range(P2reps): t = time.time() expA3 = e2.expm(A) p2time = min(p2time, time.time()-t) Tc.append(ctime) Tp.append(ptime) Tp2.append(p2time) speedup = ptime/p2time cpeedup = ptime/ctime print "C (Eigen): %1.4f"%ctime," scipy: %1.4f"%ptime," new python: %1.4f"%p2time," Python speedup: %1.4f, %1.1f%%"%(speedup,100*(speedup-1)/speedup)," C++ speedup: %1.4f, %1.1f%%"%(cpeedup,100*(cpeedup-1)/cpeedup) normA = norm(expA-expA2)/norm(expA) # "relative" norm normA2 =norm(expA3-expA2)/norm(expA3) # "relative" norm epsilon = 1e-5 if normA > epsilon or normA2 > epsilon: print "A-A2 or A3-A2 is not small (%f or %f > %f)!"%(normA,normA2,epsilon) del A, expA, expA2, expA3
def test_expm_random_integers(self): """ a simple, arbitrary matrix """ a = np.array([[1, 2, 3], [1, 2, 1.1], [1, 0, 3]], dtype=np.float64) phi = expm(a) out_scipy = expm_scipy(a) assert_arrays_equal(out_scipy, phi, decimal=12)
def test_expm_all_ones(self): """ Test against a matrix of all ones """ a = np.ones((33, 33), dtype='float64') phi = expm(a) out_scipy = expm_scipy(a) assert_arrays_equal(out_scipy, phi, decimal=12)
def test_expm_all_zeros(self): """ Test against a matrix of all zeros """ a = np.zeros((20, 20), dtype='float32') phi = expm(a) out_scipy = expm_scipy(a) assert_arrays_equal(out_scipy, phi, decimal=12)
def test_expm_smallish_floats(self): """ This is just some arbitrary set of small-ish floats """ a = np.arange(1, 17, dtype='float64').reshape(4, 4) / 1e3 phi = expm(a) out_scipy = expm_scipy(a) assert_arrays_equal(out_scipy, phi, decimal=12)