quantumlib / Cirq

A python framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
Apache License 2.0
4.2k stars 999 forks source link

Easy method to transform from an unparameterized circuit to a parameterized circuit #970

Closed babbush closed 6 months ago

babbush commented 5 years ago

I want to share some initial thoughts that I think are part of a larger discussion we should have about which design patterns should be preferred when providing functions to generate parameterized circuits like those in QAOA or VQE.

I think it would be useful to be able "reparameterize" Cirq circuits, in a way that I will now explain. Suppose we are interested in implementing a variational algorithm based on low Trotter number, Trotterized adiabatic state preparation. Sort of like QAOA, the idea is to take as variational parameters the rotation angles corresponding to the Hamiltonian coefficients in the Trotter steps. But as with all parameterized circuits, the "initial guess" (by which I mean the initial setting of the variational parameters) is critically important. If one just takes a random initial guess, there is concentration of measure and gradients vanish, etc.

Currently, to produce functionality for exploring this ansatz, one would: 1) Write a function which produces a Cirq circuit using "symbols" as the variational parameters. 2) Write another function which computes the initial guess parameters.

Note that the function (1) will produce only a very specific parameterization, that is likely suitable for just one type of architecture using one type of gate. For instance, if we design function (1) to produce a circuit with parameterized CPhase gates, it is highly nontrivial to reuse that code to use QAOA on an ion trap, or the Gmon, which have different parameterized gates.

Suppose instead that one first writes a function which compiles a specific Cirq circuit (not using Symbol), which corresponds to doing the Trotterized adiabatic state preparation with the initial guess parameters. Then, imagine we had a function which would take as input this compiled Cirq circuit (not using Symbol), as well as a specification of a parameterized gate set (that does use Symbol) and then output for us a parameterized circuit (that does use Symbol) which consists of the parameterized gates we provided the function, as well as a dictionary which matches Symbols in the parameterized circuits to values so that if we assign those values to the Symbols in the parameterized circuit, we recover a unitary that is equivalent to original compiled circuit.

So the function I am suggesting here allows us to turn a compiled circuit into a variational algorithm, with a parameterization of our choosing, and we were able to extract the initial parameters effortlessly. I think this is a very NISQy way of thinking about variational algorithms. One downside is that it is not immediately apparent how one can constrain variational parameters if one wants to do that. For instance, suppose your Hamiltonian has some symmetry such that many of the coefficients in the Hamiltonian (and thus, rotations in the Trotter step ansatz) are identical. You might want to constrain all those angles to be the same in order to simplify the outer loop optimization (this is common in QAOA). What I've suggested above does not provide a good solution for constraining the parameters in this fashion. So, I don't think this design is perfect, and I don't recommend that anybody rush ahead and implement this. But I wanted to get a conversation started.

Thoughts?

dabacon commented 5 years ago

This is certainly doable and I see the motivation. Especially after passing through code that optimizes for hardware, this would give a more natural way to define parameter searches "near this initial guess".

Some implementation challenges:

kevinsung commented 5 years ago

Sometimes you might want a set of rotation angles to be constrained in a more complicated way than just being the same. For example, if you want to use the QAOA ansatz with an Ising Hamiltonian with random real-valued coefficients, then you would want the Symbol (representing "gamma" in the QAOA context) to be a multiplicative factor on the Hamiltonian coefficients. Then, you'd have a set of rotation angles whose possible values are linearly related (if you think of the angles as a vector, you're multiplying this vector by a scalar).

kevinsung commented 5 years ago

The specification of which gates should be parameterized will need to be more expressive than simply specifying the type of gate. We might want certain CZ gates to be parameterized and not others.

bryano commented 5 years ago

I'm missing the motivation. Is it not possible to do symbolic compilation?

bryano commented 5 years ago

Well, I guess I see the motivation, but I don't see why the solution is not to just have symbolic parameters from start to finish. Taking a concrete circuit and making it symbolic seems like way too hard a solution to a problem that wouldn't arise if we never made it concrete in the first place.

babbush commented 5 years ago

The primary motivation Bryan is that we want a way to easily change the parameterization. But I also see some problems with working with symbolic parameters. Let's take for instance a very common Trotter step variational circuit, based on the "split operator" method of forming Trotter steps. We have a Hamiltonian H = T + V T = \sum{pq} T{pq} a^\dagger_p aq V = \sum{pq} V_{pq} n_p n_q where these are the usual fermionic operators.

In the split operator Trotter step we decompose the T operator as R D R^\dagger where R is a Givens rotation based basis transformation and D is a diagonal single-electron operator. This allows us to apply e^{i T} exactly, as opposed to using Trotterization for it.

