-
Notifications
You must be signed in to change notification settings - Fork 90
Add Type1 target package and simulator support #367
Conversation
|
@bettinaheim @cgranade Something that is worth calling out in these changes is the introduction of By way of background, the need to introduce these was that the target gate set only supports singly controlled X and singly controlled Z, and no other controlled operations. So anywhere that our API has support for the Controlled specialization that accepts arbitrary number of controls, it had to be replaced with a decomposition on top of singly controlled variants. |
cgranade
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only taken a rough skim so far, but left what comments I have so far; I did have one quick question, though:
Initially I'd hoped to have these marked as internal so that we wouldn't have to fully document them, but in order for a body intrinsic operation to be implemented outside of its own binary it cannot be marked as internal.
Is that a limitation on the Q# source, or the generated C# source? If the latter, is that the case even with the InternalsVisibleTo C# attribute?
src/Simulation/Simulators.Type1.Tests/OperationsTestHelperSimSupport.cs
Outdated
Show resolved
Hide resolved
src/Simulation/Simulators.Type1.Tests/OperationsTestHelperSimSupport.cs
Outdated
Show resolved
Hide resolved
| { | ||
| public partial class QuantumSimulator | ||
| { | ||
| public class QSimApplyControlledZ : Intrinsic.ApplyControlledZ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be curious how much this and the MCX call above help performance compared with decomposing at the Q# level and then calling into the lower-level simulator functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are decomposing at the Q# level as much as possible right now, and that's part of what made these necessary. Because the Controlled specialization allows for any number of controls, I need to have a callable that is specifically the singly controlled gate and no Controlled specialization. Otherwise it won't match what the target architecture actually supports. It makes sense from a pure instruction translation standpoint, but it looks very odd and redundant when layered on top of our simulator that supports more than this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To answer that a slightly different way, the Type1 package captures targeting a gate set is that is significantly more constrained than the one exposed by our simulator. As a result, implementing these limited intrinsics on top of our simulator requires using these C# classes to act as a shim that exposes less functionality. It's essentially the opposite of optimization, and it shows: simulating certain operations using this Type1 package are noticeably slower because it will use a complex decomposition, possibly including additional qubits and entanglers, instead of just calling directly into an optimized simulator API.
src/Simulation/TargetDefinitions/Decompositions/ApplyControlledX.qs
Outdated
Show resolved
Hide resolved
| } | ||
| } | ||
| else { | ||
| using( q = Qubit() ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks to use an additional spare qubit when measuring a multi-qubit Pauli operator; that's completely valid, but there's a tradeoff between that and finding the right Clifford to map back to a single-qubit measurement, then calling the measurement within that Clifford.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! The hope here is that as we find alternative decompositions for certain operations, we can introduce those here and allow users to select between them by choosing different targeting packages. How we can do that in a way that doesn't cause an explosion in the number of targeting packages is another question though...
Co-authored-by: Chris Granade <chgranad@microsoft.com>
It is just a C# limitation, but even using |
|
@cgranade @bettinaheim I suppose I could use the |
I agree that approach would kick the can down the road, but I honestly think that may be for the best in this case. Introducing new public Q# APIs comes at a large cost in terms of long-term maintenance, such that kicking the can gives us a bit of an opportunity to come up with an approach that doesn't commit us longer-term. That's just my 2¢, though, so please take with an appropriate grain of salt (apologies for mixed metaphors). |
Since they're intended to be implemented by simulators, it seems to me like they are actually part of the public API? Is the issue only that it shouldn't be exposed to Q# code, or that it shouldn't be exposed at all (in Q# or .NET)? |
I think part of the problem is that they are part of the public C# API, but shouldn't be part of the public Q# API; from the perspective of a quantum program, these APIs are an implementation detail internal to the simulation runtime, if I understand correctly. |
|
I see, so we want the operation to be internal in Q#, but for C# generation to generate a public class. That is technically possible to do if we change C# generation, but it seems like there should be a cleaner way. |
Agreed, there should be; hence why I tend to lean towards kicking the can whilst we brainstorm on and design that better way, rather than commiting to new public APIs in lieu of that. |
|
Agreed on all points, thanks @cgranade and @SamarSha! I'll make the update in this PR to switch these new additions to internal and add this specific issue to the checklist in #249 so we can review it before the feature branch gets merged. For now, I'll treat any and all callabes that would be API additions as internal to avoid any new public API surface, and then we can debate the merits of exposing and fully documenting them on a case by case basis at a later date. |
|
Ok, this seems like it should be ready for merge into the feature branch. Please let me know if there are any other things I should address in this PR and/or track in the overall issue for this feature! |
src/Simulation/Simulators.Type1.Tests/Tests.Microsoft.Quantum.Simulators.Type1.csproj
Show resolved
Hide resolved
|
This is once again building and passing the tests :). I'd like to try merging it by EOD. |
|
@SamarSha @cgranade @alan-geller @bettinaheim Any chance I could get sign off on this so I can get it merged into the feature branch? Thanks! |
src/Simulation/Simulators/QuantumSimulator/ApplyUncontrolledZ.cs
Outdated
Show resolved
Hide resolved
| if (Length(paulis) == 1) { | ||
| rotation(paulis[0], qubits[0]); | ||
| } | ||
| else { // Length(paulis) > 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or Length(paulis) == 0? Does the qubits[0] reference below fail in that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thankfully no, because this utility is only called by Exp in ExpFromExpUtil and ExpFrac in ExpFracFromExpUtil with a check:
if (Length(paulis) != 0) {or
let (newPaulis, newQubits) = RemovePauliI(paulis, qubits);
if (Length(newPaulis) != 0) {
ExpUtil(newPaulis, theta , newQubits, R(_, -2.0 * theta, _));
}
else {
ApplyGlobalPhase(theta);
}Where RemovePauliI correctly handles empty lists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a quick comment mentioning this would be good, since it's potentially a gotcha if another place uses this operation without checking for length == 0?
| } | ||
| } | ||
| } | ||
| else { // Length(paulis) > 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or Length(paulis) == 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above; this utility is only called by Exp and ExpFrac that already check for an empty Pauli array.
Co-authored-by: Sarah Marshall <33814365+samarsha@users.noreply.github.com>
This change adds the Type1 target package for Q# targets that support a limited number of control operations. The change includes support for simulating a Type1 package and tests that verify unitary gate behavior for that package. This part of the ongoing work described in #249.