def test_circuit_of_circuit(self): c1 = circuit.qc('c1') c1.reg(6, 0) c1.x(0) c1.cx(1, 2) c2 = circuit.qc('c2', eager=False) c2.x(1) c2.cx(3, 1) c1.qc(c2, 0) c1.qc(c2, 1) c1.qc(c2, 2) self.assertEqual(8, c1.ir.ngates)
def test_toffoli(self): qc = circuit.qc() qc.bitstring(1, 1, 0, 1, 1) qc.toffoli(0, 1, 3) self.compare_to(qc.psi, 1, 1, 0, 0, 1) qc = circuit.qc() qc.bitstring(1, 1, 0, 1, 1) qc.toffoli(4, 3, 0) self.compare_to(qc.psi, 0, 1, 0, 1, 1) qc = circuit.qc() qc.bitstring(1, 1, 0, 1, 1) qc.toffoli(1, 2, 3) self.compare_to(qc.psi, 1, 1, 0, 1, 1)
def test_cswap(self): qc = circuit.qc() qc.bitstring(1, 1, 0, 1, 1) qc.cswap(1, 2, 3) self.compare_to(qc.psi, 1, 1, 1, 0, 1) qc = circuit.qc() qc.bitstring(1, 1, 0, 1, 0) qc.cswap(2, 3, 4) self.compare_to(qc.psi, 1, 1, 0, 1, 0) qc = circuit.qc() qc.bitstring(1, 1, 0, 1, 0) qc.cswap(3, 2, 1) self.compare_to(qc.psi, 1, 0, 1, 1, 0)
def test_multi0(self): c = circuit.qc('multi', eager=True) comp = c.reg(4, (1, 0, 0, 1)) aux = c.reg(4) ctl = [0, [1], [2]] c.multi_control(ctl, 3, aux, ops.PauliX(), f'multi-x({ctl}, 5)') self.assertGreater(c.psi.prob(1, 0, 0, 0, 0, 0, 0, 0), 0.99)
def simple_walk(): """Simple quantum walk, allowing initial experiments.""" nbits = 8 qc = circuit.qc('simple_walk') x = qc.reg(nbits, 0b10000000) aux = qc.reg(nbits, 0) coin = qc.reg(1, 0) for _ in range(32): # Using a Hadamard coin, others are possible, of course. qc.h(coin[0]) incr(qc, 0, nbits, aux, [coin[0]]) decr(qc, 0, nbits, aux, [[coin[0]]]) # Find and print the non-zero amplitudes for all states for bits in helper.bitprod(nbits): idx_bits = bits for i in range(nbits): idx_bits = idx_bits + (0,) idx_bits0 = idx_bits + (0,) idx_bits1 = idx_bits + (1,) # Printing bits0 only, this can be changed, of course. if qc.psi.ampl(*idx_bits0) != 0.0: print('{:5.1f} {:5.4f}'.format(float(helper.bits2val(bits)), qc.psi.ampl(*idx_bits0).real))
def test_x_error_first_approach(self): error_qubit = 0 qc = circuit.qc('x-flip / correction') qc.qubit(alpha=0.6) qc.reg(2, 0) qc.cx(0, 1) qc.cx(0, 2) # insert error (index 0, 1, or 2) qc.x(error_qubit) syndrom = qc.reg(2, 0) qc.cx(0, syndrom[0]) qc.cx(1, syndrom[0]) qc.cx(1, syndrom[1]) qc.cx(2, syndrom[1]) # Measure syndrom qubit 3, 4: # 00 - nothing needs to be done # 01 - x(2) # 10 - x(0) # 11 - x(1) qc.x(error_qubit) p2, _ = qc.measure_bit(error_qubit, 0) self.assertTrue(np.allclose(p2, 0.36, atol=0.001))
def run_two_qubit_zi_experiment(): """Run VQE experiments with a given ansatz.""" # Best achieved result. Goal is to get as close to +1 as possible. max_expect = 0.0 # Perform experiments with randomly selected angles. for experiment in range(flags.FLAGS.experiments): # Pick random angles. for i in range(10): angles[i] = random.random() * 2.0 * math.pi # Construct and run the circuit. qc = circuit.qc('vqe') full_ansatz(qc) qc.z(0) # Measure the probablities as computed from the amplitudes. # We only do this once per experiment. p0, _ = qc.measure_bit(0, collapse=True) p1, _ = qc.measure_bit(1, collapse=True) # Simulate multiple measurements by sampling over the probabilities # to obtain a distribution of sampled states. The measurements above # are the probablities that a state would be found in the |0> state. # For each bit, we compare this probability against another random value r. # If the measured probability is < r, we pretend we've actually measured an # |0> state, else a |1> state. We do this via sample_state() on both qubits. # num_shots = flags.FLAGS.shots counts = [0] * 4 for _ in range(num_shots): bit0 = qc.sample_state(p0) bit1 = qc.sample_state(p1) counts[bit1 * 2 + bit0] += 1 # Compute the expectation value from samples measurements. Again, # |00> and |01> map to Eigenvalue +1 # |10> and |11> map to Eigenvalue -1 # # This is a bit of cheating. In this example we _know_ the # Eigenvalues and can therefore properly construct the expectation # value. I'd think in the general case it has to actually be # computed with <psi|H|psi>, which is still O(n^2). # expect = (counts[0] + counts[1] - counts[2] - counts[3]) / num_shots # Update and print currently best result. # if expect > max_expect: max_expect = expect print( 'Max expecation of H for experiment {:5d}: {:.4f} (target: 1.0)' .format(experiment, max_expect)) print(' |00>: {}, |01>: {}, |10>: {}, |11>: {}'.format( counts[0], counts[1], counts[2], counts[3])) print(' ', end='') for i in range(10): print('{:.1f} '.format(angles[i] / 2 / math.pi * 360), end='') print()
def test_multi1(self): c = circuit.qc('multi', eager=False) comp = c.reg(6) aux = c.reg(6) ctl = [0, 1, 2, 3, 4] c.multi_control(ctl, 5, aux, ops.PauliX(), f'multi-x({ctl}, 5)') self.assertEqual(41, c.ir.ngates)
def single_qubit_ansatz(theta: float, phi: float) -> circuit.qc: """Generate a single qubit ansatz.""" qc = circuit.qc('single-qubit ansatz Y') qc.qubit(1.0) qc.rx(0, theta) qc.ry(0, phi) return qc
def main(argv): if len(argv) > 1: raise app.UsageError('Too many command-line arguments.') print('Order finding.') number = flags.FLAGS.N a = flags.FLAGS.a # The classical part are handled in 'shor_classic.py' nbits = number.bit_length() print('Shor: N = {}, a = {}, n = {} -> qubits: {}'.format( number, a, nbits, nbits * 4 + 2)) qc = circuit.qc('order_finding') # Aux register for additional and multiplication. aux = qc.reg(nbits + 2, name='q0') # Register for QFT. This reg will hold the resulting x-value. up = qc.reg(nbits * 2, name='q1') # Register for multiplications. down = qc.reg(nbits, name='q2') qc.h(up) qc.x(down[0]) for i in range(nbits * 2): cmultmodn(qc, up[i], down, aux, int(a**(2**i)), number, nbits) inverse_qft(qc, up, 2 * nbits, with_swaps=1) qc.dump_to_file() print('Measurement...') total_prob = 0.0 for bits in helper.bitprod(nbits * 4 + 2): prob = qc.psi.prob(*bits) if prob > 0.01: intval = helper.bits2val(bits[nbits + 2:nbits + 2 + nbits * 2][::-1]) phase = helper.bits2frac(bits[nbits + 2:nbits + 2 + nbits * 2][::-1]) r = fractions.Fraction(phase).limit_denominator(8).denominator guesses = [ math.gcd(a**(r // 2) - 1, number), math.gcd(a**(r // 2) + 1, number) ] print( 'Final x: {:3d} phase: {:3f} prob: {:.3f} factors: {}'.format( intval, phase, prob.real, guesses)) total_prob += qc.psi.prob(*bits) if total_prob > 0.999: break print(qc.stats())
def test_circuit_of_inv_circuit(self): c1 = circuit.qc('c1') c1.reg(6, 0) c1.x(0) c1.rx(1, math.pi / 3) c1.h(1) c1.cz(3, 2) c2 = c1.inverse() c1.qc(c2, 0) self.assertEqual(8, c1.ir.ngates)
def test_acceleration(self): psi = state.bitstring(1, 0, 1, 0) qc = circuit.qc() qc.bitstring(1, 0, 1, 0) for i in range(4): qc.x(i) psi.apply(ops.PauliX(), i) qc.y(i) psi.apply(ops.PauliY(), i) qc.z(i) psi.apply(ops.PauliZ(), i) qc.h(i) psi.apply(ops.Hadamard(), i) if i: qc.cu1(0, i, 1.1) psi.apply_controlled(ops.U1(1.1), 0, i) if not psi.is_close(qc.psi): raise AssertionError('Numerical Problems') psi = state.bitstring(1, 0, 1, 0, 1) qc = circuit.qc() qc.bitstring(1, 0, 1, 0, 1) for n in range(5): qc.h(n) psi.apply(ops.Hadamard(), n) for i in range(0, 5): qc.cu1(n - (i + 1), n, math.pi / float(2**(i + 1))) psi.apply_controlled(ops.U1(math.pi / float(2**(i + 1))), n - (i + 1), n) for i in range(0, 5): qc.cu1(n - (i + 1), n, -math.pi / float(2**(i + 1))) psi.apply_controlled(ops.U1(-math.pi / float(2**(i + 1))), n - (i + 1), n) qc.h(n) psi.apply(ops.Hadamard(), n) if not psi.is_close(qc.psi): raise AssertionError('Numerical Problems')
def experiment_decr(): """Run a few decr experiments.""" qc = circuit.qc('decr') x = qc.reg(4, 15) aux = qc.reg(4) for val in range(15, 0, -1): decr(qc, 0, 4, aux) maxbits, _ = qc.psi.maxprob() res = helper.bits2val(maxbits[0:4]) if val-1 != res: raise AssertionError('Invalid Result')
def experiment_qc(a, b, cin, expected_sum, expected_cout): """Run a simple classic experiment, check results.""" qc = circuit.qc('classic') qc.bitstring(a, b, cin, 0, 0) fulladder_qc(qc) bsum, _ = qc.measure_bit(3, tostate=1, collapse=False) bout, _ = qc.measure_bit(4, tostate=1, collapse=False) print(f'a: {a} b: {b} cin: {cin} sum: {bsum} cout: {bout}') if bsum != expected_sum or bout != expected_cout: raise AssertionError('invalid results')
def experiment_mod_9(): """Run a few incr-mod-9 experiments.""" qc = circuit.qc('incr') x = qc.reg(4, 0) aux = qc.reg(5) # extra aux for val in range(18): incr_mod_9(qc, aux) maxbits, _ = qc.psi.maxprob() res = helper.bits2val(maxbits[0:4]) if ((val+1) % 9) != res: raise AssertionError('Invalid Result')
def arith_quantum(n, init_a, init_b, factor=1.0, dumpit=False): """Run a quantum add experiment.""" qc = circuit.qc('qadd') a = qc.reg(n + 1, helper.val2bits(init_a, n)[::-1], name='a') b = qc.reg(n + 1, helper.val2bits(init_b, n)[::-1], name='b') for i in range(0, n + 1): qft(qc, a, n - i) for i in range(0, n + 1): evolve(qc, a, b, n - i, factor) for i in range(0, n + 1): inverse_qft(qc, a, i) if dumpit: qc.dump_to_file() check_result(qc.psi, init_a, init_b, n + 1, factor)
def test_shor_9_qubit_correction(self): for i in range(9): qc = circuit.qc('shor-9') # print(f'Initialize qubit as 0.60|0> + 0.80|1>, error on qubit {i}') qc.qubit(0.6) qc.reg(8, 0) # Left Side. qc.cx(0, 3) qc.cx(0, 6) qc.h(0) qc.h(3) qc.h(6) qc.cx(0, 1) qc.cx(0, 2) qc.cx(3, 4) qc.cx(3, 5) qc.cx(6, 7) qc.cx(6, 8) # Error insertion, use x(i), y(i), or z(i) qc.x(i) # Fix. qc.cx(0, 1) qc.cx(0, 2) qc.ccx(1, 2, 0) qc.h(0) qc.cx(3, 4) qc.cx(3, 5) qc.ccx(4, 5, 3) qc.h(3) qc.cx(6, 7) qc.cx(6, 8) qc.ccx(7, 8, 6) qc.h(6) qc.cx(0, 3) qc.cx(0, 6) qc.ccx(6, 3, 0) prob0, _ = qc.measure_bit(0, 0, collapse=False) prob1, _ = qc.measure_bit(0, 1, collapse=False) self.assertTrue(math.isclose(math.sqrt(prob0), 0.6, abs_tol=0.001)) self.assertTrue(math.isclose(math.sqrt(prob1), 0.8, abs_tol=0.001))
def test_circuit_exec(self): c = circuit.qc('c', eager=False) c.reg(3, 0) c.h(0) c.h(1) c.h(2) c.run() self.assertEqual(3, c.ir.ngates) for i in range(8): self.assertGreater(c.psi[i], 0.3) c.run() self.assertEqual(3, c.ir.ngates) self.assertGreater(c.psi[0], 0.99) for i in range(1, 8): self.assertLess(c.psi[i], 0.01)
def test_opt(self): def decr(qc, idx, nbits, aux, controller=[]): for i in range(0, nbits): ctl = controller.copy() for j in range(nbits - 1, i, -1): ctl.append([j + idx]) qc.multi_control(ctl, i + idx, aux, ops.PauliX(), "multi-0-X") qc = circuit.qc('decr') x = qc.reg(4, 15) aux = qc.reg(4) for val in range(15, 0, -1): decr(qc, 0, 4, aux) # print(qc.stats()) qc.optimize()
def test_x_error(self): qc = circuit.qc('x-flip / correction') qc.qubit(0.6) qc.reg(2, 0) qc.cx(0, 2) qc.cx(0, 1) self.assertTrue(np.allclose(qc.psi.prob(0, 0, 0), 0.36, atol=0.001)) self.assertTrue(np.allclose(qc.psi.prob(1, 1, 1), 0.64, atol=0.001)) qc.x(0) # Fix qc.cx(0, 1) qc.cx(0, 2) qc.ccx(1, 2, 0) p0, _ = qc.measure_bit(0, 0, collapse=False) self.assertTrue(np.allclose(p0, 0.36)) p1, _ = qc.measure_bit(0, 1, collapse=False) self.assertTrue(np.allclose(p1, 0.64))
def main(argv): if len(argv) > 1: raise app.UsageError('Too many command-line arguments.') print(f'LaRose benchmark with {flags.FLAGS.nbits} qubits, ' + f'depth: {flags.FLAGS.depth}...') qc = circuit.qc(eager=False) qc.reg(flags.FLAGS.nbits, random.randint(0, 2 ^ flags.FLAGS.nbits), name='q') for d in range(flags.FLAGS.depth): print(f' depth: {d}') for bit in range(flags.FLAGS.nbits): qc.h(bit) qc.v(bit) if bit > 0: qc.cx(bit, 0) qc.dump_to_file()
def arith_quantum_constant(n, init_a, c): """Run a quantum add-constant experiment.""" qc = circuit.qc('qadd') a = qc.reg(n + 1, helper.val2bits(init_a, n)[::-1], name='a') for i in range(0, n + 1): qft(qc, a, n - i) angles = precompute_angles(c, n) for i in range(0, n): qc.u1(a[i], angles[i]) for i in range(0, n + 1): inverse_qft(qc, a, i) maxbits, _ = qc.psi.maxprob() result = helper.bits2val(maxbits[0:n][::-1]) if result != init_a + c: print(f'{init_a} + {c} = {result}') raise AssertionError('incorrect addition')
def single_qubit(): """Compute Pauli representation of single qubit.""" for i in range(10): # First we construct a circuit with just one, very random qubit. # qc = circuit.qc('random qubit') qc.qubit(random.random()) qc.rx(0, math.pi * random.random()) qc.ry(0, math.pi * random.random()) qc.rz(0, math.pi * random.random()) # Every qubit (rho) can be put in the Pauli Representation, # which is this Sum over i from 0 to 3 inclusive, representing # the four Pauli matrices (including the Identity): # # 3 # rho = 1/2 * Sum(c_i * Pauli_i) # i=0 # # To compute the various factors c_i, we multiply the Pauli # matrices with the density matrix and take the trace. This # trace is the computed factor: # rho = qc.psi.density() i = np.trace(ops.Identity() @ rho) x = np.trace(ops.PauliX() @ rho) y = np.trace(ops.PauliY() @ rho) z = np.trace(ops.PauliZ() @ rho) # Let's verify the result and construct a density matrix # from the Pauli matrices using the computed factors: # new_rho = 0.5 * (i * ops.Identity() + x * ops.PauliX() + y * ops.PauliY() + z * ops.PauliZ()) if not np.allclose(rho, new_rho): raise AssertionError('Invalid Pauli Representation') print(f'qubit({qc.psi[0]:11.2f}, {qc.psi[1]:11.2f}) = ', end='') print(f'{i:11.2f} I + {x:11.2f} X + {y:11.2f} Y + {z:11.2f} Z')
def test_multi_n(self): c = circuit.qc('multi', eager=True) c.reg(4, (1, 0, 0, 1)) aux = c.reg(4) ctl = [] c.multi_control(ctl, 3, aux, ops.PauliX(), 'single') self.assertGreater(c.psi.prob(1, 0, 0, 0, 0, 0, 0, 0), 0.99) ctl = [0] c.multi_control(ctl, 3, aux, ops.PauliX(), 'single') self.assertGreater(c.psi.prob(1, 0, 0, 1, 0, 0, 0, 0), 0.99) ctl = [1] c.multi_control(ctl, 3, aux, ops.PauliX(), 'single') self.assertGreater(c.psi.prob(1, 0, 0, 1, 0, 0, 0, 0), 0.99) ctl = [[1]] c.multi_control(ctl, 3, aux, ops.PauliX(), 'single') self.assertGreater(c.psi.prob(1, 0, 0, 0, 0, 0, 0, 0), 0.99) ctl = [0, [1], [2]] c.multi_control(ctl, 3, aux, ops.PauliX(), 'single') self.assertGreater(c.psi.prob(1, 0, 0, 1, 0, 0, 0, 0), 0.99)
def bench(): qc = circuit.qc() qc.rand(nbits) for i in range(nbits): qc.h(i)
def main(argv): if len(argv) > 1: raise app.UsageError('Too many command-line arguments.') # Alice has qubits 1 and 4 # Bob has qubits 2 and 3 qc = circuit.qc('swap the entanglement', eager=True) reg = qc.reg(4, 0) # Qubits 1 and 2 are entangled, and so are qubits 3 and 4: # # Alice 1 4 # | | # Bob 2 3 # qc.h(0) qc.cx(0, 1) qc.h(2) qc.cx(2, 3) # Now Alice and Bob physically separate. # Alice keeps qubits 1 and 4 on Earth. # Bob takes qubits 2 and 3 to the Moon. # ... travel, space ships, etc... # Alice performs a Bell measurement between qubits 1 and 4, # which means to apply a reverse entangler circuit: # # Alice 1~~~BM~~~4 # | | # Bob 2 3 # qc.cx(0, 3) qc.h(0) # At this point, Alice will physically measure her qubits 1 and 4. # There are four possible outcomes, |00>, |01>, |10>, and |11>, # # Depending on that outcome, now qubits 2 and 3 will also be # in a corresponding Bell state! The entanglement has been # teleported to qubits 2 and 3, even though they never # interacted before! # # Alice 1 4 # | | # Bob 2 ~~~~~~ 3 # # # To check the results: # # Iterate over the four possibilities # This array 'cases' represents 2 cases in each entry, either # qubit entries 0, 1, 2, 3 or # qubit entries 0, 5, 6, 3 (multiplied by c[7]) # # This covers all expected results. # # qubits 0 1 2 3 1 2 factor #----------------------------------------- cases = [[0, 0, 0, 0, 1.0, 1, 1, 1.0], [0, 0, 1, 1, 1.0, 1, 0, 1.0], [1, 0, 0, 0, 1.0, 1, 1, -1.0], [1, 0, 1, 1, 1.0, 1, 0, -1.0]] c07 = 1 / math.sqrt(2) psi = qc.psi for c in cases: qc.psi = psi qc.measure_bit(0, c[0], collapse=True) qc.measure_bit(3, c[3], collapse=True) qc.psi.dump(f'after measuring |{c[0]}..{c[3]}>') if not math.isclose(np.real(qc.psi.ampl(c[0], c[1], c[2], c[3])), c07, abs_tol=1e-5): raise AssertionError('Invalid measurement results') if not math.isclose(np.real(qc.psi.ampl(c[0], c[5], c[6], c[3])), c07 * c[7], abs_tol=1e-5): raise AssertionError('Invalid measurement results')
def compare_to(self, psi, *bits): qc = circuit.qc() qc.bitstring(*bits) self.assertTrue(psi.is_close(qc.psi))
def two_qubit(): """Compute Pauli representation for two-qubit system.""" for iteration in range(10): # First we construct a circuit with two, very random qubits. # qc = circuit.qc('random qubit') qc.qubit(random.random()) qc.qubit(random.random()) # Potentially entangle them. qc.h(0) qc.cx(0, 1) # Additionally rotate around randomly. for i in range(2): qc.rx(i, math.pi * random.random()) qc.ry(i, math.pi * random.random()) qc.rz(i, math.pi * random.random()) # Compute density matrix. rho = qc.psi.density() # Every rho can be put in the 2-qubit Pauli representation, # which is this Sum over i, j from 0 to 3 inclusive, representing # the four Pauli matrices (including the Identity): # # 3 # rho = 1/4 * Sum(c_ij * Pauli_i kron Pauli_j) # i,j=0 # # To compute the various factors c_ij, we multiply the Pauli # tensor products with the density matrix and take the trace. This # trace is the computed factor: # paulis = [ops.Identity(), ops.PauliX(), ops.PauliY(), ops.PauliZ()] c = np.zeros((4, 4), dtype=np.complex64) for i in range(4): for j in range(4): tprod = paulis[i] * paulis[j] c[i][j] = np.trace(rho @ tprod) # To test whether the two qubits are entangled, the diagonal factors # (without c[0][0]) are added up. If the sum is < 1.0, the qubit # states are still seperable. # diag = np.abs(c[1][1]) + np.abs(c[2][2]) + np.abs(c[3][3]) print(f'{iteration}: diag: {diag:5.2f} ', end='') if diag > 1.0: print('--> Entangled') else: print('Seperable') # Let's verify the result and construct a density matrix # from the Pauli matrices using the computed factors: # new_rho = np.zeros((4, 4), dtype=np.complex64) for i in range(4): for j in range(4): tprod = paulis[i] * paulis[j] new_rho = new_rho + c[i][j] * tprod if not np.allclose(rho, new_rho / 4, atol=1e-5): raise AssertionError('Invalid Pauli Representation')
def test_swap(self): qc = circuit.qc() qc.bitstring(1, 1, 0, 1, 1) qc.swap(1, 2) self.compare_to(qc.psi, 1, 0, 1, 1, 1)