Skip to content

Commit

Permalink
Upgrade cvxopt (#348)
Browse files Browse the repository at this point in the history
* Update `pre-commit` and make some other fixes

* Update version pins

* Remove references to `cvxopt`

* Add type

Co-authored-by: Alan R Lowe <[email protected]>

* Use `|=`

* Remove extra dependencies

---------

Co-authored-by: Alan R Lowe <[email protected]>
  • Loading branch information
2 people authored and p-j-smith committed Aug 8, 2023
1 parent 817a73e commit 7ca973b
Show file tree
Hide file tree
Showing 13 changed files with 36 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .napari-hub/DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,5 @@ If working on Apple Silicon make sure to also install the following
packages from `conda-forge`.

```sh
conda install -c conda-forge cvxopt pyqt
conda install -c conda-forge pyqt
```
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.272
rev: v0.0.278
hooks:
- id: ruff
- repo: https://github.com/Lucas-C/pre-commit-hooks
Expand Down
11 changes: 0 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,6 @@ it is now built against `C++=17`.
pip install btrack
```

#### Installing on Apple Silicon

Install the following with
[conda](https://github.com/conda-forge/miniforge):

```sh
conda install -c conda-forge cvxopt
```

then run the above `pip` install command.

## Usage examples

Visit [btrack documentation](https://btrack.readthedocs.io) to learn how to use it and see other examples.
Expand Down
26 changes: 11 additions & 15 deletions btrack/btypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import ctypes
from collections import OrderedDict
from typing import Any, NamedTuple, Optional
from typing import Any, ClassVar, NamedTuple, Optional

import numpy as np

Expand Down Expand Up @@ -86,7 +86,7 @@ class PyTrackObject(ctypes.Structure):
"""

_fields_ = [
_fields_: ClassVar[list] = [
("ID", ctypes.c_long),
("x", ctypes.c_double),
("y", ctypes.c_double),
Expand All @@ -109,9 +109,7 @@ def __init__(self):

@property
def properties(self) -> dict[str, Any]:
if self.dummy:
return {}
return self._properties
return {} if self.dummy else self._properties

@properties.setter
def properties(self, properties: dict[str, Any]):
Expand All @@ -129,7 +127,7 @@ def set_features(self, keys: list[str]) -> None:
self.n_features = 0
return

if not all(k in self.properties for k in keys):
if any(k not in self.properties for k in keys):
missing_features = list(set(keys).difference(set(self.properties.keys())))
raise KeyError(f"Feature(s) missing: {missing_features}.")

Expand All @@ -151,7 +149,7 @@ def to_dict(self) -> dict[str, Any]:
for k, _ in PyTrackObject._fields_
if k not in ("features", "n_features")
}
node.update(self.properties)
node |= self.properties
return node

@staticmethod
Expand Down Expand Up @@ -217,7 +215,7 @@ class PyTrackingInfo(ctypes.Structure):
"""

_fields_ = [
_fields_: ClassVar[list] = [
("error", ctypes.c_uint),
("n_tracks", ctypes.c_uint),
("n_active", ctypes.c_uint),
Expand All @@ -235,8 +233,7 @@ def to_dict(self) -> dict[str, Any]:
"""Return a dictionary of the statistics"""
# TODO(arl): make this more readable by converting seconds, ms
# and interpreting error messages?
stats = {k: getattr(self, k) for k, typ in PyTrackingInfo._fields_}
return stats
return {k: getattr(self, k) for k, typ in PyTrackingInfo._fields_}

@property
def tracker_active(self) -> bool:
Expand Down Expand Up @@ -265,7 +262,7 @@ class PyGraphEdge(ctypes.Structure):
source timestamp, we just assume that the tracker has done it's job.
"""

_fields_ = [
_fields_: ClassVar[list] = [
("source", ctypes.c_long),
("target", ctypes.c_long),
("score", ctypes.c_double),
Expand All @@ -274,8 +271,7 @@ class PyGraphEdge(ctypes.Structure):

def to_dict(self) -> dict[str, Any]:
"""Return a dictionary describing the edge."""
edge = {k: getattr(self, k) for k, _ in PyGraphEdge._fields_}
return edge
return {k: getattr(self, k) for k, _ in PyGraphEdge._fields_}


class Tracklet:
Expand Down Expand Up @@ -516,9 +512,9 @@ def to_dict(
"""Return a dictionary of the tracklet which can be used for JSON
export. This is an ordered dictionary for nicer JSON output.
"""
trk_tuple = tuple([(p, getattr(self, p)) for p in properties])
trk_tuple = tuple((p, getattr(self, p)) for p in properties)
data = OrderedDict(trk_tuple)
data.update(self.properties)
data |= self.properties
return data

def to_array(
Expand Down
8 changes: 3 additions & 5 deletions btrack/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import os
from pathlib import Path
from typing import Optional
from typing import ClassVar, Optional

import numpy as np
from pydantic import BaseModel, conlist, validator
Expand Down Expand Up @@ -95,9 +95,7 @@ class TrackerConfig(BaseModel):

@validator("volume", pre=True, always=True)
def _parse_volume(cls, v):
if isinstance(v, tuple):
return ImagingVolume(*v)
return v
return ImagingVolume(*v) if isinstance(v, tuple) else v

@validator("tracking_updates", pre=True, always=True)
def _parse_tracking_updates(cls, v):
Expand All @@ -112,7 +110,7 @@ def _parse_tracking_updates(cls, v):
class Config:
arbitrary_types_allowed = True
validate_assignment = True
json_encoders = {
json_encoders: ClassVar[dict] = {
np.ndarray: lambda x: x.ravel().tolist(),
}

Expand Down
4 changes: 1 addition & 3 deletions btrack/io/_localization.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,7 @@ def __call__(
for dim in range(len(centroids))
}

nodes = {"t": [frame] * num_nodes}
nodes.update(coords)

nodes = {"t": [frame] * num_nodes} | coords
for img_prop in self.img_props:
nodes[img_prop] = [
getattr(props[idx], img_prop) for idx in range(num_nodes)
Expand Down
2 changes: 1 addition & 1 deletion btrack/io/exporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def export_LBEP(filename: os.PathLike, tracks: list):
def _export_HDF(
filename: os.PathLike,
tracker,
obj_type=None,
obj_type: Optional[str] = None,
filter_by: Optional[str] = None,
):
"""Export to HDF."""
Expand Down
20 changes: 10 additions & 10 deletions btrack/io/hdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import os
import re
from ast import literal_eval
from functools import wraps
from typing import TYPE_CHECKING, Any, Optional, Union

Expand Down Expand Up @@ -276,7 +277,7 @@ def filtered_objects(
else:
raise ValueError(f"Cannot filter objects by {f_expr}")

filtered_idx = [i for i, x in enumerate(data) if eval(f_eval)]
filtered_idx = [i for i, x in enumerate(data) if literal_eval(f_eval)]

else:
filtered_idx = range(txyz.shape[0]) # default filtering uses all
Expand All @@ -302,7 +303,7 @@ def filtered_objects(

# add the filtered properties
for key, props in properties.items():
objects_dict.update({key: props[filtered_idx]})
objects_dict[key] = props[filtered_idx]

return objects_from_dict(objects_dict)

Expand Down Expand Up @@ -404,16 +405,16 @@ def write_properties(

# Check if the property is already in the props_grp:
if key in props_grp:
if allow_overwrite is False:
if allow_overwrite:
del self._hdf[f"objects/{self.object_type}/properties"][key]
logger.info(f"Property '{key}' erased to be overwritten...")

else:
logger.info(f"Property '{key}' already written in the file")
raise KeyError(
f"Property '{key}' already in file -> switch on "
"'overwrite' param to replace existing property "
)
else:
del self._hdf[f"objects/{self.object_type}/properties"][key]
logger.info(f"Property '{key}' erased to be overwritten...")

# Now that you handled overwriting, write the values:
logger.info(f"Writing properties/{self.object_type}/{key} {values.shape}")
props_grp.create_dataset(key, data=data[key], dtype="float32")
Expand Down Expand Up @@ -451,9 +452,8 @@ def tracks(self) -> list[btypes.Tracklet]:
obj = self.filtered_objects(f_expr=f_expr)

def _get_txyz(_ref: int) -> int:
if _ref >= 0:
return obj[_ref]
return dummy_obj[abs(_ref) - 1] # references are -ve for dummies
# references are -ve for dummies
return obj[_ref] if _ref >= 0 else dummy_obj[abs(_ref) - 1]

tracks = []
for i in range(track_map.shape[0]):
Expand Down
5 changes: 3 additions & 2 deletions btrack/optimise/hypothesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@


import ctypes
from typing import ClassVar

from btrack import constants

Expand Down Expand Up @@ -49,7 +50,7 @@ class Hypothesis(ctypes.Structure):
These are automatically generated by the optimiser
"""

_fields_ = [
_fields_: ClassVar[list] = [
("hypothesis", ctypes.c_uint),
("ID", ctypes.c_uint),
("probability", ctypes.c_double),
Expand Down Expand Up @@ -94,7 +95,7 @@ class PyHypothesisParams(ctypes.Structure):
"""

_fields_ = [
_fields_: ClassVar[list] = [
("lambda_time", ctypes.c_double),
("lambda_dist", ctypes.c_double),
("lambda_link", ctypes.c_double),
Expand Down
6 changes: 0 additions & 6 deletions docs/dev_guide/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ If you would rather install the latest development version, and/or compile direc
./build.sh
pip install -e .
If working on Apple Silicon then also run:

.. code:: sh
conda install -c conda-forge cvxopt
If developing the documentation then run the following

.. code:: sh
Expand Down
5 changes: 1 addition & 4 deletions docs/user_guide/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,4 @@ After we've created and activated the virtual environment, on the same terminal,

pip install btrack

This will download and install ``btrack`` and all its dependencies. If working
on Apple Silicon then also run::

conda install -c conda-forge cvxopt
This will download and install ``btrack`` and all its dependencies.
2 changes: 1 addition & 1 deletion docs/user_guide/napari.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ dependencies needed to use these plugins, install the ``napari`` extra via.::

If working on Apple Silicon then also run::

conda install -c conda-forge cvxopt pyqt
conda install -c conda-forge pyqt

The Tracks layer
================
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ classifiers = [
"Topic :: Scientific/Engineering :: Visualization",
]
dependencies = [
"cvxopt>=1.2.0",
"cvxopt>=1.3.1",
"h5py>=2.10.0",
"numpy>=1.17.3",
"pandas>=2.0.3",
"pooch>=1.0.0",
"pydantic~=1.9",
"pydantic<2",
"scikit-image>=0.16.2",
"scipy>=1.3.1",
"tqdm>=4.65.0",
Expand Down

0 comments on commit 7ca973b

Please sign in to comment.