Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

quantum counting algorithm #168

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

friguzzi
Copy link

@tcNickolas I'm submitting a pull request for quantum counting. Haven't yet had tim to do the jupyther notebook

@msftclas
Copy link

msftclas commented Aug 20, 2019

CLA assistant check
All CLA requirements met.

@tcNickolas
Copy link
Member

It's fine to start with just a Q# project for now, and polish the Notebook later - depending on what exactly you do in the testing harness it might be a bit tricky, and it is usually easier to convert the final versions of the tasks to Notebook format than to update both presentations at each review iteration.

I'll take a closer look a bit later - I didn't expect that you have the kata ready, and I'm a bit busy today. Sorry about the delay, and thank you for the PR!

Copy link
Member

@tcNickolas tcNickolas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most interesting thing about quantum counting is that it can be used to find the approximate number of solutions to the problem (which then can be used to decide on the number of iterations to use in Grover search). The exact counting tales a lot of oracle calls (I'll need to do the math carefully but I think it's O(N) calls, so it's not an improvement over classical counting). I don't have a good reference for this at the moment, sorry, I'll try to find one and post it here.

If you want to keep developing this kata, this should be the direction to take it.

Sorry for the delay reviewing this PR!



// The Grover iteration
operation GroverIteration (register : Qubit[], oracle : (Qubit[] => Unit is Adj+Ctl)) : Unit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to use a more concise version of Grover iteration, similar to the one used here. The version from GroversAlgorithm kata is split into a lot of pieces for the purposes of making it into a kata, if you just need the iteration and the oracle conversion you can make it much shorter.

// - GroverIteration for the Grover operator
// - QuantumPhaseEstimation, for estimating the phase
// Counting should return the number of models, 4 in this case
operation Counting() : Double {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to teach the learner to implement a general-case quantum counting, not specific to one oracle instance. For that we'll need to pass the oracle and the problem size to the task as a parameter, otherwise a lot of things can be hardcoded. You can see this tutorial as an example.

operation T21_Counting_Test () : Unit {
let reference=Counting();
let actual=4.0;
EqualityWithinToleranceFact(reference,actual,3.0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the previous comment, you'll need to adjust the test to check the results on multiple oracles and input sizes. I found it easiest to use an oracle which just marks the first M out of N inputs as answers - it is not a very interesting oracle per se, but in the tasks in which we just care about the number of solutions to the problem it is a nice fast option (faster than, say, using an oracle that encodes a SAT problem).

operation Oracle_SolutionCount (queryRegister : Qubit[], target : Qubit, nSol : Int) : Unit is Adj {
    // Designate first nSol integers solutions (since we don't really care which ones are solutions)
    for (i in 0 .. nSol - 1) {
        (ControlledOnInt(i, X))(queryRegister, target);
    }
}

You'll also need to account for the fact that phase estimation is a probabilistic algorithm, so quantum counting fails with certain probability - you need to either modify the test harness to run the learner's solution multiple times or to stress in the task description that the solution has to take care of that.

// Hint: to solve this task you also need to use ancilla qubits
// This formula has 4 models out of 8 possible worlds

operation Oracle_Sprinkler (queryRegister : Qubit[], target : Qubit, ancilla : Qubit[]) : Unit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ancilla qubits are typically allocated inside the oracle operation and deallocated after their role in the computation is over; it will be better for this oracle to have just queryRegister and target as inputs and to handle ancilla qubits inside the operation. (In this case it is also possible to implement the oracle without using the ancillae by hardcoding the solutions, so it should be up to the learner to choose the implementation)

@tcNickolas tcNickolas mentioned this pull request Sep 22, 2019
@friguzzi
Copy link
Author

@tcNickolas the complexity of quantum counting is O(sqrt(N)), see the Nilsson Cheung book.
Thanks for the comments to the pull request, I will implement them

@friguzzi
Copy link
Author

friguzzi commented Oct 4, 2019

Hi @tcNickolas,
I've written this code

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

//////////////////////////////////////////////////////////////////////
// This file contains reference solutions to all tasks.
// The tasks themselves can be found in Tasks.qs file.
// We recommend that you try to solve the tasks yourself first,
// but feel free to look up the solution if you get stuck.
//////////////////////////////////////////////////////////////////////

namespace Quantum.Kata.Counting {
    
    open Microsoft.Quantum.Arrays;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;
	open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Oracles;
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Arithmetic;    
    open Microsoft.Quantum.Characterization;


    //////////////////////////////////////////////////////////////////
    // Part I. Oracle for Counting
    //////////////////////////////////////////////////////////////////
    
	operation Oracle_SolutionCount_Reference (queryRegister : Qubit[], target : Qubit, nSol : Int) : Unit is Ctl+ Adj {
    // Designate first nSol integers solutions (since we don't really care which ones are solutions)
		for (i in 0 .. nSol - 1) {
        (ControlledOnInt(i, X))(queryRegister, target);
		}
	}
    
    //////////////////////////////////////////////////////////////////
    // Part II. The Grover iteration
    //////////////////////////////////////////////////////////////////
   

    // Helper operation which converts marking oracle into phase oracle using an extra qubit
    operation ApplyMarkingOracleAsPhaseOracle (markingOracle : ((Qubit[], Qubit) => Unit is Ctl+Adj), register : Qubit[]) : Unit is Adj+Ctl {
        using (target = Qubit()) {
            // Put the target into the |-⟩ state
            X(target);
            H(target);
                
            // Apply the marking oracle; since the target is in the |-⟩ state,
            // flipping the target if the register satisfies the oracle condition will apply a -1 factor to the state
            markingOracle(register, target);
                
            // Put the target back into |0⟩ so we can return it
            H(target);
            X(target);
        }
    }
    
    // The Grover iteration
    operation GroverIteration (register : Qubit[], oracle : ((Qubit[],Qubit) => Unit is Adj)) : Unit  is Ctl+Adj
    {

            // apply oracle
            ApplyMarkingOracleAsPhaseOracle(oracle, register);
            // apply inversion about the mean
            ApplyToEachCA(H, register);
            ApplyToEachCA(X, register);
            Controlled Z(Most(register), Tail(register));
            ApplyToEachCA(X, register);
            ApplyToEachCA(H, register);
	}
    
    
    //////////////////////////////////////////////////////////////////
    // Part III. Putting it all together: Quantum Counting
    //////////////////////////////////////////////////////////////////
    

	operation Counting_Reference(n_bit : Int, n_sol: Int, precision: Int) : Double {
        mutable phase = -1.0;

        using ((reg,phaseRegister)=(Qubit[n_bit],Qubit[precision]))
        {                                  
        let oracle = OracleToDiscrete(GroverIteration(_, Oracle_SolutionCount_Reference(_,_,n_sol)));


        // Allocate qubits to hold the eigenstate of U and the phase in a big endian register 
            
            let phaseRegisterBE = BigEndian(phaseRegister);
            // Prepare the eigenstate of U
                ApplyToEach(H, reg);
            // Call library
            QuantumPhaseEstimation(oracle, reg, phaseRegisterBE);
            // Read out the phase
            set phase = IntAsDouble(MeasureInteger(BigEndianAsLittleEndian(phaseRegisterBE))) / IntAsDouble(1 <<< (n_bit));

            ResetAll(reg);
            ResetAll(phaseRegister);
        }
        let angle = PI()*phase;
        let res = (PowD(Sin(angle),2.0));

        return PowD(2.0,IntAsDouble(n_bit))*res;
    }

     operation CR(): Double {
	   return  Counting_Reference(4, 4, 3);
	 }
}

but I get this error

Errore	QS6207	The given argument does not support the required functor(s). Missing support for the functor(s) Controlled.	Counting	C:\Users\Fabrizio\source\repos\QuantumKatas\Counting\ReferenceImplementation.qs	61	

Line 61 is the following

  ApplyMarkingOracleAsPhaseOracle(oracle, register);

in GroverIteration.
It seems to me that Adj and Ctl are available for ApplyMarkingOracleAsPhaseOracle

@friguzzi
Copy link
Author

friguzzi commented Oct 4, 2019

I found the problem.

@friguzzi
Copy link
Author

friguzzi commented Oct 5, 2019

Hi @tcNickolas
I've prepared a draft, it's commit d5d5daf
Unfortunately I get values far from the correct answer, can you have a look?

@friguzzi
Copy link
Author

friguzzi commented Oct 8, 2019

The problem is the number of solutions found is 2^N-n_sol instead of n_sol

3 tests with different number of solutions: 16, 20, 40
@friguzzi
Copy link
Author

friguzzi commented Oct 9, 2019

Hi, the kata now works but I had to add a global phase

            R(PauliI, 2.0 * PI(), register[0]);

to GroverIteration.
The kata has now 3 tests with 7 bits, 5 precision bits and number of solution 16, 20 and 40

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants