Here's an update for those interested (& before I had read Michael's comment)

I changed my problem around a bit, after discovering TFQ/cirq cannot (yet) handle serialising complex power-raised gates. Also, it was getting too complex using a `ControlledPQC`

to have non-trainable values (`phi`

) and trainable values `alpha`

and `beta`

.. I think it's doable but it really does get tricky.

Anyway, here is the modified problem and solution I found adapting TensorFlow Quantum's examples ....

I now have this circuit:

```
# define two qubits
q0 = cirq.GridQubit(0,0)
q1 = cirq.GridQubit(0,1)
# make a PauliSum gate that will return [1] if the state is a bell state b00 (|00>)
z0 = cirq.Z(q0)
z1 = cirq.Z(q1)
z00 = (((1+z0)/2)*((1+z1)/2) )
# define three parameters, x will be varied - a,b are fixed
phi = sp.Symbol('phi')
alpha = sp.Symbol('alpha')
beta = sp.Symbol('beta')
circuit = cirq.Circuit([
cirq.rx(phi)(q0),
cirq.ry(-2*phi)(q1),
cirq.H(q0),
cirq.CNOT(q0,q1),
cirq.rx(alpha)(q0),
cirq.ry(beta)(q1),
])
```

which looks like:

I can calculate the z00 expectation which is going to give me the same distribution as would running measurements on the state $|00\rangle$ (obviously)

```
#fix two values of alpha and beta
a = 1.8
b = -3
#loop over phi from -6 to 6
x_values = np.arange(-6,6,0.05)
y_expectations = []
for j in x_values:
#resolve the circuit for different values of phi
resolver = cirq.ParamResolver({'alpha':a,'beta':b,'phi':j})
#calcuclate the expectation value
result = cirq.Simulator().simulate(cirq.resolve_parameters(circuit,resolver))
e = z00.expectation_from_state_vector(result.final_state_vector,{q0:0,q1:1})
y_expectations.append(e)
plt.scatter(x_values,y_expectations,label=f'Expected, $\\alpha$={a}, $\\beta$={b}')
plt.xlabel('$\phi$')
plt.ylabel('Expectation')
plt.legend()
plt.show();
```

Gives the following:

I can split the circuit into two parts:

- Input qubits with rotations by the angle
`\phi`

```
input_circuit = cirq.Circuit([
cirq.rx(phi)(q0),
cirq.ry(-2*phi)(q1)
])
>>> input_circuit
(0, 0): ───Rx(phi)──────
(0, 1): ───Ry(-2*phi)───
```

- The circuit that is going to be my model - aka given some inputs (rotated qubits) these will be entangled and rotated by fixed values that I want to learn.

```
model_circuit = cirq.Circuit([
cirq.H(q0),
cirq.CNOT(q0,q1),
cirq.rx(alpha)(q0),
cirq.ry(beta)(q1),
])
>>> model_circuit
(0, 0): ───H───@───Rx(alpha)───
│
(0, 1): ───────X───Ry(beta)────
```

Using `tfq`

I can make a really simple model based on the `model_circuit`

:

```
#building a keras model
#- the input is the input_circuit
#- the PQC outputs the expectation to be in state |00> from the model circuit, given the input circuit
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=(), dtype=tf.string),
tfq.layers.PQC(model_circuit, z00),
])
```

Also, need to convert the input circuits (one circuit per value of phi) into tensors:

```
#generate a series of input circuits, with different x values (values of phi)
input_circuits = tfq.convert_to_tensor([
cirq.resolve_parameters(input_circuit, cirq.ParamResolver({'phi':x}))
] for x in x_values)
```

I can test the model (randomly initialised values of `alpha`

and `beta`

given my input circuits:

```
#test the model
y_test = model([input_circuits]).numpy()
#plot how a randomly initiated model compares with the expected
plt.plot(x_values,y_expected,lw=4,label=f'Expected, $\\alpha$={a}, $\\beta$={b}')
plt.plot(x_values,y_test,'ro',label='Pre-trained NN output')
plt.xlabel('$\phi$')
plt.ylabel('Expectation')
plt.legend()
plt.show();
```

Next is to compile the model, using a basic optimiser and loss, e.g.:

```
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
loss = tf.keras.losses.MeanSquaredError()
model.compile(optimizer=optimizer, loss=loss)
model.summary()
```

Then the model can be fitted given now the `y_expected`

from the original expectations (this could easily be the original 'measurements' distribution instead)

```
y_expected = np.array(y_expectations)
history = model.fit(
input_circuits, y_expected,
epochs=20,
verbose=0)
```

After the model has been trained, I can make the predictions again and see how it's easily learnt what the parameters were:

I hope this helps someone - it's not quite the answer to my problem and I would like to not have to split up the circuit into an input and model. Ultimately this was just me having a bit of fun and I don't think is very realistic anyway!

I don't know about the machine learning side, but what does the fourier transform of your data look like? – DaftWullie – 2020-11-12T13:10:35.553

@DaftWullie I could potentially plot that - why are you interested in seeing it? This is just an arbitrary problem, im not particular interested in actually finding an actual solution - if that makes sense. cheers – Calum Macdonald – 2020-11-12T13:54:16.627

I suppose, for me, I want to know what I can understand about a system first, before I go to some relatively black-box method that just spits out an answer but might not help my understanding. I'm guessing here that the values you get out from the Fourier transform would tell you a lot about the different angles used in the system, even if it doesn't immediately help you identify which is which. – DaftWullie – 2020-11-12T14:24:04.657

Fair enough, and thanks for thinking about it! Yeah, thought that might be why you asked. I attempted to make a FT but it came out crazy - a problem on my side - I think it'd be very possible to do it to infer some information.

My question was more of a technical one, inspired by this: https://www.tensorflow.org/quantum/tutorials/hello_many_worlds#2_hybrid_quantum-classical_optimization but instead of having three parameters acting on one qubit, i'm trying to learn how three parameters act of 2 entangled qubits to give an interference - which I could get from the expectation values