Closed alexanderivrii closed 10 months ago
I have assigned myself to the issue. However, any thoughts on
(1) whether supporting general floating-point powers for Operator
class should or should not be allowed, and
(2) whether a one-line method to raise a complex-valued matrix to a floating point already exists (and which would probably be equivalent to the code in gate.py
anyway)
are welcome.
The need of nth root of a operator may sound convincing if supporting general floating powers is in question
A one liner does exists in scipy => scipy.linalg.fractional_matrix_power(A, t)
where A is a square matrix of order n and t is a fractional power, but a implementation can be made for more robustness
Yes I think it can be supported. We just have to use the same code that is used in the Gate class now, which does in fact raise a matrix to a power using scipy.linalg.schur
and the output type is currently a UnitaryGate. I prefer natively supporting an Operator in the circuit rather than UnitaryGate (which seems like a duplication of Operator from when we could not put Operator in the circuit). In that sense I would prefer the raising to a power to return an Operator, and the code to do this to reside in the Operator.
Also I think the "control" method should also work for operators. It amounts to increasing the dimension and making the new matrix a direct sum of Identity and U (if control is the lower qubit according to qiskit's little endian convention, otherwise a permutation of this matrix).
Fractional-power Operator
seems completely reasonable.
To speak to Ali's other point: I think in the higher-abstraction-level world QuantumCircuit
lives in now, there is still a need for both Operator
and UnitaryGate
. Operator
would represent an abstract high-level quantum operator, where UnitaryGate
would be a low-level primitive unitary
for backends that support it (e.g. Aer). qiskit.quantum_info
would exclusively deal with Operator
, which would be then be lowered by high-level gate decompositions to the target ISA, either by full synthesis (a place we currently use UnitaryGate
as a temporary, but Operator
could/should replace it), or by the trivial synthesis Operator -> UnitaryGate
if the backend ISA includes the variadic unitary
instruction (as Aer does).
The Instruction
interface (which UnitaryGate
satisfies and Operator
doesn't) is more constraining than the Operation
interface (which Operator
does satisfy), and I think it's best to keep both concepts of low- and high-level descriptions, so that backends, and consequently the Target
ISA descriptor, etc, need only deal with the low level.
(As a very much side note, can we stop referring to an "endianness" convention? It's at best misleading terminology, and it's not strictly related to the concepts here; the convention for "which qubit is the control?" is a separate question to "in which order do we perform the Kronecker product?" and "in which order do we label qubits?", and none of those are strictly about endianness.)
I think all I'm saying for the Operator vs. UnitaryGate thing is that we should make it similar to how Cliffords are done now:
>>>c = QuantumCircuit(2)
>>>c.append(random_clifford(2), [0, 1])
>>>c.append(random_unitary(4), [0, 1])
>>>print(type(c[0]))
>>>print(type(c[1]))
qiskit.quantum_info.operators.symplectic.clifford.Clifford
qiskit.circuit.library.generalized_gates.unitary.UnitaryGate
The first one just uses Clifford, which is far better in my opinion as it gives access to the machinery we have around analyzing Cliffords and doesn't create unnecessary classes. The second one converts Operator -> UnitaryGate
under the hood, which is unnecessary IMHO. If we allow CircuitInstructions to point to some quantum_info object, then we immediately allow very general mathematical oeprators to be dropped into circuits, and then teach the transpiler, simulators, etc. to deal with them. The fact that Aer knows about UnitaryGate right now isn't fundamental, it can be adapted to work with CircuitInstruction(operation=Operator(...), ...)
Ok we can call it something other than endianness :) I was trying to say that U.control()
should become I \otimes |0><0| + U \otimes |1><1|
which is counter-intuitive but makes sense if we consider the original operation as U(t1, t2, t3)
and the new one as controlled-U(c, t1, t2, t3)
, i.e. the 0th qubit is the control, i.e. the least-significant-bit. This is a combination of kronecker product ordering and how we order the qargs of an instruction.
Oh yeah, I totally agree with all that - these to me are examples of a user interacting with the objects in an abstract mathematical way, so it's preferable that they stay as quantum_info
objects.
Operator
doesn't have a control
method yet, so we can give it whatever semantics we like when we add it, right? I totally agree that we should cause Operator.control
and UnitaryGate.control
to produce the same control-qubit ordering as each other in their matrix forms - it'd be super confusing to get them reversed.
Supporting Operator.control
would also be very easy, since we already have the implementation in Qiskit:
_compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=None)
incircuit/utils.py
(and used in UnitaryGate.control
). I will add this to #11534.
Update: though let me first make sure that everyone agrees that the method Operator.control(num_ctrl_qubits, ctrl_state=None)
does make sense as a part of Operator
class.
Environment
What is happening?
In the description of the
Operator.power(n)
method: https://github.com/Qiskit/qiskit/blob/4b305983465be7429129d5f89287ae7dc7b91952/qiskit/quantum_info/operators/operator.py#L511-L523the argument
power
is of typefloat
, however in practice this code only works for integer values ofn
.How can we reproduce the issue?
What should happen?
The
power
method ingate.py
:https://github.com/Qiskit/qiskit/blob/4b305983465be7429129d5f89287ae7dc7b91952/qiskit/circuit/gate.py#L64-L75
does work with floating-point powers and (as far as I can judge) could be moved to the
Operator
class.Any suggestions?
No response