Suppose that we are interested in having the T{pq} values be the variational parameters (this is common). To determine R one must diagonalize the polynomial sized matrix of T{pq} values. You can see that the rotations in the circuit will be some horrible function of the variational parameters. The function will involve matrix diagonalization. We certainly do not want to symbolically diagonalize matrices. So there is really no choice but to use an implicit function, as opposed to something explicitly parameterized by Symbols. More generally, I don't think its fair to restrict the mapping between variational parameters and circuits to be something easily expressible with Symbols (and what I describe above is an example of such a case).

But there are other considerations too. For instance, do we want to write entirely different code for implementing this Trotter step and for making a variational algorithm out of the Trotter step? I don't think so. With what I'm suggesting one can take basically any circuit and turn it into a variational algorithm. And once that's accomplished, one can change the parameterization however one sees fit.

Wuggins commented 5 years ago

Has there been some more discussion about the mapping between variational parameters and circuits?

I was attempting to start a simple project today by parameterizing an exact coupled cluster singles ansatz (essentially just using the T_{pq} values from Ryan's above example as the only parameters of a circuit) and I was disappointed to realize how restrictive the current paradigm for a parameterized circuit is.

I realize that I'm new to the challenges that are being addressed here, but is there a fundamental reason why I can't just provide a function that generates a circuit for each setting of the parameters and skips the use of Symbols entirely? Even if the compilation and optimization of the circuit for a device adds a lot of overhead this might not be such a problem when amortized over enough circuit executions to estimate observables.

babbush commented 5 years ago

No there is no reason why you can't do that. In fact, we've been having some internal discussions that are increasingly sympathetic to that style of generating parameterized circuits.

Wuggins commented 5 years ago

Okay. It looks like that isn't well supported by the current infrastructure for variational studies, etc... Is that correct? I will put together a minimally disruptive way of enabling this kind of approach as a subclass of VariationalStudy and share it if you think it would be helpful.

babbush commented 5 years ago

As helpful as that sounds, please wait on this for a couple more weeks. We've been working on plans for this sort of thing that we'll start implementing and try to communicate to the Cirq community rather soon. The current "variational study" paradigm will be changing rather substantially.

Wuggins commented 5 years ago

Okay, great. I will just hack it in for my own purposes in the meantime. Thanks for the heads up.

dabacon commented 4 years ago

I still think this is a viable primitive. The real use would be if you are using this with parameters that are sweepable in some way, i.e. if you aren't just using this to generate single circuits (in which case like @Wuggins says just write a function of the parameters).

Would be curious to hear more feedback on if this is still something that feels useful.

babbush commented 4 years ago

For me this still feel useful.

Wuggins commented 4 years ago

Yeah, definitely! Although I think there have been two separate issues discussed in this thread and I would love to see solutions for both of them.

  1. It would be great to have a way of variationally adjusting some or all of the gates in a circuit that was specified without symbolic parameters to begin with. This was the original suggestion and it would be great for taking circuits which serve as a good initial guess and tuning them up.

  2. It would also be great to have better functionality for circuits that depend on their parameters in complicated ways. For example, the rotations angles in a givens rotation network can be determined programmatically from the t amplitudes (as in https://github.com/quantumlib/OpenFermion-Cirq/blob/master/openfermioncirq/primitives/bogoliubov_transform.py). The best way to use this in a variational algorithm right now is to generate the circuit again and again. I don't really see an easy way around this in the general case. As Ryan said, we don't really want to symbolically diagonalize matrices, and even if we did, we still wouldn't capture the whole range of what users might want to do with their parameterized circuits. If we could minimize the bottlenecks for performing parameter sweeps using this approach that would also be very useful.

tanujkhattar commented 6 months ago

With the recent changes to cirq transformers infrastructure, this use case should be fairly straightforward to implement using transformer primitives like cirq.map_operations.

In order to transform an "unparameterized circuit" to a "parameterized circuit", we can follow the following steps:

  1. Write a function def custom_map_func(op, moment_id) -> cirq.Operation: which maps an unparameterized operation in a circuit into a parameterized operation. The only requirement to write this function is to understand the parameters of the supporte gateset. For example, for an Rx rotation gate one could write

    param_dict = {}
    def custom_map_func(op, moment_id):
    if isinstance(op.gate, cirq.Rx):
        symbol = sympy.Symbol(f'{str(op.qubits[0])}:{moment_id}') # create a new unique symbol to parameterize this gate
        param_dict[symbol] = op.gate._rads
        return cirq.Rx(rads=symbol).on(*op.qubits)
    # similarly handle cases for other operations
  2. Once we have such a custom mapping function, we can call cirq.map_operations(circuit, map_func = custom_map_func) to preserve the structure of the ansatz but replace each gate with it's parameterized version. The resulting param_dict would get populated with default values of parameters that were originally used in the circuit.

I'll mark this issue as closed but feel free to reopen if you think there's more work to be done in order to address this use case.