Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
551 changes: 526 additions & 25 deletions pdm.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ dependencies = [
"line-profiler>=4.1.3",
"scikit-image>=0.25.2",
"numba>=0.61.2",
"noob @ git+https://github.com/miniscope/noob.git@nobes-web#subdirectory=packages/noob"
]
requires-python = ">=3.11,<3.13"
readme = "README.md"
license = { text = "GPL-3.0" }

[project.entry-points."noob.add_sources"]
tubes = "indeca.config:tube_path"

[tool.pdm]
distribution = true
Expand Down
8 changes: 8 additions & 0 deletions src/indeca/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pathlib import Path


def tube_path() -> Path:
"""
Tell noob where our tubes are using the `noob.add_sources` entrypoint
"""
return Path(__file__).parent / "tubes"
2 changes: 1 addition & 1 deletion src/indeca/core/AR_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def convolve_g(s, g):
return np.array(Gi @ s.reshape((-1, 1))).squeeze()


def convolve_h(s, h):
def convolve_h(s: np.ndarray, h: np.ndarray) -> np.ndarray:
T = len(s)
H0 = h.reshape((-1, 1))
H1n = [
Expand Down
15 changes: 15 additions & 0 deletions src/indeca/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from pathlib import Path
import numpy as np
from pydantic import ConfigDict

from noob.asset import Asset


class NPYAsset(Asset):
path: Path
obj: np.ndarray = None

model_config = ConfigDict(arbitrary_types_allowed=True)

def init(self):
self.obj = np.load(self.path)
Empty file added src/indeca/tubes/deconv.yaml
Empty file.
68 changes: 68 additions & 0 deletions src/indeca/tubes/demo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
noob_id: indeca-demo
noob_model: noob.tube.TubeSpecification
noob_version: 0.0.1
description: |
Demo of a simple noob tube with an input,
an asset that is mutated across epochs,
and a simple operation that does the mutation.

See tests/test_tubes.py:test_tube for an example invocation.

Further detail -
we pass a path to some save array when instantiating the tube
The NPYAsset then uses that path to load the array.
Since the array is `runner`-scoped, it will persist as long as the runner does,
and we use the `depends` on the asset to ensure that it is updated
from the result of the add operation.
The structured mutation allows asset mutations to work even when we are in a multi-process
context like the ZMQRunner (which we'll get to later)

We use `params` for static parameters passed to the function that do not change,
and dependencies are the "dynamic" structure of the graph - which events get passed to the nodes.
`depends` is usually an array of objects (as it is used in the fft node)
where the key is the param in the function, and the value is the signal that we depend on
In this case none of the nodes annotate the names of their signals, so the signal is just named "value"
The `add` node uses positional arguments (because it is a builtin function that doesn't take kwargs)
which are passed as just an array of strings (not an array of objects)

The return node is the one special-cased node in noob,
and the structure of its `depends` determines the structure of what comes out of the tube when you call process
an array of objects like we have here will return a dictionary like `{'a': .., 'b': ...}`
An array of strings would return a list like `[..., ...]`
and a scalar `depends: add.value` would return just the array itself.


input:
array_path:
type: pathlib.Path
scope: tube

assets:
array:
type: indeca.demo.NPYAsset
params:
path: input.array_path
scope: runner
depends: add.value

nodes:
fft:
type: indeca.core.AR_kernel.noise_fft
params:
noise_range: [0.1, 0.9]
depends:
- px: assets.array
add:
type: operator.add
depends:
- assets.array
- fft.value

return:
type: return
depends:
- a: fft.value
- b: add.value



Empty file added src/indeca/tubes/pipeline.yaml
Empty file.
32 changes: 32 additions & 0 deletions tests/test_tubes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import numpy as np
from noob import Tube, SynchronousRunner
from noob.config import add_config_source

from indeca.config import tube_path


def _make_sin(frequency: float) -> np.ndarray:
sampling_rate = 1000 # Hz
duration = 1.0 # seconds
amplitude = 1
t = np.linspace(0, duration, int(sampling_rate * duration), endpoint=False)
wave = amplitude * np.sin(2 * np.pi * frequency * t)
return wave


def test_tube(tmp_path):
"""just show what tubes do!"""
add_config_source(tube_path())

wave = _make_sin(5)
npy_path = tmp_path / "array.npy"
np.save(npy_path, wave)

tube = Tube.from_specification("indeca-demo", input={"array_path": npy_path})
runner = SynchronousRunner(tube)

incremented_by = 0
for i in range(5):
result = runner.process()
incremented_by += result["a"]
assert np.allclose(result["b"], wave + incremented_by)
Loading