Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extracting part of a cell, network etc. #249

Open
jnsbck opened this issue Feb 21, 2024 · 5 comments
Open

extracting part of a cell, network etc. #249

jnsbck opened this issue Feb 21, 2024 · 5 comments
Assignees
Labels
enhancement New feature or request v1.0

Comments

@jnsbck
Copy link
Contributor

jnsbck commented Feb 21, 2024

Hi,

I have a morphology

cell = jx.read_swc(f"morphology.swc", nseg=4, max_branch_len=300.0, min_radius=5.0)
cell.insert(HH())

but I want to simulate only a specific branch. I don't care about anything else that is attached. Since cell.branch(0) returns an instance of BranchView, the following does not work.

branch = cell.branch(0)

branch.comp(0).stimulate(current)
branch.record()

voltage = jx.integrate(branch)

Is this somehow possible, since I was not able to find an easy way to do this? If not it would be awesome to have this functionality.

i.e. by adding a .copy() method to the BranchView object, that returns a new instance of Branch

branch = cell.branch(0).copy()

branch..comp(0).stimulate(current)
branch.record()

voltage = jx.integrate(branch)

Same would be applicable to Cell and Comp. Would be happy to provide a PR if this functionality is otherwise non-trivially obtained.

Thanks for your input on this. :)

@michaeldeistler
Copy link
Contributor

michaeldeistler commented Feb 21, 2024

Nope this is not possible. I agree that this would be nice but I do not think that I will find the time to do this anytime soon.

If you can make a PR this would be super awesome.

@michaeldeistler michaeldeistler added the enhancement New feature or request label Feb 21, 2024
@jnsbck
Copy link
Contributor Author

jnsbck commented Feb 21, 2024

Cool, will make a PR then :)

@jnsbck jnsbck linked a pull request Apr 24, 2024 that will close this issue
9 tasks
@michaeldeistler
Copy link
Contributor

Related to #354

@jnsbck
Copy link
Contributor Author

jnsbck commented Oct 23, 2024

This can be done in a limited fashion with #447 and should be improved down the line.

@jnsbck jnsbck self-assigned this Dec 22, 2024
@jnsbck
Copy link
Contributor Author

jnsbck commented Jan 10, 2025

the following works if #487 is merged!

def infer_module_type_from_inds(idxs: pd.DataFrame) -> str:
    nuniques = idxs[["cell_index", "branch_index", "comp_index"]].nunique()
    nuniques.index = ["cell", "branch", "compartment"]
    nuniques = pd.concat([pd.Series({"network": 1}), nuniques])
    return_type = nuniques.loc[nuniques == 1].index[-1]
    return return_type


def build_module_scaffold(
    idxs: pd.DataFrame,
    return_type: Optional[str] = None,
    parent_branches: Optional[List[np.ndarray]] = None,
) -> Union[jx.Network, jx.Cell, jx.Branch, jx.Compartment]:
    """Builds a skeleton module from a DataFrame of indices.
    This is useful for instantiating a module that can be filled with data later.
    Args:
        idxs: DataFrame containing cell_index, branch_index, comp_index, i.e.
            Module.nodes or View.view.
        return_type: Type of module to return. If None, the type is inferred from the
            number of unique values in the indices. I.e. only 1 unique cell_index
                and 1 unique branch_index -> return_type = "jx.Branch".
    Returns:
        A skeleton module with the correct number of compartments, branches, cells, or
        networks."""
    return_types = ["compartment", "branch", "cell", "network"]
    build_cache = {k: [] for k in return_types}

    if return_type is None:  # infer return type from idxs
        return_type = infer_module_type_from_inds(idxs)

    comp = jx.Compartment()
    build_cache["compartment"] = [comp]

    if return_type in return_types[1:]:
        nsegs = idxs["branch_index"].value_counts().iloc[0]
        branch = jx.Branch([comp for _ in range(nsegs)])
        build_cache["branch"] = [branch]

    if return_type in return_types[2:]:
        for cell_id, cell_groups in idxs.groupby("cell_index"):
            num_branches = cell_groups["branch_index"].nunique()
            default_parents = np.arange(num_branches) - 1  # ignores morphology
            parents = (
                default_parents if parent_branches is None else parent_branches[cell_id]
            )
            cell = jx.Cell([branch] * num_branches, parents)
            build_cache["cell"].append(cell)

    if return_type in return_types[3:]:
        build_cache["network"] = [jx.Network(build_cache["cell"])]

    module = build_cache[return_type][0]
    build_cache.clear()
    return module

def to_module(view: View, reset_index: bool = True) -> "Module":
    """Extract part of a module and return a copy of its View or a new module.

    This can be used to call `jx.integrate` on part of a Module.

    Args:
        reset_index: if True, the indices of the new module are reset to start from 0.
        as_module: if True, a new module is returned instead of a View.

    Returns:
        A part of the module or a copied view of it."""
    view = deepcopy(view)
    if reset_index:
        view.nodes.reset_index(drop=True, inplace=True)
        view.edges.reset_index(drop=True, inplace=True)
        # TODO: also re-enumerate cell,branch,comp indices in nodes and edges

    testnodes = view.nodes.copy()
    testnodes.rename({"global_"+k:k for k in ["cell_index", "branch_index", "comp_index"]}, axis=1, inplace=True)
    mod_type = infer_module_type_from_inds(testnodes)
    module = build_module_scaffold(testnodes, mod_type)
    module.__dict__.update(view.__dict__)
    return module

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request v1.0
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants