Getting started with qUnits

[1]:
import qrobot
import time

Set up a basic qBrain

First, define a sensorial input:

[2]:
# Layer 0 - Unit 0
l0_unit0 = qrobot.qunits.SensorialUnit("l0_unit_0", Ts=0.1)
[3]:
l0_unit0
[3]:
SensorialUnit "l0_unit_0-214657"
     name:      l0_unit_0
     id:        l0_unit_0-214657
     Ts:        0.1

Then, define a model and the desired bursts:

[4]:
qrobot.models.AngularModel(n=2, tau=10)
[4]:
[model: AngularModel, n: 2, tau: 10]
[5]:
qrobot.bursts.ZeroBurst()
[5]:
<qrobot.bursts.zeroburst.ZeroBurst at 0x7f4e7d348bb0>
[6]:
qrobot.bursts.OneBurst()
[6]:
<qrobot.bursts.oneburst.OneBurst at 0x7f4e7d361100>

You can use objects like those to create a basic qBrain:

dac1052679c440faade21c0fafb412a6

[7]:
from qrobot.models import AngularModel
from qrobot.bursts import ZeroBurst, OneBurst

# Layer 1 - Unit 0
l1_unit0 = qrobot.qunits.QUnit(
    name="l1_unit0",
    model=AngularModel(n=1, tau=10),
    burst=OneBurst(),
    Ts=0.3,
    in_qunits={0: l0_unit0.id},  # Will receive Input from l0_unit0, dim 0
)

# Layer 1 - Unit 1
l1_unit1 = qrobot.qunits.QUnit(
    name="l1_unit1",
    model=AngularModel(n=1, tau=25),
    burst=ZeroBurst(),
    Ts=0.2,
    in_qunits={0: l0_unit0.id},  # Will receive input from l0_unit0, dim 1
)
[8]:
l1_unit0
[8]:
QUnit "l1_unit0-e96c53"
     name:      l1_unit0
     id:        l1_unit0-e96c53
     model:     [model: AngularModel, n: 1, tau: 10]
     burst:     <class 'qrobot.bursts.oneburst.OneBurst'>
     query:     [0.0]
     Ts:        0.3
[9]:
l1_unit1
[9]:
QUnit "l1_unit1-77319d"
     name:      l1_unit1
     id:        l1_unit1-77319d
     model:     [model: AngularModel, n: 1, tau: 25]
     burst:     <class 'qrobot.bursts.zeroburst.ZeroBurst'>
     query:     [0.0]
     Ts:        0.2

Inputs and queries

Check the default input for l0_unit0:

[10]:
l0_unit0.scalar_reading
[10]:
0.0

The input units for each qUnit are:

[11]:
print(l1_unit0.in_qunits)
print(l1_unit1.in_qunits)
{0: 'l0_unit_0-214657'}
{0: 'l0_unit_0-214657'}

Modify l1_unit1 query:

[12]:
l1_unit1.query = 0.8

Real-time processing

[13]:
l0_unit0.start()
l1_unit0.start()
l1_unit1.start()
2023-06-12 20:16:08,194 — l0_unit_0-214657 — INFO — start:72 — Starting SensorialUnit
2023-06-12 20:16:08,206 — l1_unit0-e96c53 — INFO — start:72 — Starting QUnit
2023-06-12 20:16:08,218 — l1_unit1-77319d — INFO — start:72 — Starting QUnit

Visualize the time evolution of the system from the redis status for 30 seconds changing the input with a random input:

[14]:
import time
import json
from random import randint
from IPython.display import clear_output

statuses = []
refresh_time = 0.5  # Read statuses every 0.5 seconds

for i in range(int(30 * (1 / refresh_time))):
    # Wait and then clean the output
    time.sleep(refresh_time)
    clear_output(wait=True)

    # Change input every 2 second
    if (i * refresh_time) % 2 == 0:
        l0_unit0.scalar_reading = randint(0, 1000) / 1000

    # Read statused and store it
    status = qrobot.qunits.redis_utils.redis_status()
    statuses.append(status)

    # Print output
    print(json.dumps(status, indent=1, sort_keys=True))
    print(int(i * refresh_time), "/30 seconds")

    # Plot graph
    qbrain_graph = qrobot.graph(status)
    qrobot.draw(qbrain_graph)
{
 "l0_unit_0-214657 class": "SensorialUnit",
 "l0_unit_0-214657 output": "0.077",
 "l1_unit0-e96c53 class": "QUnit",
 "l1_unit0-e96c53 in_qunits": "{\"0\": \"l0_unit_0-214657\"}",
 "l1_unit0-e96c53 output": "1.0",
 "l1_unit0-e96c53 query": "[0.0]",
 "l1_unit0-e96c53 state": "1",
 "l1_unit1-77319d class": "QUnit",
 "l1_unit1-77319d in_qunits": "{\"0\": \"l0_unit_0-214657\"}",
 "l1_unit1-77319d output": "1.0",
 "l1_unit1-77319d query": "[0.8]",
 "l1_unit1-77319d state": "0"
}
29 /30 seconds

Read manually the latest outputs of the qunits found on the redis database:

[15]:
l1_unit0.get_burst_output()
[15]:
1.0
[16]:
l1_unit1.get_burst_output()
[16]:
1.0

Stop the processing loops:

[17]:
l0_unit0.stop()
l1_unit0.stop()
l1_unit1.stop()
2023-06-12 20:16:40,653 — l0_unit_0-214657 — INFO — stop:83 — Stopping SensorialUnit
2023-06-12 20:16:40,655 — l0_unit_0-214657 — INFO — stop:85 — Cleaning redis
2023-06-12 20:16:40,661 — l1_unit0-e96c53 — INFO — stop:83 — Stopping QUnit
2023-06-12 20:16:40,663 — l1_unit0-e96c53 — INFO — stop:85 — Cleaning redis
2023-06-12 20:16:40,671 — l1_unit1-77319d — INFO — stop:83 — Stopping QUnit
2023-06-12 20:16:40,674 — l1_unit1-77319d — INFO — stop:85 — Cleaning redis

Flush the redis to clean all traces (should not be necessary if the qUnits processing loops stopped correctly):

[18]:
qrobot.qunits.redis_utils.redis_status()
[18]:
{}
[19]:
qrobot.qunits.redis_utils.flush_redis()
2023-06-12 20:16:40,703 — redis — INFO — flush_redis:81 — Flushing redis database

Visualize the results

We can visualize the time evolution of such time period:

[20]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

status_df = pd.DataFrame(statuses)
units = [l0_unit0.id + " output", l1_unit0.id + " output", l1_unit1.id + " output"]
status_df = status_df[units]
status_df = status_df.astype(np.float64)
status_df.index = status_df.index * refresh_time

fig, ax = plt.subplots(1, 1, figsize=(15, 5))
# Plot time evolution
styles = ["g", "y", "b"]
status_df[units].plot(style=styles, ax=ax)
# Plot queries as dashed lines
ax.hlines(
    y=l1_unit0.query, xmin=0, xmax=30, linewidth=1, color="y", linestyles="dashed"
)
ax.hlines(
    y=l1_unit1.query, xmin=0, xmax=30, linewidth=1, color="b", linestyles="dashed"
)
plt.show()
../_images/notebooks_06_qunits_getting_started_35_0.png

Focusing on l1_unit0:

[21]:
print(l1_unit0)
fig, ax = plt.subplots(1, 1, figsize=(15, 5))
# Plot time evolution
units = [l0_unit0.id + " output", l1_unit0.id + " output"]
styles = ["g", "b"]
status_df[units].plot(style=styles, ax=ax)
# Plot time windows
t_start = status_df[l1_unit0.id + " output"].dropna().index[0]
t_step = l1_unit0.model.tau * l1_unit0.Ts
t_windows = np.arange(t_start, 31, t_step)
plt.vlines(x=t_windows, ymin=0, ymax=1, colors="gray", ls="dotted", lw=1)
# Plot query as dashed line
ax.hlines(y=l1_unit0.query, xmin=t_start, xmax=30, linewidth=1, color="b", ls="dashed")
plt.show()
QUnit "l1_unit0-e96c53"
     name:      l1_unit0
     id:        l1_unit0-e96c53
     model:     [model: AngularModel, n: 1, tau: 10]
     burst:     <class 'qrobot.bursts.oneburst.OneBurst'>
     query:     [0.0]
     Ts:        0.3
../_images/notebooks_06_qunits_getting_started_37_1.png

