For preparing an arbitrary $n$-qubit state with real amplitudes, you can simply:
- Take a circuit for preparing arbitrary states,
- Replace all single-qubits rotations with $R_y$ rotations.
Here is how I do it in Qiskit:
def arbitrary_real_superposition_qiskit( amps: List[ float ], qc_arb = None ):
_amps = np.array( amps )
if not misc.is_power_of_two( _amps.shape[ 0 ] + 1 ):
raise Exception( 'The number of parameters for arbitrary_real_superposition_qiskit() has to be 2^n-1.' )
nqubit = int( np.log2( _amps.shape[ 0 ] + 1 ) )
qc = qiskit.circuit.quantumcircuit.QuantumCircuit( nqubit )
if type( qc_arb ) != qiskit.circuit.quantumcircuit.QuantumCircuit:
qc_arb = init_state_qiskit( [ random.random() for i in range( _amps.shape[ 0 ] ) ] )
i = 0
for inst in qc_arb.data:
if type( inst[ 0 ] ) == qiskit.circuit.library.standard_gates.u3.U3Gate \
or type( inst[ 0 ] ) == qiskit.circuit.library.standard_gates.u2.U2Gate \
or type( inst[ 0 ] ) == qiskit.circuit.library.standard_gates.u1.U1Gate:
qc.ry( _amps[ i ], inst[ 1 ] )
i = i + 1
elif type( inst[ 0 ] ) == qiskit.circuit.library.standard_gates.x.CXGate:
qc.cx( inst[ 1 ][ 0 ], inst[ 1 ][ 1 ] )
else:
raise Exception( 'A gate other than u1/u2/u3 and cx was encountered.' )
return qc
where
def is_power_of_two( N ):
"""Returns True if n is a power of 2."""
n = int(N)
if n <= 0:
return False
else:
return n & (n - 1) == 0 # How the hell is this even working??? :D
and
def init_state_qiskit( amps: List[ complex ] ):
qubitn = int( np.log2( amps.shape[ 0 ] ) )
qc = qiskit.circuit.quantumcircuit.QuantumCircuit( qubitn )
qc.isometry( amps, list( range( qubitn ) ), None )
while True:
qc = qc.decompose()
if qc == qc.decompose():
break
qc = qiskittranspile( qc, optimization_level = 3, basis_gates = [ 'u1', 'u2', 'u3', 'cx' ] )
return qc
What happens here is that I initialize an $n$-qubit state from an arbitrary $2^n$ vector (to make sure that all the $2^n-1$ single-qubit rotations are present in the circuit), and replace those single-qubit rotations with $R_y$'s.