Monodimensional Angular Model

Research provenance

The model introduced here is presented in A Preliminary Study for a Quantum-like Robot Perception Model (2020), and later incorporated into Quantum-like Modeling of Cognitive Architectures for Robotics.

import numpy as np
import matplotlib.pyplot as plt
from qiskit.visualization import plot_histogram
from qrobot.models import AngularModel

In this notebook we present a 1-dimensional (\(n=1\)) demo for the AngularModel class. The model integrates a sequence of normalized sensor events in one qubit and produces a binary outcome when that qubit is measured.

n = 1

Here, we considered a time window of \(\tau > 1\).

tau = 30

For each event \(x_t\in[0,1]\), the model applies a fractional rotation \(R_y(\pi x_t/\tau)\). Since every rotation uses the same axis, the final angle is

\[ \theta=\frac{\pi}{\tau}\sum_t x_t=\pi\bar{x}, \]

where \(\bar{x}\) is the mean input in the temporal window. Consequently, \(P(0)=\cos^2(\theta/2)\) and \(P(1)=\sin^2(\theta/2)\).

Input definition

We start by defining an arbitrary continuous input sequence. Its first half spans the full normalized interval, while the second half is biased toward larger readings:

sequence = list()

# Balanced events (between 0 and 1)
for i in range(0, int(tau / 2)):
    sequence.append(np.random.randint(0, 1000) / 1000)

# Unbalanced events (balanced between .5 and 1)
for i in range(int(tau / 2), tau):
    sequence.append(np.random.randint(500, 1000) / 1000)

plt.figure()
plt.stem(sequence, linefmt="C0-", markerfmt="C0o", basefmt="C0-")
plt.xlabel("Time index t")
plt.ylabel("Input value x")
plt.title("Input sequence")
plt.show()
../_images/465ac924b54f9f6a387a8c423a601219bc2c79d352de04e54566ec91deaac20c.png

Encode the input in the model

We initialize the model by instantiating an object with \(n\) and \(\tau\)

model = AngularModel(n, tau)

Using the encode method, we can encode each event’s data in the model (for multidimensional inputs, a second loop is needed in order to loop through the \(n\) dimensions of the input).

model.clear()  # Keep this cell repeatable by discarding any earlier encoding.

for t in range(0, model.tau):  # loop throug the event sequence
    model.encode(sequence[t], dim=0)

The model is implemented by a Qiskit quantum circuit:

model.print_circuit()
   ┌──────────────┐┌──────────────┐┌──────────────┐┌──────────────┐»
q: ┤ Ry(0.028693) ├┤ Ry(0.086813) ├┤ Ry(0.090164) ├┤ Ry(0.096028) ├»
   └──────────────┘└──────────────┘└──────────────┘└──────────────┘»
«   ┌──────────────┐┌──────────────┐┌────────────┐┌──────────────┐»
«q: ┤ Ry(0.034976) ├┤ Ry(0.077702) ├┤ Ry(0.1042) ├┤ Ry(0.087755) ├»
«   └──────────────┘└──────────────┘└────────────┘└──────────────┘»
«   ┌──────────────┐┌──────────────┐┌──────────────┐┌──────────────┐»
«q: ┤ Ry(0.097808) ├┤ Ry(0.090164) ├┤ Ry(0.090583) ├┤ Ry(0.086184) ├»
«   └──────────────┘└──────────────┘└──────────────┘└──────────────┘»
«   ┌───────────────┐┌───────────────┐┌───────────────┐┌──────────────┐»
«q: ┤ Ry(0.0074351) ├┤ Ry(0.0093201) ├┤ Ry(0.0031416) ├┤ Ry(0.063565) ├»
«   └───────────────┘└───────────────┘└───────────────┘└──────────────┘»
«   ┌──────────────┐┌────────────┐┌─────────────┐┌──────────────┐»
«q: ┤ Ry(0.060737) ├┤ Ry(0.0821) ├┤ Ry(0.06367) ├┤ Ry(0.056339) ├»
«   └──────────────┘└────────────┘└─────────────┘└──────────────┘»
«   ┌──────────────┐┌─────────────┐┌──────────────┐┌──────────────┐»
«q: ┤ Ry(0.087336) ├┤ Ry(0.07833) ├┤ Ry(0.089012) ├┤ Ry(0.057701) ├»
«   └──────────────┘└─────────────┘└──────────────┘└──────────────┘»
«   ┌─────────────┐┌──────────────┐┌──────────────┐┌──────────────┐»
«q: ┤ Ry(0.10409) ├┤ Ry(0.076131) ├┤ Ry(0.072466) ├┤ Ry(0.052883) ├»
«   └─────────────┘└──────────────┘└──────────────┘└──────────────┘»
«   ┌──────────────┐┌──────────────┐
«q: ┤ Ry(0.067858) ├┤ Ry(0.083252) ├
«   └──────────────┘└──────────────┘

Given the input we defined above, the model is in the following state:

model.plot_state_mat()
../_images/1c61f29d0bef51549462d6f6ba2e2d1c95b0d85371b48f5ed92b19ed0e6c4a9a.png

Density matrix (see Wikipedia): for a finite-dimensional state space, the most general density operator is of the form

\[\rho =\sum _{j}p_{j}|\psi _{j}\rangle \langle \psi _{j}|\]

where the coefficients \(p_{j}\) are non-negative and add up to one, and \(|\psi _{j}\rangle \langle \psi _{j}|\) is an outer product written in bra-ket notation. This represents a mixed state, with probability \( p_{j}\) that the system is in the pure state \(|\psi _{j}\rangle \).

Measurement simulation

A single call to model.decode() performs one measurement and can be interpreted as one stochastic decision:

print(f"One-shot decision: |{model.decode()}⟩")
One-shot decision: |0⟩

Now we instead simulate many shots to expose the probability distribution for the two possible basis-state outcomes \(\lvert 0 \rangle\) and \(\lvert 1 \rangle\) and validate the information encoded by the temporal window:

shots = 1000000
counts = model.measure(shots)

Raw counts for each possible outcome:

import json

print("Aggregated binary outcomes of the circuit:")
print(json.dumps(counts, sort_keys=True, indent=4))
Aggregated binary outcomes of the circuit:
{
    "0": 253463,
    "1": 746537
}

From the raw counts we can obtain the relative frequencies (aka the probabilities) and compare them with the input sequence shape:

plt.figure(figsize=(15, 4), dpi=150)

ax1 = plt.subplot(1, 2, 1)
ax1.stem(sequence, linefmt="C0-", markerfmt="C0o", basefmt="C0-")
ax1.set_xlabel("Time index t")
ax1.set_ylabel("Input value x")
ax1.set_title("Input sequence")

ax2 = plt.subplot(1, 2, 2)
plot_histogram(counts, ax=ax2)
ax2.set_ylabel("")
ax2.set_title("Probabilities")

plt.show()
../_images/58b6f7d555b1e479bf7fa064486bb9722739d47bd78a74ddc0029eb1fafa1b70.png

References