Focusing on l1_unit1:

[22]:
print(l1_unit1)
fig, ax = plt.subplots(1, 1, figsize=(15, 5))
# Plot time evolution
units = [l0_unit0.id + " output", l1_unit1.id + " output"]
styles = ["g", "b"]
status_df[units].plot(style=styles, ax=ax)
# Plot time windows
t_start = status_df[l1_unit1.id + " output"].dropna().index[0]
t_step = l1_unit1.model.tau * l1_unit1.Ts
t_windows = np.arange(t_start, 31, t_step)
plt.vlines(x=t_windows, ymin=0, ymax=1, colors="gray", ls="dotted", lw=1)
# Plot query as dashed line
ax.hlines(y=l1_unit1.query, xmin=t_start, xmax=30, linewidth=1, color="b", ls="dashed")
plt.show()
QUnit "l1_unit1-77319d"
     name:      l1_unit1
     id:        l1_unit1-77319d
     model:     [model: AngularModel, n: 1, tau: 25]
     burst:     <class 'qrobot.bursts.zeroburst.ZeroBurst'>
     query:     [0.8]
     Ts:        0.2
../_images/notebooks_06_qunits_getting_started_39_1.png

Detailed logs

Print the last 30 lines of the log:

[23]:
def print_log(filter_by: "list[str]" = None, n_lines: int = 30):
    log_file = qrobot._logger.log_file()
    i = 0
    with open(log_file) as log:
        for line in log.readlines()[-n_lines:]:
            if filter_by is None or all(x in line for x in filter_by):
                print(line, end="")
                i += 1
            if i >= n_lines:
                break


print_log()
2023-06-12 20:16:40,114 — l0_unit_0-214657 — DEBUG — _unit_task:80 — Writing input on redis
2023-06-12 20:16:40,208 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 4/25
2023-06-12 20:16:40,212 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,220 — l0_unit_0-214657 — DEBUG — _unit_task:79 — scalar_reading=0.077
2023-06-12 20:16:40,220 — l0_unit_0-214657 — DEBUG — _unit_task:80 — Writing input on redis
2023-06-12 20:16:40,325 — l0_unit_0-214657 — DEBUG — _unit_task:79 — scalar_reading=0.077
2023-06-12 20:16:40,325 — l0_unit_0-214657 — DEBUG — _unit_task:80 — Writing input on redis
2023-06-12 20:16:40,329 — l1_unit0-e96c53 — DEBUG — _unit_task:198 — Temporal window event 4/10
2023-06-12 20:16:40,331 — l1_unit0-e96c53 — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,416 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 5/25
2023-06-12 20:16:40,421 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,429 — l0_unit_0-214657 — DEBUG — _unit_task:79 — scalar_reading=0.077
2023-06-12 20:16:40,429 — l0_unit_0-214657 — DEBUG — _unit_task:80 — Writing input on redis
2023-06-12 20:16:40,535 — l0_unit_0-214657 — DEBUG — _unit_task:79 — scalar_reading=0.077
2023-06-12 20:16:40,535 — l0_unit_0-214657 — DEBUG — _unit_task:80 — Writing input on redis
2023-06-12 20:16:40,624 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 6/25
2023-06-12 20:16:40,626 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,633 — l1_unit0-e96c53 — DEBUG — _unit_task:198 — Temporal window event 5/10
2023-06-12 20:16:40,635 — l1_unit0-e96c53 — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,640 — l0_unit_0-214657 — DEBUG — _unit_task:79 — scalar_reading=0.077
2023-06-12 20:16:40,640 — l0_unit_0-214657 — DEBUG — _unit_task:80 — Writing input on redis
2023-06-12 20:16:40,653 — l0_unit_0-214657 — INFO — stop:83 — Stopping SensorialUnit
2023-06-12 20:16:40,655 — l0_unit_0-214657 — INFO — stop:85 — Cleaning redis
2023-06-12 20:16:40,661 — l1_unit0-e96c53 — INFO — stop:83 — Stopping QUnit
2023-06-12 20:16:40,663 — l1_unit0-e96c53 — INFO — stop:85 — Cleaning redis
2023-06-12 20:16:40,671 — l1_unit1-77319d — INFO — stop:83 — Stopping QUnit
2023-06-12 20:16:40,674 — l1_unit1-77319d — INFO — stop:85 — Cleaning redis
2023-06-12 20:16:40,703 — redis — INFO — flush_redis:81 — Flushing redis database
2023-06-12 20:16:40,705 — redis — DEBUG — flush_redis:82 — Previous redis state: {}
2023-06-12 20:16:40,708 — redis — DEBUG — flush_redis:85 — Current redis state: {}

Print the last lines of the log only for l1_unit1:

[24]:
print_log(["l1_unit1"], 200)
2023-06-12 20:16:35,810 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 8/25
2023-06-12 20:16:35,814 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.82]
2023-06-12 20:16:36,016 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 9/25
2023-06-12 20:16:36,017 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.82]
2023-06-12 20:16:36,218 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 10/25
2023-06-12 20:16:36,219 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.82]
2023-06-12 20:16:36,421 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 11/25
2023-06-12 20:16:36,426 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.82]
2023-06-12 20:16:36,630 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 12/25
2023-06-12 20:16:36,633 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.82]
2023-06-12 20:16:36,836 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 13/25
2023-06-12 20:16:36,841 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:37,044 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 14/25
2023-06-12 20:16:37,048 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:37,252 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 15/25
2023-06-12 20:16:37,258 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:37,462 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 16/25
2023-06-12 20:16:37,468 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:37,672 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 17/25
2023-06-12 20:16:37,677 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:37,881 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 18/25
2023-06-12 20:16:37,885 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:38,089 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 19/25
2023-06-12 20:16:38,092 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:38,295 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 20/25
2023-06-12 20:16:38,299 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:38,503 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 21/25
2023-06-12 20:16:38,510 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:38,713 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 22/25
2023-06-12 20:16:38,718 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:38,922 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 23/25
2023-06-12 20:16:38,928 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.608]
2023-06-12 20:16:39,132 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 24/25
2023-06-12 20:16:39,137 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:39,341 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 25/25
2023-06-12 20:16:39,346 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:39,349 — l1_unit1-77319d — DEBUG — _unit_task:212 — Querying for state [0.8]
2023-06-12 20:16:39,376 — l1_unit1-77319d — DEBUG — _unit_task:216 — Output state = 0
2023-06-12 20:16:39,376 — l1_unit1-77319d — DEBUG — _unit_task:218 — Opening a connection to redis...
2023-06-12 20:16:39,377 — l1_unit1-77319d — DEBUG — _unit_task:220 — Redis connected: Redis<ConnectionPool<Connection<host=localhost,port=6379,db=0>>>
2023-06-12 20:16:39,380 — l1_unit1-77319d — DEBUG — _unit_task:231 — Initializing a new temporal window
2023-06-12 20:16:39,582 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 1/25
2023-06-12 20:16:39,589 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:39,793 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 2/25
2023-06-12 20:16:39,797 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,002 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 3/25
2023-06-12 20:16:40,005 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,208 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 4/25
2023-06-12 20:16:40,212 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,416 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 5/25
2023-06-12 20:16:40,421 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,624 — l1_unit1-77319d — DEBUG — _unit_task:198 — Temporal window event 6/25
2023-06-12 20:16:40,626 — l1_unit1-77319d — DEBUG — _unit_task:203 — input_vector=[0.077]
2023-06-12 20:16:40,671 — l1_unit1-77319d — INFO — stop:83 — Stopping QUnit
2023-06-12 20:16:40,674 — l1_unit1-77319d — INFO — stop:85 — Cleaning redis
[25]:
print("Time window time:", l1_unit1.Ts * l1_unit1.model.tau, "seconds")
print_log(["l1_unit1", "Output state = "], 1000)
Time window time: 5.0 seconds
2023-06-12 20:16:18,500 — l1_unit1-77319d — DEBUG — _unit_task:216 — Output state = 0
2023-06-12 20:16:23,725 — l1_unit1-77319d — DEBUG — _unit_task:216 — Output state = 1
2023-06-12 20:16:28,941 — l1_unit1-77319d — DEBUG — _unit_task:216 — Output state = 1
2023-06-12 20:16:34,151 — l1_unit1-77319d — DEBUG — _unit_task:216 — Output state = 0
2023-06-12 20:16:39,376 — l1_unit1-77319d — DEBUG — _unit_task:216 — Output state = 0