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

Adds various convenience functions to qiskit #12470

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions qiskit/circuit/library/blueprintcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,10 @@ def count_ops(self):
self._build()
return super().count_ops()

def num_nonlocal_gates(self):
def num_nonlocal_gates(self, n=2):
if not self._is_built:
self._build()
return super().num_nonlocal_gates()
return super().num_nonlocal_gates(n)

def num_connected_components(self, unitary_only=False):
if not self._is_built:
Expand Down
98 changes: 94 additions & 4 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3386,6 +3386,91 @@ def depth(

return max(op_stack)

def depth_2q(self, include_directives: bool = False) -> int:
"""Returns the depth in terms of quantum gates with at least 2-qubits,
Copy link
Member

Choose a reason for hiding this comment

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

why the function depth_2q is with >=2 and num_2q_gates is with ==2 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

num_nq_gates exactly checks the number of qubits because I thought users might be interested in that quantity in general. Also, num_nonlocal_gates determines the number of gates with at least two qubits. I extended the arguments of num_nonlocal_gates to include an integer n that specifies the minimum number of qubits in a gate to still be counted towards the quantity returned by num_nonlocal_gates. The default behavior of num_nonlocal_gates stays the same, so I hope our stability policy is not violated...

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree the name is a bit confusing an inconsistent with the others. Consider changing to something like depth_multi_qubit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was also thinking about whether depth_nq should work on gates with exactly n qubits or for >= n qubits. I decided for the latter because I thought the reason an user might want depth_nq to exclude certain gates is that they are comparatively cheap. I think it is safe to assume that if an user regards a gate with n qubits to be relatively expensive, they would also consider a gate with n+i qubits to be expensive (if there are no other properties to decide on). I also was not able to see a situation where an user might only be interested in the two-qubit gate depth while ignoring all other gates in the presence of e.g. three-qubit gates.

excluding directives by default.

Args:
include_directives (bool): Whether to include directives such as barriers

Returns:
int: Quantum circuit depth in terms of two-qubit gates
"""
if not include_directives:
return self.depth(
lambda x: not getattr(x.operation, "_directive", False) and x.qubits >= 2
)
else:
return self.depth(lambda x: x.qubits >= 2)

def depth_nq(self, n: int, include_directives: bool = False) -> int:
"""Returns the depth in terms of quantum gates with at least n-qubits,
Copy link
Member

Choose a reason for hiding this comment

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

The key phrase seems to be at least. And for this reason, depth_nq with n=2 is not the same thing as depth_2q. This is potentially confusing.

Copy link
Member

Choose a reason for hiding this comment

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

why the function depth_nq is with >=n and num_nq_gates is with ==n ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe have just this one with default n = 2 and drop depth_2q

excluding directives by default.

Args:
n (int): The minimum number of qubits in an instruction that counts towards the depth.
include_directives (bool): Whether to include directives such as barriers
Returns:
int: Quantum circuit depth in terms of n-qubit gates.
"""
if not include_directives:
return self.depth(
lambda x: not getattr(x.operation, "_directive", False) and x.qubits >= n
)
else:
return self.depth(lambda x: x.qubits >= n)

def num_2q_gates(self, include_directives: bool = False) -> int:
"""Returns the number of gates with exactly two qubits, excluding directives by default.

Args:
include_directives (bool): Whether to include directives such as barriers

Returns:
int: The number of gates in the quantum circuit with exactly two qubits.
"""
if not include_directives:
return self.size(
lambda x: not getattr(x.operation, "_directive", False) and x.qubits == 2
)
else:
return self.size(lambda x: x.qubits == 2)

def num_nq_gates(self, n: int, include_directives: bool = False) -> int:
Copy link
Contributor

Choose a reason for hiding this comment

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

Same suggestion: have n:int = 2 and drop the num_2q_gates

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for your suggestion. I removed the n=2 versions of the methods and updated the release notes accordingly!

"""Returns the number of gates with exactly n qubits, excluding directives by default.
See :meth:`.num_nonlocal_gates` to retrieve the number of gates with at least two-qubits,
(excluding directives).

Args:
n (int): The exact number of qubits in an instruction.
include_directives (bool): Whether to include directives such as barriers

Returns:
int: The number of gates in the quantum circuit with exactly n qubits.
"""
if not include_directives:
return self.size(
lambda x: not getattr(x.operation, "_directive", False) and x.qubits == n
)
else:
return self.size(lambda x: x.qubits == n)

def num_nonidle_qubits(self) -> int:
"""Returns the number of qubits in the quantum circuit that are non-idle,
i.e. are part of at least one gate.

Returns:
int: The number of non-idle qubits in the quantum circuit.
"""
return len(
{
qubit
for inst in self.data
for qubit in inst.qubits
if not getattr(inst.operation, "_directive", False)
}
)

def width(self) -> int:
"""Return number of qubits plus clbits in circuit.

Expand Down Expand Up @@ -3424,14 +3509,19 @@ def count_ops(self) -> "OrderedDict[Instruction, int]":
count_ops[instruction.operation.name] = count_ops.get(instruction.operation.name, 0) + 1
return OrderedDict(sorted(count_ops.items(), key=lambda kv: kv[1], reverse=True))

def num_nonlocal_gates(self) -> int:
"""Return number of non-local gates (i.e. involving 2+ qubits).
def num_nonlocal_gates(self, n: int = 2) -> int:
"""Returns the number of non-local gates (gates with at least n qubits), excluding directives and
including conditional gates. By default, returns the number of non-local gates with at least two
qubits.

Conditional nonlocal gates are also included.
Args:
n (int): The minimum number of qubits in a gate to count towards the returned quantity.
Returns:
int: Number of gates in the quantum circuit with at least n qubits (default n=2).
"""
multi_qubit_gates = 0
for instruction in self._data:
if instruction.operation.num_qubits > 1 and not getattr(
if instruction.operation.num_qubits >= n and not getattr(
instruction.operation, "_directive", False
):
multi_qubit_gates += 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
features:
- |
Added convenience methods to the :class:`.QuantumCircuit` class that return the depth in terms of
gates that have at least two-qubits (:meth:`.QuantumCircuit.depth_2q`) or at least n-qubits (:meth:`.QuantumCircuit.depth_nq`).
Also, convenience methods were added for determining the number of 2-qubit (:meth:`.QuantumCircuit.num_2q_gates`) and n-qubit gates (:meth:`.QuantumCircuit.num_nq_gates`).
Another method was added that returns the number of non-idle qubits in the quantum circuit, i.e. qubits that are part of at least one gate (:meth:`.QuantumCircuit.num_nonidle_qubits`).
Loading