QTensor
QTensor copied to clipboard
Alternative initial QAOA state.
In place of the standard all |+> state (created by layer_of_Hadamards), one may also want to supply a different initial state. Similar in structure, I am interested in creating separable state, given by the code below.
def warm_start(self, G):
X_rotations = [ self.solution[i][1] for i in range(len(self.solution)) ]
Z_rotations = [ self.solution[i][0] for i in range(len(self.solution)) ]
for (q,Xrot,Zrot) in zip(self.qubits, X_rotations, Z_rotations):
self.apply_gate(self.operators.rx, [Xrot], q)
self.apply_gate(self.operators.rz, [Zrot+np.pi/2], q)
This is contained within CircuitComposer and is called the same way as layer_of_Hadamards. In this definitions case, 'solution' is supplied as a list in the form of [ [z_1,x_1],[z_2,_x_2]... ] to WeightedQAOASimulator. However, when using this initial state, the resulting energy of this initial state is not as expected. In this case I am using WeightedQAOASimulator but this particular example is an unweighted graph. Additionally, for the "standard" initial state, all cases match within numerical precision, so it's likely some issue with this different initial state.
For an example:
Qtensor Energy
rotated =[[4.872435284588963, 3.140924329249939],
[6.2089546789130585, 0.0],
[0.44075316687891064, 1.0512066194209708],
[3.581626554108839, 1.046481705810185],
[3.580582956190857, 2.0938670181912022],
[0.441558317709732, 2.098012455304285]]
qt_sim=WeightedQAOASimulator(WeightedQAOAComposer,solution=rotated)
qt_sim.energy_expectation(G, gamma=[0], beta=[0])[0]
This returns an energy of 5.624767271462993
Qiskit and Cirq
from qiskit.circuit import QuantumCircuit
from qiskit import execute
from qiskit import Aer
from qiskit.optimization.ising import max_cut, tsp,common
import cirq
##Cirq construction
X_rotations = [ rotated[i][1] for i in range(len(rotated)) ]
Z_rotations = [ rotated[i][0] for i in range(len(rotated)) ]
cirq1=cirq.Circuit()
for (q,Xrot,Zrot) in zip(cirq.LineQubit.range(len(rotated)),X_rotations,Z_rotations):
cirq1.append(cirq.rx(Xrot).on(q))
cirq1.append(cirq.rz(Zrot+np.pi/2).on(q))
s=cirq.Simulator()
sim=s.simulate(cirq1)
##Qiskit construction
circuit = QuantumCircuit(G.number_of_nodes())
for (q,Xrot,Zrot) in zip(range(G.number_of_nodes()-1,-1,-1),X_rotations,Z_rotations):
circuit.rx(Xrot,q)
circuit.rz(Zrot+np.pi/2,q)
ex1 = execute(circuit,backend=Aer.get_backend("statevector_simulator")).result()
warm_state = ex1.get_statevector()
w = np.zeros([n,n])
for i in range(n):
for j in range(n):
temp = G.get_edge_data(i,j,default=0)
if temp != 0:
w[i,j] = temp['weight']
qubitOp, offset = max_cut.get_operator(w)
print(-(qubitOp.evaluate_with_statevector(warm_state)[0].real+offset))
print(-(qubitOp.evaluate_with_statevector(sim.final_state_vector)[0].real+offset))
Both qiskit and cirq in this case return an energy of ~4.998846035923315 . Note that the qiskit circuit is "flipped" since qiskit defines the ordering of qubits differently than cirq.
Hi, thanks for submitting the issue
The code for warm_start looks good, but did you check it actually changes anything? The solution=rotated will not end up in the constructor of the composer, see here: https://github.com/danlkv/QTensor/blob/dev/qtensor/QAOASimulator.py#L21 I'll try to add support for custom arguments in composer. Before that, you can just use something like
zz_exp = 0
for edge in G.edges:
comp = MyCustomComposer(solution=rotated)
comp.energy_expectation_lightcone(edge)
zz_exp += sim.simualte(comp.circuit)
full_exp = 05*(G.number_of_edges - zz_exp)
Can you check what's the energy for |+> initial state with same gamma=beta=0? My guess is that it'll be 5.6247672
Yes, it does supply the proper circuit when using the alternative initial state. I didn't list it in the above, but I also modified to various parts so that WeightedQAOASimulator(WeightedQAOAComposer,solution=rotated) forwards the relevant information to the new circuit.
With the standard initial state WeightedQAOASimulator(WeightedQAOAComposer,solution=None), the energy is 4.5 . This energy is consistent for QTensor, qiskit and cirq.
I checked probability amplitudes and they are equal up to a phase shift https://github.com/danlkv/QTensor/blob/warm_start/qtensor/tests/test_warm_start.py. There is a bug is in lightcone optimization, it treats all qubits as equal, which is not the case for the warm start state. I added a monkey patch in the warm_start branch, but a proper solution will take a week or two. See the test file linked above for usage example.
Note that you can obtain amplitudes from qtensor using state_vec = sim.simulate_batch(comp.circuit, batch_vars=n_qubits), and then pass it to qubitOp.evaluate_with_statevector , but it would be very inefficient.
Great! I agree that the two constructions agree in this regard.
Additionally, I recently looked at the exact p=1 analytic result and can also confirm that both the qiskit and qtensor codes faithfully generate this result as well.