Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
eb7fad3
fastplotlib demo
Aug 28, 2023
1187d49
update README
Aug 28, 2023
5833bc4
pinning sphinx-autoapi
jmxpearson Dec 29, 2023
673afd9
work from docs branch for now
jmxpearson Dec 29, 2023
4b936f6
downgrading astroid
jmxpearson Dec 29, 2023
cae5d0a
on push to main
jmxpearson Dec 29, 2023
24f8d07
Merge branch 'main' into docs
jmxpearson Dec 29, 2023
b9915cb
adding custom css
jmxpearson Dec 30, 2023
f78d5e4
stubbing out docs pages
jmxpearson Dec 30, 2023
2f73cd4
adding install docs
jmxpearson Dec 30, 2023
c76ea1c
adding bibliography
jmxpearson Dec 30, 2023
1f2156f
adding page on running improv
jmxpearson Dec 31, 2023
342a8cc
Merge branch 'main' into docs
jmxpearson Dec 31, 2023
5635407
updating install docs
jmxpearson Jan 1, 2024
88c99f1
refining docs
jmxpearson Jan 1, 2024
cd96f8c
starting design doc
jmxpearson Jan 1, 2024
8d6db00
lots of edits to design doc
jmxpearson Jan 1, 2024
04d6501
adding readme badges
jmxpearson Jan 1, 2024
f0c5e56
basic version of writing actors
jmxpearson Jan 1, 2024
2505a8d
small typo
jmxpearson Jan 1, 2024
6b86a95
adding `run` caveat
jmxpearson Jan 5, 2024
60bc444
restrict pypi actions to main, dev
jmxpearson Jan 5, 2024
5c30c05
Merge pull request #165 from project-improv/dev
draelos Jan 7, 2024
7b4e145
update fpl demo as a sample_actor
Jan 8, 2024
c1700b7
fix visual actor
Jan 8, 2024
1f8ef71
Merge branch 'dev' of https://github.com/project-improv/improv into f…
Jan 8, 2024
b31103b
add requirement.txt, check for gpu, close zmq socket on stop signal
Jan 8, 2024
45dffbb
update GPU check and requirements.txt
Jan 9, 2024
b9f9f75
more figure stuff and improving README
jmxpearson Jan 10, 2024
cef1597
small spacing tweak
jmxpearson Jan 10, 2024
935a556
trying local github file
jmxpearson Jan 10, 2024
f467435
reverting
jmxpearson Jan 10, 2024
148de2d
updating bubblewrap readme
jmxpearson Jan 10, 2024
bdb627a
drafting signals
jmxpearson Jan 10, 2024
a3ee0ac
move readme and requirements.txt, update instructions
Jan 10, 2024
636d046
tweak to table
jmxpearson Jan 12, 2024
c0d9145
minor edits
jmxpearson Jan 15, 2024
0626a77
Merge pull request #171 from project-improv/docs
draelos Jan 15, 2024
4ba6cd4
try install without no-binary
jmxpearson Jan 18, 2024
34d3d81
Merge tag 'main' into dev
jmxpearson Jan 18, 2024
558b0ba
remove other pyzmq build
jmxpearson Jan 25, 2024
0144940
Add Support for Redis as Datastore (#168)
rwschonberg Jul 29, 2024
2579cdd
fixing broken image links in readme
jmxpearson Jul 29, 2024
770378e
Updating minimal spawn demo to work with Redis store (#185)
draelos Aug 20, 2024
89fd730
Pass store port correctly to spawned actors
Aug 30, 2024
bcdb61f
Merge pull request #186 from rwschonberg/redis-port-fix
draelos Sep 3, 2024
b510afe
fastplotlib demo
Aug 28, 2023
0661970
update README
Aug 28, 2023
14364dc
update fpl demo as a sample_actor
Jan 8, 2024
0fe970c
fix visual actor
Jan 8, 2024
e0f7af0
add requirement.txt, check for gpu, close zmq socket on stop signal
Jan 8, 2024
1a1b3bb
update GPU check and requirements.txt
Jan 9, 2024
7f98429
move readme and requirements.txt, update instructions
Jan 10, 2024
4c92800
only run CI on non-draft PR
clewis7 Nov 21, 2024
0ec9148
rebase with current dev, start updating
clewis7 Nov 21, 2024
ebaa664
merge conflict resolve
clewis7 Nov 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,49 @@ on:
branches:
- main
- dev
- redis
push:
branches:
- main
- dev
- redis

jobs:
test:

runs-on: ${{matrix.os}}
if: ${{ !github.event.pull_request.draft }}
strategy:
max-parallel: 1
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
python-version: ["3.8", "3.9", "3.10"]
os: [ubuntu-latest, macos-latest] # [ubuntu-latest, macos-latest, windows-latest]

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Update Ubuntu and Install Dependencies
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y lsb-release curl gpg
- name: Fetch Redis gpg (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
- name: Configure Redis gpg (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
- name: Install Redis (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get install -y redis && sleep 5 && sudo systemctl stop redis-server
- name: Install Redis (macOS)
if: startsWith(matrix.os, 'macos')
run: |
brew install redis
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
Expand All @@ -34,7 +58,7 @@ jobs:
- name: Install package (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: |
pip install -e .[tests,lint] --no-binary pyzmq
pip install -e .[tests,lint]
- name: Install package (Mac)
if: startsWith(matrix.os, 'macos')
run: |
Expand Down Expand Up @@ -64,7 +88,7 @@ jobs:

- name: Install package (Ubuntu)
run: |
pip install -e .[lint] --no-binary pyzmq
pip install -e .[lint]

- name: Check format with Black
uses: psf/black@stable
Expand All @@ -81,4 +105,4 @@ jobs:
- name: Close parallel build
uses: coverallsapp/github-action@v1
with:
parallel-finished: true
parallel-finished: true
File renamed without changes.
2 changes: 2 additions & 0 deletions .github/workflows/pypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
branches:
- 'main'

jobs:
build:
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/test-pypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
# based on https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
name: Publish Python 🐍 distribution 📦 to TestPyPI

on: push
on:
push:
branches:
- 'main'
- 'dev'

jobs:
build:
Expand Down
6 changes: 0 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,6 @@ dmypy.json
# Pyre type checker
.pyre/

# LMDB
*.mdb
*.xml
*.hdf5
*.iml

*.tif
arrow

Expand Down
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
# improv
[![PyPI](https://img.shields.io/pypi/v/improv?style=flat-square?style=flat-square)](https://pypi.org/project/improv)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/improv?style=flat-square)](https://pypi.org/project/improv)
[![docs](https://github.com/project-improv/improv/actions/workflows/docs.yaml/badge.svg?style=flat-square)](https://project-improv.github.io/)
[![tests](https://github.com/project-improv/improv/actions/workflows/CI.yaml/badge.svg?style=flat-square)](https://project-improv.github.io/)
[![Coverage Status](https://coveralls.io/repos/github/project-improv/improv/badge.svg?branch=main)](https://coveralls.io/github/project-improv/improv?branch=main)
[![PyPI - License](https://img.shields.io/pypi/l/improv?style=flat-square)](https://opensource.org/licenses/MIT)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square)](https://github.com/psf/black)

A flexible software platform for real-time and adaptive neuroscience experiments.

improv is a streaming software platform designed to enable adaptive experiments. By analyzing data, such as 2-photon calcium images, as it comes in, we can obtain information about the current brain state in real time and use it to adaptively modify an experiment as data collection is ongoing.
_improv_ is a streaming software platform designed to enable adaptive experiments. By analyzing data, such as 2-photon calcium images, as it comes in, we can obtain information about the current brain state in real time and use it to adaptively modify an experiment as data collection is ongoing.

![](https://dibs-web01.vm.duke.edu/pearson/assets/videos/zebrafish/improvGif.gif)
![](https://dibs.duke.edu/sites/default/files/improvGif.gif)

This video shows raw 2-photon calcium imaging data in zebrafish, with cells detected in real time by [CaImAn](https://github.com/flatironinstitute/CaImAn), and directional tuning curves (shown as colored neurons) and functional connectivity (lines) estimated online, during a live experiment. Here only a few minutes of data have been acquired, and neurons are colored by their strongest response to visual simuli shown so far.
We also provide up-to-the-moment estimates of the functional connectivity by fitting linear-nonlinear-Poisson models online, as each new piece of data is acquired. Simple visualizations offer real-time insights, allowing for adaptive experiments that change in response to the current state of the brain.


### How improv works

<img src="figures/improv_design.png" width=85%>
<img src="https://dibs.duke.edu/sites/default/files/improv_design.png" width=85%>

improv allows users to flexibly specify and manage adaptive experiments to integrate data collection, preprocessing, visualization, and user-defined analytics. All kinds of behavioral, neural, or modeling data can be incorporated, and input and output data streams are managed independently and asynchronously. With this design, streaming analyses and real-time interventions can be easily integrated into various experimental setups. improv manages the backend engineering of data flow and task execution for all steps in an experimental pipeline in real time, without requiring user oversight. Users need only define their particular processing pipeline with simple text files and are free to define their own streaming analyses via Python classes, allowing for rapid prototyping of adaptive experiments.
<br />
<br />

<img src="https://dibs-web01.vm.duke.edu/pearson/assets/images/zebrafish/actor_model.png" width=60%>
<img src="https://dibs.duke.edu/sites/default/files/actor_model.png" width=60%>

improv's design is based on a steamlined version of the actor model for concurrent computation. Each component of the system (experimental pipeline) is considered an 'actor' and has a unique role. They interact via message passing, without the need for a central broker. Actors are implemented as user-defined classes that inherit from improv's Actor class, which supplies all queues for message passing and orchestrates process execution and error handling. Messages between actors are composed of keys that correspond to items in a shared, in-memory data store. This both minimizes communication overhead and data copying between processes.
_improv_'s design is based on a streamlined version of the actor model for concurrent computation. Each component of the system (experimental pipeline) is considered an 'actor' and has a unique role. They interact via message passing, without the need for a central broker. Actors are implemented as user-defined classes that inherit from _improv_'s `Actor` class, which supplies all queues for message passing and orchestrates process execution and error handling. Messages between actors are composed of keys that correspond to items in a shared, in-memory data store. This both minimizes communication overhead and data copying between processes.



## Installation

For installation instructions, please consult the [wiki](https://github.com/project-improv/improv/wiki/Installation) on our github.
For installation instructions, please consult the [docs](https://project-improv.github.io/improv/installation.html) on our github.

### Contact
To get in touch, feel free to reach out on Twitter <a href="http://twitter.com/annedraelos" target="_blank">@annedraelos</a>.
To get in touch, feel free to reach out on Twitter <a href="http://twitter.com/annedraelos" target="_blank">@annedraelos</a> or <a href="http://twitter.com/jmxpearson" target="_blank">@jmxpearson</a>.
2 changes: 1 addition & 1 deletion demos/bubblewrap/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Running Bubblewrap demo
## Ubuntu users
After installing improv, install additional dependencies (JAX on CPU by default) navigating to `demos/bubblewrap` and doing
After installing _improv_, install additional dependencies (JAX on CPU by default) navigating to `demos/bubblewrap` and doing

- `pip install -r requirements.txt`

Expand Down
67 changes: 67 additions & 0 deletions demos/minimal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# minimal demo

This folder contains a minimal demo for running improv. In this demo, data generated by the **generator** `actor` is
stored in a data store and a key to the generated data is sent via a queue to the **processor** `actor` that accesses
the data in the store and processes it.

Usage:

```bash
# cd to this dir
cd .../improv/demos/minimal

# start improv
improv run ./minimal.yaml

# call `setup` in the improv TUI
setup

# call `run` in the improv TUI
run

# when you are ready to stop the process, call `stop` in the improv TUI
stop
```

## Visualization using `fastplotlib`

You can also run the minimal demo and visualize the generated data using `fastplotlib`.

As before, data is generated by the **generator** `actor` is stored in a data store and a key to the generated data is
sent via a queue to the **processor** `actor` that accesses the data in the store and processes it. Additionally, the
`fastplotlib.ipynb` notebook then receives the most recent data via `zmq` and displays it using
[`fastplotlib`](https://github.com/fastplotlib/fastplotlib).

### Instructions

1. Swap the **processor** `actor` found in `/improv/demos/sample_actors/visual/sample_processor.py` for the one in
the minimal demo

2. Run `pip install -r requirements.txt` in this directory.

Usage:

```bash
# cd to this dir
cd .../improv/demos/minimal

# start improv
improv run ./minimal.yaml

# call `setup` in the improv TUI
setup

# Run the cells in the jupyter notebook until you receive
# a plot that has a white cosine wave

# once the plot is ready call `run` in the improv TUI
run

# You should see the plot updating between a cosine and sine wave with different colors depending on
# whether the frame number is even or odd

# when you are ready to stop the process, call `stop` in the improv TUI
stop
```

#### Note: The `fastplotlib.ipynb` can only be run in `jupyter lab`
20 changes: 13 additions & 7 deletions demos/minimal/actors/sample_generator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from improv.actor import Actor, RunManager
from datetime import date # used for saving
from improv.actor import Actor
import numpy as np
import logging

Expand Down Expand Up @@ -50,13 +49,20 @@ def runStep(self):
"""

if self.frame_num < np.shape(self.data)[0]:
data_id = self.client.put(
self.data[self.frame_num], str(f"Gen_raw: {self.frame_num}")
)
if self.store_loc:
data_id = self.client.put(
self.data[self.frame_num], str(f"Gen_raw: {self.frame_num}")
)
else:
data_id = self.client.put(self.data[self.frame_num])
# logger.info('Put data in store')
try:
self.q_out.put([[data_id, str(self.frame_num)]])
logger.info("Sent message on")
if self.store_loc:
self.q_out.put([[data_id, str(self.frame_num)]])
else:
self.q_out.put(data_id)
# logger.info("Sent message on")

self.frame_num += 1
except Exception as e:
logger.error(
Expand Down
18 changes: 11 additions & 7 deletions demos/minimal/actors/sample_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,23 @@ def runStep(self):

frame = None
try:
frame = self.q_in.get(timeout=0.001)
frame = self.q_in.get(timeout=0.05)

except:
except Exception:
logger.error("Could not get frame!")
pass

if frame is not None and self.frame_num is not None:
self.done = False
self.frame = self.client.getID(frame[0][0])
if self.store_loc:
self.frame = self.client.getID(frame[0][0])
else:
self.frame = self.client.get(frame)
avg = np.mean(self.frame[0])

# print(f"Average: {avg}")
logger.info(f"Average: {avg}")
self.avg_list.append(avg)
# print(f"Overall Average: {np.mean(self.avg_list)}")
# print(f"Frame number: {self.frame_num}")
self.frame_num += 1
logger.info(f"Overall Average: {np.mean(self.avg_list)}")
logger.info(f"Frame number: {self.frame_num}")

self.frame_num += 1
28 changes: 11 additions & 17 deletions demos/minimal/actors/sample_spawn_processor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from improv.actor import Actor, RunManager
from improv.actor import Actor
import numpy as np
from queue import Empty
import logging
Expand Down Expand Up @@ -39,17 +39,6 @@ def stop(self):
logger.info("Processor stopping")
return 0

# def run(self):
# """ Send array into the store.
# """
# self.fcns = {}
# self.fcns['setup'] = self.setup
# self.fcns['run'] = self.runStep
# self.fcns['stop'] = self.stop

# with RunManager(self.name, self.fcns, self.links) as rm:
# logger.info(rm)

def runStep(self):
"""Gets from the input queue and calculates the average.

Expand All @@ -63,16 +52,21 @@ def runStep(self):
frame = self.q_in.get(timeout=0.05)
except Empty:
pass
except:
except Exception:
logger.error("Could not get frame!")
pass

if frame is not None and self.frame_num is not None:
self.done = False
self.frame = self.client.getID(frame[0][0])
if self.store_loc:
self.frame = self.client.getID(frame[0][0])
else:
self.frame = self.client.get(frame)
avg = np.mean(self.frame[0])
print(f"Average: {avg}")

logger.info(f"Average: {avg}")
self.avg_list.append(avg)
print(f"Overall Average: {np.mean(self.avg_list)}")
print(f"Frame number: {self.frame_num}")
logger.info(f"Overall Average: {np.mean(self.avg_list)}")
logger.info(f"Frame number: {self.frame_num}")

self.frame_num += 1
5 changes: 4 additions & 1 deletion demos/minimal/minimal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ actors:
class: Processor

connections:
Generator.q_out: [Processor.q_in]
Generator.q_out: [Processor.q_in]

redis_config:
port: 6379
13 changes: 13 additions & 0 deletions demos/minimal/minimal_plasma.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
actors:
Generator:
package: actors.sample_generator
class: Generator

Processor:
package: actors.sample_processor
class: Processor

connections:
Generator.q_out: [Processor.q_in]

plasma_config:
3 changes: 3 additions & 0 deletions demos/minimal/minimal_spawn.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ actors:

connections:
Generator.q_out: [Processor.q_in]

redis_config:
port: 6378
2 changes: 2 additions & 0 deletions demos/minimal/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fastplotlib[notebook]
simplejpeg
4 changes: 2 additions & 2 deletions demos/sample_actors/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
class MeanAnalysis(Actor):
# TODO: Add additional error handling
# TODO: this is too complex for a sample actor?
def __init__(self, *args):
super().__init__(*args)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def setup(self, param_file=None):
"""Set custom parameters here
Expand Down
Loading