Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove qargs/cargs/circuit from instructions, in favor of circuit keeping context #1816

Merged
merged 39 commits into from
Apr 3, 2019

Conversation

ajavadia
Copy link
Member

@ajavadia ajavadia commented Feb 15, 2019

Summary

Currently each instruction knows about the qubits and cbits that it is attached to. There are two problems with this:
1- It is redundant, as when building a DAG, there are edges corresponding to the qubits and cbits incoming to an instruction. So it adds the burden that these two sources of information must always be synced.
2- It prevents the instruction from being anonymous and thus reused and attached elsewhere.

Additionally, each instruction has a pointer to the circuit that contains it. This is also redundant, cyclic information, and causes many headaches (for example copying instructions amounts to a massively expensive operation). As a result there has been extensive efforts to avoid copying instructions (#1431, #1530), which made the code more verbose and hacky.

In this PR, I remove qargs and cargs from instructions, and give the responsibility of keeping context for each instruction to the circuit. Additionally I remove the circuit pointer, making the instructions very light-weight.

Examples:

So now you can create circuits like this (these can be e.g. opaque gates):

from qiskit import QuantumRegister
from qiskit import QuantumCircuit
from qiskit.circuit import Gate

q = QuantumRegister(3)
circ = QuantumCircuit(q)
my_gate = Gate(name='my_gate', num_qubits=2, params=[])
circ.append(my_gate, [q[0], q[1]])
circ.append(my_gate, [q[1], q[2]])
circ.draw()
         ┌─────────┐           
q1_0: |0>┤         ├───────────
         │ My_gate │┌─────────┐
q1_1: |0>┤         ├┤         ├
         └─────────┘│ My_gate │
q1_2: |0>───────────┤         ├
                    └─────────┘

Or custom composite gate:

# build a sub-circuit
sub_q = QuantumRegister(2)
sub_circ = QuantumCircuit(sub_q, name='sub_circ')
sub_circ.h(sub_q[0])
sub_circ.crz(0.1, sub_q[0], sub_q[1])
sub_circ.barrier()
sub_circ.iden(sub_q[1])
sub_circ.u3(0.1, 0.2, -0.2, sub_q[0])

# convert to a gate and stick it into an arbitrary place in the bigger circuit
sub_inst = sub_circ.to_instruction()
circ.append(sub_inst, [q[1], q[2]])
circ.draw()
q3_0: |0>────────────
         ┌──────────┐
q3_1: |0>┤          ├
         │ Sub_circ │
q3_2: |0>┤          ├
         └──────────┘

This will decompose to the sub gates when it goes through a Decompose or Unroller pass.

from qiskit import BasicAer
from qiskit.transpiler.passes import Decompose
from qiskit.transpiler import PassManager, transpile

sim = BasicAer.get_backend('qasm_simulator')

passes = [
    Decompose()
]
pm = PassManager(passes)
new_circ = transpile(circ, sim, pass_manager=pm)
new_circ.draw()

                                                
q3_0: |0>───────────────────────────────────────
         ┌───┐            ░ ┌──────────────────┐
q3_1: |0>H ├─────■──────░─┤ U3(0.1,0.2,-0.2) ├
         └───┘┌────┴────┐ ░ ├──────────────────┤
q3_2: |0>─────┤ Rz(0.1) ├─░─┤        Id        ├
              └─────────┘ ░ └──────────────────┘

Details and comments

1- Adds a public circuit.append() method that allows any operation to be added to the circuit on any wires (as long as the shapes match).

2- Adds a circuit.to_instruction() method to convert a constructed circuit to a Gate, which can then be added into another circuit. A gate is anonymous and not attached to any registers, so it's more flexible.

3- Adds a circuit.decompose() method that decomposes each composite instruction in the circuit by one level. Recursively calling this fuction will decompose the circuit entirely to a U and CX basis.

4- CompositeGate is no longer used. Instead, any Gate can be composite or not (for example, ZGate is a composite that is made up of U1Gate). If you build a gate from a circuit, then that circuit is your decomposition rule.

5- An instruction is uniquely identified by the following:
its type, name, num_qubits/num_clbits (i.e. its shape), and params.

6- Removed the reapply() method of instructions as it is no longer needed (it was used to attach a gate to the same qubits it was defined on).

7- The logic of how to print QASM is now in the circuit, because the instructions are significantly simplified.

Breaking Changes

Everything should work in a backwards compatible way except the following syntax for inverting gates:

circuit.s(qr).inverse()

This is not possible to keep with the current method because it requires an instruction to know about the circuit it is attached to. I think this is a weird syntax to begin with, and it should eventually have been removed. This basically says attach a gate, then modify it in place. Instead, we should attach modified versions of the instructions in the first place. For example:

circuit.append(SGate().inverse(), qr)
circuit.append(SGate().control(), qr)
circuit.append(SGate().power(), qr)
etc.

@ajavadia ajavadia force-pushed the no-instruction-qargs-cargs branch 2 times, most recently from aa5a407 to c53b52c Compare March 9, 2019 07:14
@ajavadia ajavadia force-pushed the no-instruction-qargs-cargs branch from 03a59d9 to 620a0ec Compare March 9, 2019 22:10
@ajavadia ajavadia changed the title [WIP] Remove qargs/cargs from instructions, in favor of circuit keeping context Remove qargs/cargs from instructions, in favor of circuit keeping context Mar 9, 2019
@ajavadia ajavadia force-pushed the no-instruction-qargs-cargs branch 7 times, most recently from 5a86f2a to 42556ba Compare March 11, 2019 06:44
@1ucian0 1ucian0 self-assigned this Mar 12, 2019
@ajavadia ajavadia force-pushed the no-instruction-qargs-cargs branch 2 times, most recently from 9fc3bdd to 3968684 Compare March 12, 2019 17:17
@ajavadia ajavadia force-pushed the no-instruction-qargs-cargs branch 2 times, most recently from c0f7fb7 to 6dce245 Compare March 14, 2019 15:30
@ajavadia ajavadia force-pushed the no-instruction-qargs-cargs branch from d79b633 to 73a3477 Compare April 3, 2019 05:20
@kdk kdk merged commit 675a645 into Qiskit:master Apr 3, 2019
maddy-tod added a commit to maddy-tod/qiskit-terra that referenced this pull request Apr 5, 2019
mtreinish added a commit to mtreinish/qiskit-core that referenced this pull request Apr 15, 2019
In Qiskit#1816 as part of the larger restructure the dag_to_circuit() and
circuit_to_dag() converter were modified to use a copy.deepcopy()
instead of native Instruction/Gate constructors. This caused a large
performance regressions (around 3x in the worst case) in certain
transpile calls. To avoid this performance penalty this commit
reintroduces a private function to call the class constructor for each
input gate to create a new instance of the object to append to the
output. This removes the deepcopy usage and should fix the performance
regression.

Fixes Qiskit#2131
kdk pushed a commit that referenced this pull request Apr 16, 2019
* Start of refactor

* More updating of variable names

* Refactored barrier before final measurements to remove a sort and several add/remove from the DAG

* Updated an import

* Updated the bits that rely on DAGNode equality to use the new equality method

* Updated to use nodes_on_wire and .remove for the list

* Fixed an issue with the difference between python 3.5 and 3.6 by adding a new method to the DAG which returns the successors in order

* Added in a new pass that merges barriers together - still needs testing

* Fixed a failing test - the condition wasn't being copied over

* Updated import to make it non cyclic

* Added in some testing for MergeAdjacentBarriers pass

* Included the test file this time

* Updated based on review comments - added in more comments and fixed sort

* Updated to work with #1816

* Fixed lint

* Updated ordered_successors() method to use topological sort instead of node_id

* Update qiskit/dagcircuit/dagcircuit.py

Co-Authored-By: maddy-tod <[email protected]>

* Renamed file and added in more comments about the types of barriers the pass will merge

* merge of stashed chanegs

* Fixed a bug in nodes_on_wire

* Removed some accidental changes to files

* Removed another unintended change

* Added in 2 more tests
kdk pushed a commit that referenced this pull request Apr 18, 2019
* Remove deepcopy usage from dag/circuit converters

In #1816 as part of the larger restructure the dag_to_circuit() and
circuit_to_dag() converter were modified to use a copy.deepcopy()
instead of native Instruction/Gate constructors. This caused a large
performance regressions (around 3x in the worst case) in certain
transpile calls. To avoid this performance penalty this commit
reintroduces a private function to call the class constructor for each
input gate to create a new instance of the object to append to the
output. This removes the deepcopy usage and should fix the performance
regression.

Fixes #2131

* Fix lint

* Fix circuit name conflict

* Try to fix lint again

* Don't convert custom gates to Instructions

* Remove node type check that's not needed with topological_op_nodes()
lia-approves pushed a commit to edasgupta/qiskit-terra that referenced this pull request Jul 30, 2019
…ping context (Qiskit#1816)

* remove qarg and carg from Instruction, modify circuit._attach

* update circuit_to_dag, remove qarg/carg from all gates

* fixing decomposition rule definitions

* update to decomposition rules

* bring measure qasm under QuantumCircuit

* add args context to InstructionSet

* fix extension tests, update matrix unitary gate to no qargs

* update snapshot instruction

* fix barrier insertion pass

* update transpiler passes for new format, rest of decomp rules

* add instruction shapes (num_qubits, num_clbits)

add instruction shapes (num_qubits, num_clbits)

* fix tests except initialize

* initialize is a generic instruction with decomposition rules

* move decompositions to generic instructions, add circuit_to_instruction

* add circuit.append()

* generalize dag_to_circuit

* use copy in circuit_to_dag and dag_to_circuit, temporary slowdown

* allow unroller pass to unroll generic instructions, not just gates

* initialize as Instruction that recursively decomposes

* revert to only gates being unrolled, and method for circuit_to_gate for now

* make qargs and cargs optional in circuit.append and dag.apply_operation_back

* add circuit.to_gate() method to wrap the convertor

* cleanup initialize

* use a list of Gate for a Gate.definition, decompose based on that

* add flag to indicate instruction reversibility

* decompose pass does not error on fundamental gates

* rename init to initialize in qobj

* circuit.decompose() method

* remove instruction._modifiers method

* remove instruction.circuit property

* CHANGELOG

* allow general subroutines via instruction definitions and update unroller

* instruction.reverse() -> instruction.mirror()

* inplace option for Instruction mirror and inverse

* base gates should not modify with inverse. add tests

* flatten register in circuit_to_instruction conversion
lia-approves pushed a commit to edasgupta/qiskit-terra that referenced this pull request Jul 30, 2019
…#2045)

* Start of refactor

* More updating of variable names

* Refactored barrier before final measurements to remove a sort and several add/remove from the DAG

* Updated an import

* Updated the bits that rely on DAGNode equality to use the new equality method

* Updated to use nodes_on_wire and .remove for the list

* Fixed an issue with the difference between python 3.5 and 3.6 by adding a new method to the DAG which returns the successors in order

* Added in a new pass that merges barriers together - still needs testing

* Fixed a failing test - the condition wasn't being copied over

* Updated import to make it non cyclic

* Added in some testing for MergeAdjacentBarriers pass

* Included the test file this time

* Updated based on review comments - added in more comments and fixed sort

* Updated to work with Qiskit#1816

* Fixed lint

* Updated ordered_successors() method to use topological sort instead of node_id

* Update qiskit/dagcircuit/dagcircuit.py

Co-Authored-By: maddy-tod <[email protected]>

* Renamed file and added in more comments about the types of barriers the pass will merge

* merge of stashed chanegs

* Fixed a bug in nodes_on_wire

* Removed some accidental changes to files

* Removed another unintended change

* Added in 2 more tests
lia-approves pushed a commit to edasgupta/qiskit-terra that referenced this pull request Jul 30, 2019
* Remove deepcopy usage from dag/circuit converters

In Qiskit#1816 as part of the larger restructure the dag_to_circuit() and
circuit_to_dag() converter were modified to use a copy.deepcopy()
instead of native Instruction/Gate constructors. This caused a large
performance regressions (around 3x in the worst case) in certain
transpile calls. To avoid this performance penalty this commit
reintroduces a private function to call the class constructor for each
input gate to create a new instance of the object to append to the
output. This removes the deepcopy usage and should fix the performance
regression.

Fixes Qiskit#2131

* Fix lint

* Fix circuit name conflict

* Try to fix lint again

* Don't convert custom gates to Instructions

* Remove node type check that's not needed with topological_op_nodes()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants