def test_hash_function_implemented(): assert hash(AddConstant(3)) == hash(str(AddConstant(3))) assert hash(SubConstant(-3)) == hash(str(AddConstant(3))) assert hash(AddConstantModN(7, 4)) == hash(str(AddConstantModN(7, 4))) assert hash(SubConstantModN(7, 4)) == hash(str(AddConstantModN(-3, 4))) assert hash(MultiplyByConstantModN(3, 5)) == hash( MultiplyByConstantModN(3, 5))
def test_hash_function_implemented(): assert hash(AddConstant(3)) == hash(str(AddConstant(3))) assert hash(SubConstant(-3)) == hash(str(AddConstant(3))) assert hash(AddConstantModN(7, 4)) == hash(str(AddConstantModN(7, 4))) assert hash(SubConstantModN(7, 4)) == hash(str(AddConstantModN(-3, 4))) assert hash(MultiplyByConstantModN(3, 5)) == hash( str(MultiplyByConstantModN(3, 5))) assert hash(AddQuantum) == hash(str(AddQuantum)) assert hash(SubtractQuantum) == hash(str(SubtractQuantum)) assert hash(ComparatorQuantum) == hash(str(ComparatorQuantum)) assert hash(DivideQuantum) == hash(str(DivideQuantum)) assert hash(MultiplyQuantum) == hash(str(MultiplyQuantum))
def test_factoring(sim): eng = get_main_engine(sim) ctrl_qubit = eng.allocate_qubit() N = 15 a = 2 x = eng.allocate_qureg(4) X | x[0] H | ctrl_qubit with Control(eng, ctrl_qubit): MultiplyByConstantModN(pow(a, 2**7, N), N) | x H | ctrl_qubit eng.flush() cheat_tpl = sim.cheat() idx = cheat_tpl[0][ctrl_qubit[0].id] vec = cheat_tpl[1] for i in range(len(vec)): if abs(vec[i]) > 1.e-8: assert ((i >> idx) & 1) == 0 Measure | ctrl_qubit assert int(ctrl_qubit) == 0 del vec, cheat_tpl H | ctrl_qubit with Control(eng, ctrl_qubit): MultiplyByConstantModN(pow(a, 2, N), N) | x H | ctrl_qubit eng.flush() cheat_tpl = sim.cheat() idx = cheat_tpl[0][ctrl_qubit[0].id] vec = cheat_tpl[1] probability = 0. for i in range(len(vec)): if abs(vec[i]) > 1.e-8: if ((i >> idx) & 1) == 0: probability += abs(vec[i])**2 assert probability == pytest.approx(.5) Measure | ctrl_qubit Measure | x
def test_modmultiplier(eng): qureg = eng.allocate_qureg(4) init(eng, qureg, 4) MultiplyByConstantModN(3, 7) | qureg assert 1.0 == pytest.approx(abs(eng.backend.cheat()[1][5])) init(eng, qureg, 5) # reset init(eng, qureg, 7) MultiplyByConstantModN(4, 13) | qureg assert 1.0 == pytest.approx(abs(eng.backend.cheat()[1][2])) All(Measure) | qureg
def modular_exponentiation(input, output, a, b): """ Calculates the modular exponentiation value |O> = A^|X> mod B. The input register should contain the exponent and the output register will contain the result of the function. If |X> is in a superposition, it and |O> will be entangled so that whatever state |X> is measured to be, |O> will always be the solution to the function. Parameters: input (Qureg): The register representing X, the exponent of the power term. This can be in any superposition you want, so you can calculate the value for multiple inputs simultaneously. output (Qureg): The register representing O, which will contain the solution to the function. a (int): The base of the power term b (int): The modulus """ X | output[-1] # Output = |0...01> # Essentially, this works by converting modular exponentiation into a bunch of modular # multiplications. It will take n multiplications (where n is the length of the input # register). If the input is a uniform superposition, then it will contain 2^n possible states, # all of which will be calculated at the same time by this function. Thus, this offers an # exponential quantum speedup over classical computation. # # The reason this works is kind of mathy, and it's not hard to explain but it does require a lot # of math notation which is really hard to write in code comments. Nevertheless, here goes... # # Basically you can represent any number in binary, where each digit is a power of 2. # Take 13 for example. 13 in big-endian binary is 1101, which = 1*2^3 + 1*2^2 + 0*2^1 + 1*2^0. # Since you're a developer reading this code, I'm going to assume you understand how binary works. # So the expression A^X mod B can be rewritten by expanding X into its binary form: # A^X mod B = ( (A^X_0*2^n-1 mod B) * (A^X_1*2^n-2 mod B) * ... * (A^X_n-1*2^0 mod B) ) mod B. # When X_i == 0, the term is ignored. When X_i == 1, the term becomes A^2^(n-i-1) mod B. # Essentially that means we can do a modular multiplication for each term, and just use the bits of X # as controls for it. A^2^(n-i-1) is easy for a classical computer to calculate, so basically this # implements quantum modular exponentiation by turning it into a bunch of quantum modular # multiplications and classical modular exponentiations. Since it only does n iterations of either one, # but can run on 2^n states simultaneously, this is a big win. # # This would normally be a fine way to calculate things if you had an input and output register, # but the Q# version of modular multiplication runs in-place which means it only has one register for # the input and output. # (Note: since Cirq doesn't come with one of these functions, I ported Q#'s code over to Cirq so I # can actually implement Shor's algorihtm). # This means we have to redo the equation a little bit which involves way too many parentheses to write # here, but the end result is the same: repeat the calculation |O> = |O> * c mod B for each qubit in # the input register, where X_i controls the multiplication and c = A^(2^(n-i-1)) mod B. input_size = len(input) for i in range(0, input_size): power_of_two = input_size - 1 - i # n-i-1 power_of_guess = 2**power_of_two # 2^(n-i-1) constant = pow(a, power_of_guess, b) # c = A^(2^(n-i-1)) mod B # Note: ProjectQ is the first Python framework to actually have a math library # with it, so I can use their MultiplyByConstantModN implementation instead of # porting my own over from the previous libraries. This is quite helpful. with Control(input.engine, input[i]): MultiplyByConstantModN(constant, b) | output # |O> = |O> * c mod B
def find_period(engine, N): n = int(np.ceil(np.log2(N))) a = shor.find_co_prime_stochastic(N) if a < 0: print("Factor is", -a) exit(0) print("a =", a) qubits = engine.allocate_qureg(3 * n) All(H) | qubits[0:(2 * n)] for i in range(2 * n): C(MultiplyByConstantModN(pow(a, 2**i, N), N)) | (qubits[i], qubits[(2 * n):(3 * n)]) qft.qft_inverse(engine, qubits[0:(2 * n)]) All(Measure) | qubits engine.flush() measurements = [int(q) for q in qubits[0:(2 * n)]] y = sum([(measurements[2 * n - 1 - i] * 1. / (1 << (i + 1))) for i in range(2 * n)]) period = Fraction(y).limit_denominator(N - 1).denominator print("period found =", period) if period % 2 != 0: period *= 2 if np.mod(period, 2) == 0: print(shor.find_prime_factors(N, pow(a, int(period / 2)))) print('\nMeasured: {0}'.format([int(q) for q in qubits]))
def shor(eng, entrada, inicial): n = int(ceil(log(entrada, 2))) x = eng.allocate_qureg(n) X | x[0] medidas = [0] * (2 * n) ctrl = eng.allocate_qubit() for i in range(2 * n): a_novo = pow(2**(2 * n - 1 - i), entrada) #exponenciacao modular em Python H | ctrl with Control(eng, ctrl): MultiplyByConstantModN( a_novo, entrada) | x #exponenciacao modular quantica for j in range(i): if medidas[j]: R(-pi / (2**(i - j))) | ctrl #transformada de Fourier quantica H | ctrl Measure | ctrl eng.flush() if medidas[i]: X | ctrl Measure | x y = sum([(medidas[2 * n - 1 - j] * 1. / (2**(j + 1))) for j in range(2 * n)]) s = Fraction(y).limit_denominator(entrada - 1).denominator return s
def run_shor(eng, N, a, verbose=False): """ Runs the quantum subroutine of Shor's algorithm for factoring. Args: eng (MainEngine): Main compiler engine to use. N (int): Number to factor. a (int): Relative prime to use as a base for a^x mod N. verbose (bool): If True, display intermediate measurement results. Returns: r (float): Potential period of a. """ n = int(math.ceil(math.log(N, 2))) x = eng.allocate_qureg(n) X | x[0] measurements = [0] * (2 * n) # will hold the 2n measurement results ctrl_qubit = eng.allocate_qubit() for k in range(2 * n): current_a = pow(a, 1 << (2 * n - 1 - k), N) # one iteration of 1-qubit QPE H | ctrl_qubit with Control(eng, ctrl_qubit): MultiplyByConstantModN(current_a, N) | x # perform inverse QFT --> Rotations conditioned on previous outcomes for i in range(k): if measurements[i]: R(-math.pi / (1 << (k - i))) | ctrl_qubit H | ctrl_qubit # and measure Measure | ctrl_qubit eng.flush() measurements[k] = int(ctrl_qubit) if measurements[k]: X | ctrl_qubit if verbose and MPI.COMM_WORLD.Get_rank() == 0: print("\033[95m{}\033[0m".format(measurements[k]), end="") sys.stdout.flush() All(Measure) | x # turn the measured values into a number in [0,1) y = sum([(measurements[2 * n - 1 - i] * 1. / (1 << (i + 1))) for i in range(2 * n)]) # continued fraction expansion to get denominator (the period?) r = Fraction(y).limit_denominator(N - 1).denominator # return the (potential) period return r
def test_modmultiplier(): sim = Simulator() eng = MainEngine(sim, [AutoReplacer(), InstructionFilter(no_math_emulation)]) qureg = eng.allocate_qureg(4) init(eng, qureg, 4) MultiplyByConstantModN(3, 7) | qureg assert 1. == pytest.approx(abs(sim.cheat()[1][5])) init(eng, qureg, 5) # reset init(eng, qureg, 7) MultiplyByConstantModN(4, 13) | qureg assert 1. == pytest.approx(abs(sim.cheat()[1][2])) Measure | qureg
def test_restriction(): engine_list = restrictedgateset.get_engine_list( one_qubit_gates=(Rz, H), two_qubit_gates=(CNOT, AddConstant, Swap), other_gates=(Toffoli, AddConstantModN, MultiplyByConstantModN(2, 8)), ) backend = DummyEngine(save_commands=True) eng = projectq.MainEngine(backend, engine_list, verbose=True) qubit1 = eng.allocate_qubit() qubit2 = eng.allocate_qubit() qubit3 = eng.allocate_qubit() eng.flush() CNOT | (qubit1, qubit2) H | qubit1 with Control(eng, qubit2): Rz(0.2) | qubit1 Measure | qubit1 AddConstant(1) | (qubit1 + qubit2) AddConstantModN(1, 9) | (qubit1 + qubit2 + qubit3) Toffoli | (qubit1 + qubit2, qubit3) Swap | (qubit1, qubit2) MultiplyByConstantModN(2, 8) | qubit1 + qubit2 + qubit3 TimeEvolution(0.5, QubitOperator("X0 Y1 Z2")) | qubit1 + qubit2 + qubit3 QFT | qubit1 + qubit2 + qubit3 Rx(0.1) | (qubit1) MultiplyByConstantModN(2, 9) | qubit1 + qubit2 + qubit3 eng.flush() assert backend.received_commands[4].gate == X assert len(backend.received_commands[4].control_qubits) == 1 assert backend.received_commands[5].gate == H assert backend.received_commands[6].gate == Rz(0.1) assert backend.received_commands[10].gate == Measure assert backend.received_commands[11].gate == AddConstant(1) assert backend.received_commands[12].gate == AddConstantModN(1, 9) assert backend.received_commands[13].gate == X assert len(backend.received_commands[13].control_qubits) == 2 assert backend.received_commands[14].gate == Swap assert backend.received_commands[15].gate == MultiplyByConstantModN(2, 8) for cmd in backend.received_commands[16:]: assert cmd.gate != QFT assert not isinstance(cmd.gate, Rx) assert not isinstance(cmd.gate, MultiplyByConstantModN) assert not isinstance(cmd.gate, TimeEvolution)
def run_simulation(sim): eng = MainEngine(sim, []) quint = eng.allocate_qureg(5) AddConstant(3) | quint All(Measure) | quint eng.flush() results[0].append([int(qb) for qb in quint]) AddConstantModN(4, 5) | quint All(Measure) | quint eng.flush() results[1].append([int(qb) for qb in quint]) MultiplyByConstantModN(15, 16) | quint All(Measure) | quint eng.flush() results[2].append([int(qb) for qb in quint])
def run_shor(eng, q, x, showMeasure, N): n = int(math.ceil(math.log(q - 1, 2))) if showMeasure: print("\n\tn: ", n) a = eng.allocate_qureg(n) All(H) | a measurements = [0] * (n) ctrl_qubit = eng.allocate_qubit() for k in range(n): print("K: ", k) current_x = pow(x, 1 << (n - 1 - k)) print("PROBLEM1") H | ctrl_qubit with Control(eng, ctrl_qubit): MultiplyByConstantModN(current_x, N) | a #PROBLEM print("PROBLEM2") # and measure Measure | ctrl_qubit eng.flush() measurements[k] = int(ctrl_qubit) if showMeasure: print("\033[95m{}\033[0m".format(measurements[k]), end="") sys.stdout.flush() All(Measure) | a m = 0 print("\n") for i in range(n): m += measurements[n - 1 - i] * 1. / (1 << (i + 1) ) #Value of m/q in Hayward's r = Fraction(m).limit_denominator(N - 1).denominator #PROBLEM print("PROBLEM3") return r
def ExecuteShorAlgorithm(eng, N, a, verbose=False): n = int(math.ceil(math.log(N, 2))) x = eng.allocate_qureg(n) X | x[0] measurements = [0] * (2 * n) ctrl_qubit = eng.allocate_qubit() for k in range(2 * n): current_a = pow(a, 1 << (2 * n - 1 - k), N) H | ctrl_qubit with Control(eng, ctrl_qubit): MultiplyByConstantModN(current_a, N) | x for i in range(k): if measurements[i]: R(-math.pi / (1 << (k - i))) | ctrl_qubit H | ctrl_qubit Measure | ctrl_qubit eng.flush() measurements[k] = int(ctrl_qubit) if measurements[k]: X | ctrl_qubit if verbose: print("\033[95m{}\033[0m".format(measurements[k]), end="") sys.stdout.flush() All(Measure) | x y = sum([(measurements[2 * n - 1 - i] * 1. / (1 << (i + 1))) for i in range(2 * n)]) r = Fraction(y).limit_denominator(N - 1).denominator return r
def test_multiplybyconstmodn(): assert MultiplyByConstantModN(3, 4) == MultiplyByConstantModN(3, 4) assert not MultiplyByConstantModN(3, 4) == MultiplyByConstantModN(4, 4) assert not MultiplyByConstantModN(3, 5) == MultiplyByConstantModN(3, 4) assert MultiplyByConstantModN(7, 4) != MultiplyByConstantModN(3, 4) assert MultiplyByConstantModN(3, 5) != MultiplyByConstantModN(3, 4) assert str(MultiplyByConstantModN(3, 4)) == "MultiplyByConstantModN(3, 4)"
def test_period_6(self): """ Tests QFT by running a single iteration of the period-finding subroutine from Shor's algorithm. This test will use 21 as the number to factor, 11 as the original guess, and ensure that QFT reports that the modular exponential equation has a period of 6. """ # So this test basically just runs a hardcoded iteration of the quantum portion # of Shor's algorithm. I don't want to explain the entire thing here; you can # look at shor.py for my implementation, which has plenty of documentation # attached to it. For this test, I'm trying to factor 21. That means the # "output" register needs to be 5 qubits (because 2^4 = 16 and 2^5 = 32, so it # needs 5 qubits to be represented in binary). For the input register, I'm going # with 9 qubits: 21^2 = 441, 2^8 = 256, and 2^9 = 512, so 21^2 needs 9 qubits to # be represented in binary. That will give 512 discrete states. For a guess of # 11, the period will be 6: # ------------------------- # State (i) | 11^i mod 21 # ------------------------- # 0 | 1 # 1 | 11 # 2 | 16 # 3 | 8 # 4 | 4 # 5 | 2 # 6 | 1 <== Pattern repeats here, after 6 entries # 7 | 11 # 8 | 16 # ... # # QFT should return some value X which, when divided by 512, should be really # close to 0/6, 1/6, 2/6, 3/6, 4/6, or 5/6. The amplitude peaks (the expected # values) are 0, 85, 171, 256, 341, and 427. input_length = 9 output_length = 5 number_to_factor = 21 guess = 11 engine = MainEngine() input = engine.allocate_qureg(input_length) output = engine.allocate_qureg(output_length) All(H) | input # Input = |+...+> X | output[-1] # Output = |0...01> # Do the arithmetic so the input register is entangled with the output register; after # this, if the state X is measured on the input register, the output register will always # be measured as 11^X mod 21. for i in range(0, input_length): power_of_two = input_length - 1 - i power_of_guess = 2 ** power_of_two constant = pow(guess, power_of_guess, number_to_factor) # Note: ProjectQ is the first Python framework to actually have a math library # with it, so I can use their MultiplyByConstantModN implementation instead of # porting my own over from the previous libraries. This is quite helpful. with Control(engine, input[i]): MultiplyByConstantModN(constant, number_to_factor) | output # Run inverse QFT (the analog of the normal DFT) to find the period with Dagger(engine): qft.qft(input) result_string = "" for qubit in input: Measure | qubit result_string += str(int(qubit)) result = int(result_string, 2) # Measure the resulting period and make sure it's close to a multiple of 1/6, # with a tolerance of 0.01. scaled_measurement = result / 512 * 6 nearest_multiple = round(scaled_measurement) delta = abs(scaled_measurement - nearest_multiple) # Cleanup reset(output) engine.flush() print(f"Measured {result}/512 => {scaled_measurement}, delta = {delta}") if delta >= 0.01: self.fail(f"QFT failed, delta of {delta} is too high.") print("Passed!")
def eng(): return MainEngine( backend=Simulator(), engine_list=[ AutoReplacer(rule_set), InstructionFilter(no_math_emulation) ], ) rule_set = DecompositionRuleSet( modules=[projectq.libs.math, qft2crandhadamard, swap2cnot]) @pytest.mark.parametrize('gate', (AddConstantModN(-1, 6), MultiplyByConstantModN( -1, 6), MultiplyByConstantModN(4, 4)), ids=str) def test_invalid(eng, gate): qureg = eng.allocate_qureg(4) init(eng, qureg, 4) with pytest.raises(ValueError): gate | qureg eng.flush() def test_adder(eng): qureg = eng.allocate_qureg(4) init(eng, qureg, 4) AddConstant(3) | qureg