From c3a0ee4001a330cabe659ea8eb7255be23f4ae05 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Fri, 29 Jul 2022 18:17:46 -0700 Subject: [PATCH 01/29] Adding Cython StepVector to the project --- tiny/rna/counter/stepvector/__init__.py | 1 + tiny/rna/counter/stepvector/__init__.pyx | 0 tiny/rna/counter/stepvector/_stepvector.pxd | 45 ++++ tiny/rna/counter/stepvector/_stepvector.pyx | 177 ++++++++++++++ tiny/rna/counter/stepvector/src/PyRef.h | 89 +++++++ .../rna/counter/stepvector/src/StepVector.cpp | 219 ++++++++++++++++++ tiny/rna/counter/stepvector/src/StepVector.h | 32 +++ 7 files changed, 563 insertions(+) create mode 100644 tiny/rna/counter/stepvector/__init__.py create mode 100644 tiny/rna/counter/stepvector/__init__.pyx create mode 100644 tiny/rna/counter/stepvector/_stepvector.pxd create mode 100644 tiny/rna/counter/stepvector/_stepvector.pyx create mode 100644 tiny/rna/counter/stepvector/src/PyRef.h create mode 100644 tiny/rna/counter/stepvector/src/StepVector.cpp create mode 100644 tiny/rna/counter/stepvector/src/StepVector.h diff --git a/tiny/rna/counter/stepvector/__init__.py b/tiny/rna/counter/stepvector/__init__.py new file mode 100644 index 00000000..97932c73 --- /dev/null +++ b/tiny/rna/counter/stepvector/__init__.py @@ -0,0 +1 @@ +from ._stepvector import StepVector \ No newline at end of file diff --git a/tiny/rna/counter/stepvector/__init__.pyx b/tiny/rna/counter/stepvector/__init__.pyx new file mode 100644 index 00000000..e69de29b diff --git a/tiny/rna/counter/stepvector/_stepvector.pxd b/tiny/rna/counter/stepvector/_stepvector.pxd new file mode 100644 index 00000000..df485bd9 --- /dev/null +++ b/tiny/rna/counter/stepvector/_stepvector.pxd @@ -0,0 +1,45 @@ +# distutils: language = c++ +# cython: language_level = 3 + +from cpython.ref cimport PyObject +from libcpp.map cimport map as ccmap + +ctypedef PyObject* PyPtr + +cdef extern from "src/PyRef.h" namespace "PyPtr": + cdef cppclass PyRef: + PyRef() except + + PyRef(PyObject* set_obj) except + + PyObject* get() const + PyRef& operator+(const PyRef &po) + +cdef extern from "src/StepVector.cpp": + pass + +cdef extern from "src/StepVector.h" namespace "sparse_vectors": + cdef cppclass _StepVector[T]: + ctypedef ccmap[long int, T].const_iterator const_iterator + + _StepVector() except + + + long int min_index + long int max_index + + T operator[](long int i) const + void set_value(long int start, long int end, T value) except + + void add_value(long int start, long int end, T value) except + + void apply_to_values(long int start, long int end, void (*func)(T &val)) + long int num_values() const + const_iterator get_values(long int start) const + const_iterator begin() const + const_iterator end() const + +cdef class StepVector: + cdef _StepVector[PyRef] *c_step + cdef long int start + cdef long int stop + cdef char _typecode + @staticmethod + cdef void _construct_wrapper(StepVector obj) + @staticmethod + cdef void _set_wrapper(StepVector obj, _StepVector[PyRef] *ref) diff --git a/tiny/rna/counter/stepvector/_stepvector.pyx b/tiny/rna/counter/stepvector/_stepvector.pyx new file mode 100644 index 00000000..47f30f8a --- /dev/null +++ b/tiny/rna/counter/stepvector/_stepvector.pyx @@ -0,0 +1,177 @@ +# distutils: language = c++ +# cython: language_level = 3 + +import sys +import HTSeq + +from cython.operator cimport dereference as deref, postincrement as cni + +cdef class StepVector: + + @classmethod + def create(cls, *args, **kwargs): + cdef StepVector self = StepVector(*args, **kwargs) + StepVector._construct_wrapper(self) + return self + + def __cinit__(self, length = sys.maxsize, typecode = 'O', start_index = 0): + self.c_step = NULL + self._typecode = ord(typecode) + self.start = start_index + self.stop = start_index + length + + @staticmethod + cdef void _construct_wrapper(StepVector obj): + obj.c_step = new _StepVector[PyRef]() + + @staticmethod + cdef void _set_wrapper(StepVector obj, _StepVector[PyRef] *ref): + obj.c_step = ref + + def __setitem__(self, index, value): + if isinstance(value, StepVector): + if value.start == index.start and value.stop == index.stop: + return + else: + raise NotImplemented("Stepvector-to-Stepvector assignment still missing") + if isinstance(index, slice): + if index.step is not None and index.step != 1: + raise ValueError("Striding slices (i.e., step != 1) are not supported") + if index.start is None: + start = self.start + else: + if index.start < self.start: + raise IndexError("start too small") + start = index.start + if index.stop is None: + stop = self.stop + else: + if index.stop > self.stop: + raise IndexError("stop too large") + stop = index.stop + # Note the "-1": The C++ object uses closed intervals, but we follow + # Python convention here and use half-open ones. + self.c_step.set_value(start, stop-1, PyRef(value)) + else: + self.c_step.set_value(index, index, PyRef(value)) + + def get_steps(self, values_only = False, merge_steps = True): + cdef _StepVector[PyRef].const_iterator startvals + cdef set value, prevval + startvals = self.c_step.get_values(self.start) + prevstart = self.start + + # Iterator bounds check until a better solution is written + if startvals == self.c_step.end(): + yield (self.start, self.stop, set()) + return + else: + prevval = deref(cni(startvals)).second.get() + + while startvals != self.c_step.end(): + pair = deref(cni(startvals)) + stepstart, value = pair.first, pair.second.get() + if merge_steps and value == prevval: + continue + if self.stop is not None and stepstart >= self.stop: + if not values_only: + yield prevstart, self.stop, prevval + else: + yield prevval + return + if not values_only: + yield prevstart, stepstart, prevval + else: + yield prevval + prevstart, prevval = stepstart, value + else: + if not values_only: + yield prevstart, min(self.stop, self.c_step.max_index), prevval + else: + yield prevval + + def __getitem__(self, index): + cdef StepVector res + cdef int start + cdef int stop + + if isinstance(index, slice): + if index.step is not None and index.step != 1: + raise ValueError("Striding slices (i.e., step != 1) are not supported") + if index.start is None: + start = self.start + else: + if index.start < self.start: + raise IndexError("start too small") + start = index.start + if index.stop is None: + stop = self.stop + else: + if index.stop > self.stop: + raise IndexError("stop too large") + stop = index.stop + res = StepVector(stop - start, 'O', start) + StepVector._set_wrapper(res, self.c_step) + res.start = start + res.stop = stop + return res + else: + return deref(self.c_step.get_values(index)).second.get() + + def __iter__(self): + for start, stop, value in self.get_steps(): + for i in range(start, stop): + yield value + + def __repr__(self): + if self.start == -sys.maxsize - 1: + start_s = "-inf" + else: + start_s = str(self.start) + if self.stop == sys.maxsize: + stop_s = "inf" + else: + stop_s = str(self.stop) + return "<%s object, type '%s', index range %s:%s, %d step(s)>" % ( + self.__class__.__name__, self.typecode(), start_s, + stop_s, self.num_steps()) + + def typecode(self): + return chr(self._typecode) + + @property + def start(self): + return self.start + + @property + def stop(self): + return self.stop + + @start.setter + def start(self, value): + self.start = value + + @stop.setter + def stop(self, value): + self.stop = value + + def __len__(self): + return self.stop - self.start + + def num_steps(self): + return self.c_step.num_values() + + def __iadd__(self, value): + cdef PyRef ref = PyRef(value) + self.c_step.add_value(self.start, self.stop-1, ref) + return self + + # Todo + def __eq__(self, other): pass + def __reduce__(self): pass + def apply(self, func, start=None, stop=None): pass + + @property + def __class__(self): + """~~master_of_disguise.exe~~""" + return HTSeq.StepVector.StepVector \ No newline at end of file diff --git a/tiny/rna/counter/stepvector/src/PyRef.h b/tiny/rna/counter/stepvector/src/PyRef.h new file mode 100644 index 00000000..37014b28 --- /dev/null +++ b/tiny/rna/counter/stepvector/src/PyRef.h @@ -0,0 +1,89 @@ +#include "Python.h" +#include + +namespace PyPtr { + class PyRef { + PyObject* obj; + + public: + PyObject* get() const {return obj;} + + // Default constructor + PyRef() { + obj = NULL; + } + + // Obj constructor + PyRef(PyObject* obj) { + if (PySet_Check(obj)){ + this->obj = obj; + } else { + // PySet_New treats the argument as an iterable + // We don't want that, e.g. if adding a tuple + // PySet_Add simply adds without iteration + this->obj = PySet_New(NULL); + PySet_Add(this->obj, obj); + } + Py_XINCREF(this->obj); + } + + // Destructor + ~PyRef() { + Py_XDECREF(obj); + } + + // Copy constructor + PyRef(const PyRef& other) { + obj = PySet_New(other.obj); + Py_XINCREF(obj); + Py_XDECREF(other.obj); + } + + // Move constructor + PyRef(PyRef&& other) { + obj = other.obj; + Py_XDECREF(other.obj); + } + + // Assignment + PyRef& operator=(const PyRef& other) { + Py_XDECREF(obj); + obj = other.obj; + Py_XINCREF(obj); + return *this; + } + + // Move assignment + PyRef& operator=(PyRef&& other) { + Py_XDECREF(obj); + obj = PySet_New(other.obj); + Py_XINCREF(obj); + other.obj = NULL; + return *this; + } + + PyRef& operator+= (const PyRef& other) { + if (PySet_Check(other.obj)){ + _PySet_Update(this->obj, PyObject_GetIter(other.obj)); + } else { + PySet_Add(this->obj, other.obj); + } + return *this; + } + + PyRef operator+ (const PyRef& other) { + PyRef result(obj); + if (PySet_Check(other.obj)){ + _PySet_Update(result.obj, PyObject_GetIter(other.obj)); + } else { + PySet_Add(result.obj, other.obj); + } + Py_XINCREF(result.obj); + return result; + } + + bool operator== (const PyRef &other) const { + return PyObject_RichCompareBool(obj, other.obj, Py_EQ); + } + }; +} \ No newline at end of file diff --git a/tiny/rna/counter/stepvector/src/StepVector.cpp b/tiny/rna/counter/stepvector/src/StepVector.cpp new file mode 100644 index 00000000..f07ca296 --- /dev/null +++ b/tiny/rna/counter/stepvector/src/StepVector.cpp @@ -0,0 +1,219 @@ +#include "StepVector.h" + +using namespace sparse_vectors; + +template _StepVector::_StepVector() { m[min_index] = T(); } + +template const long int _StepVector::min_index = LONG_MIN; + +template const long int _StepVector::max_index = LONG_MAX; + +template const T _StepVector::operator[](long int i) const { + const_iterator it = m.upper_bound(i); + it--; + return it->second; +} + +template +void _StepVector::set_value(long int from, long int to, T value) { + if (from > to) + throw std::out_of_range("Indices reversed in StepVector."); + + // Unless the new step extends to the end, we need to insert a new + // value afterwards unless the step to the right has the same value + if (to < max_index) { + T next_value = (*this)[to + 1]; + if (!(next_value == value)) + m[to + 1] = next_value; + } + + // Find the left step, i.e., the step whose start is smaller or equal + // to 'from': + typename std::map::iterator left = m.upper_bound(from); + left--; + assert(left->first <= from); + + // Get rid of the steps present between from and to + typename std::map::iterator it = m.lower_bound(from); + if (it->first == from) + it++; + assert(it->first > from); + if (it->first <= to) { + m.erase(it, m.upper_bound(to)); + } + + if (!(left->second == value)) { + if (left->first != from) + // Insert a new step + m[from] = value; + else { + // We have from == left->first, so the step is already present. + // Would changing m[from] to value make it equal to its left + // neighbor? + if (left == m.begin()) + // no, there is no left neighbor + m[from] = value; + else { + typename std::map::iterator leftleft = left; + leftleft--; + if (!(leftleft->second == value)) + // ok, change the value + m[from] = value; + else + // no, rather delete the step + m.erase(left); + } + } + } +} + +bool debug = false; +template +void _StepVector::add_value(long int from, long int to, T value) { + if (debug) { + std::cout << std::endl << "Begin: " << std::flush; + PyObject_Print(value.get(), stdout, 0); + std::cout << std::endl << std::flush; + } + if (from > to) + throw std::out_of_range("Indices reversed in StepVector."); + + if (to < max_index) { + T next_value = (*this)[to + 1]; + auto before = m[to + 1]; + m[to + 1] = next_value; + if (debug) { + std::cout << to + 1 << ": setting \"next\" from: "; + PyObject_Print(before.get(), stdout, 0); + std::cout << " to "; + PyObject_Print(m[to + 1].get(), stdout, 0); + std::cout << " (" << m[to+1].get() << ") " << std::endl; + } + } + + typename std::map::iterator it = m.upper_bound(from); + it--; + bool need_to_insert_step_at_from = it->first < from; + T old_val_at_from; + if (need_to_insert_step_at_from) { + old_val_at_from = it->second; + it++; + } + // Now, it points to the first element with it->first >= from + + for (; it != m.end() && it->first <= to; it++){ + if (debug){ + std::cout << it->first << ": adding to "; + PyObject_Print(it->second.get(), stdout, Py_PRINT_RAW); + std::cout << " (" << it->second.get() << ") " << std::endl; + } + + it->second += value; + } + + if (need_to_insert_step_at_from){ + m[from] = old_val_at_from + value; + if (debug){ + std::cout << from << ": inserting step into "; + PyObject_Print(old_val_at_from.get(), stdout, Py_PRINT_RAW); + std::cout << " (" << old_val_at_from.get() << ") " << std::endl; + } + } +} + +template +void _StepVector::apply_to_values(long int from, long int to, void (*func)(T &val)) { + if (from > to) + throw std::out_of_range("Indices reversed in StepVector."); + + if (to < max_index) { + T next_value = (*this)[to + 1]; + m[to + 1] = next_value; + } + + typename std::map::iterator it = m.upper_bound(from); + it--; + bool need_to_insert_step_at_from = it->first < from; + T old_val_at_from; + if (need_to_insert_step_at_from) { + old_val_at_from = it->second; + it++; + } + // Now, it points to the first element with it->first >= from + + for (; it != m.end() && it->first <= to; it++) + func(it->second); + + if (need_to_insert_step_at_from) { + func(old_val_at_from); + m[from] = old_val_at_from; + } +} + +template +typename _StepVector::const_iterator +_StepVector::get_values(long int from) const { + return --m.upper_bound(from); +} + +template< class T > +long int _StepVector::num_values( ) const{ + return this->m.size(); +} + +template +typename _StepVector::const_iterator +_StepVector::begin() const { + return m.begin(); +} + +template +typename _StepVector::const_iterator +_StepVector::end() const { + return m.end(); +} + +//template +//iterator::iterator(){ +// current = m.begin() +// last = m.end() +//} +// +//template +//iterator::iterator(typename const_iterator from){ +// current = from +// last = m.end() +//} +// +//template +//std::pair& iterator::operator*(){return current;} +// +//template +//iterator iterator::operator++(){ +// if (current == last){ +// PyErr_SetString(PyExc_StopIteration, "Reached StepVector end"); +// return NULL; +// } else { +// return *current++; +// } +//} +// +//template +//iterator iterator::operator--(){ +// if (current == begin()){ +// PyErr_SetString(PyExc_StopIteration, "Reached StepVector start");} +// return NULL; +// } else { +// return *current--; +// } +//} +// +//template +//bool iterator::operator==(const iterator other){ +// return current == other.current; +//} +// +//template +//bool iterator::operator!=(const iterator other){ +// return current != other.current; +//} \ No newline at end of file diff --git a/tiny/rna/counter/stepvector/src/StepVector.h b/tiny/rna/counter/stepvector/src/StepVector.h new file mode 100644 index 00000000..ee64a316 --- /dev/null +++ b/tiny/rna/counter/stepvector/src/StepVector.h @@ -0,0 +1,32 @@ +#ifndef _STEP_VECTOR_H_ +#define _STEP_VECTOR_H_ + +#include +#include +#include +#include //for now only + +#include "Python.h" + +namespace sparse_vectors { + template< class T > + class _StepVector { + protected: + std::map< long int, T > m; + public: + typedef typename std::map< long int, T >::const_iterator const_iterator; + static const long int min_index; + static const long int max_index; + _StepVector( ); + const T operator[]( long int i ) const; + void set_value( long int from, long int to, T value ); + void add_value( long int from, long int to, T value ); + void apply_to_values( long int from, long int to, void (*func)( T & val ) ); + long int num_values( ) const; + const_iterator get_values( long int from ) const; + const_iterator begin( ) const; + const_iterator end( ) const; + }; +}; + +#endif //_STEP_VECTOR_H_ \ No newline at end of file From 4619b6a02d2c0f7009bb557d70d33e4a48d1829c Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Fri, 29 Jul 2022 18:19:54 -0700 Subject: [PATCH 02/29] Adding Cythonization to setup.py. This is currently a macOS-only implementation. Linux compatability requires essentially removing the extra_*_args kwds, but further testing is needed on both Linux and macOS --- setup.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index d0bd6109..cc79fd94 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,7 @@ import setuptools from setuptools.command.install import install +from Cython.Build import cythonize # Package metadata NAME = 'tinyrna' @@ -14,20 +15,29 @@ PLATFORM = 'Unix' REQUIRES_PYTHON = '>=3.7.0' VERSION = '0.1' +REQUIRED = [] # Required packages are installed via Conda's environment.yml -# Required packages are installed via Conda's environment.yml -# See PreFlight below... -REQUIRED = [] - -class PreFlight(install): +class PrereqAndExec(install): def run(self): - if not all([os.getenv(conda_var) for conda_var in ["CONDA_PREFIX", "CONDA_DEFAULT_ENV"]]): - sys.exit("CRITICAL ERROR: you appear to be installing %s outside of a conda environment.\n" - "Instead, please run: ./setup.sh" % (NAME,)) + if not self.in_conda_env(): + sys.exit("CRITICAL ERROR: you appear to be installing %s outside of a conda environment.\n" % (NAME,) + + "Instead, please run: ./setup.sh" ) else: install.run(self) + def in_conda_env(self): + return all([os.getenv(conda_var) for conda_var in ["CONDA_PREFIX", "CONDA_DEFAULT_ENV"]]) + +debug_cython = False +cython_extensions = [ + setuptools.Extension( + "tiny.rna.counter.stepvector._stepvector", + sources=['tiny/rna/counter/stepvector/_stepvector.pyx'], + extra_compile_args=['-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include', '-stdlib=libc++', '-std=c++11'], + extra_link_args=['-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'], + language='c++') +] setuptools.setup( name=NAME, @@ -35,7 +45,7 @@ def run(self): author=AUTHOR, author_email=EMAIL, description=DESCRIPTION, - cmdclass={'install': PreFlight}, + cmdclass={'install': PrereqAndExec}, include_package_data=True, packages=['tiny'], zip_safe=False, @@ -48,6 +58,11 @@ def run(self): 'tiny-plot = tiny.rna.plotter:main' ] }, + ext_modules=cythonize( + cython_extensions, + compiler_directives={'language_level': '3'}, + gdb_debug=debug_cython + ), scripts=['tiny/rna/tiny-deseq.r'], classifiers=[ 'Programming Language :: Python :: 3', From ee577a460b767c3e600126b1b4bb17e413e8ad0d Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Fri, 29 Jul 2022 18:27:04 -0700 Subject: [PATCH 03/29] Integrating the Cython StepVector with ReferenceTables. The necessary changes are actually pretty minimal. There's some extra fluff here to make switching between Cython and HTSeq StepVector easier --- tiny/rna/counter/hts_parsing.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index 43925a11..178c4e4c 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -351,7 +351,11 @@ def __init__(self, gff_files: Dict[str, list], feature_selector, **kwargs): self._set_filters(**kwargs) self.gff_files = gff_files # ----------------------------------------------------------- Primary Key: - self.feats = HTSeq.GenomicArrayOfSets("auto", stranded=False) # Root Match ID + if use_cython_stepvector: + self.feats = HTSeq.GenomicArray("auto", stranded=False) # Root Match ID + else: + self.feats = HTSeq.GenomicArrayOfSets("auto", stranded=False) # Root Match ID + self.parents, self.filtered = {}, set() # Original Feature ID self.intervals = defaultdict(list) # Root Feature ID self.matches = defaultdict(set) # Root Match ID @@ -362,6 +366,10 @@ def __init__(self, gff_files: Dict[str, list], feature_selector, **kwargs): # Patch the GFF attribute parser to support comma separated attribute value lists setattr(HTSeq.features.GFF_Reader, 'parse_GFF_attribute_string', staticmethod(parse_GFF_attribute_string)) + # Patch HTSeq's StepVector with our Cython implementation (60% faster) + if use_cython_stepvector: + setattr(HTSeq.StepVector, 'StepVector', StepVector) + @report_execution_time("GFF parsing") def get(self) -> Tuple[StepVector, AliasTable, ClassTable, dict]: """Initiates GFF parsing and returns the resulting reference tables""" From 831069fda7a3a447a79f86193556db26d99213bf Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sat, 30 Jul 2022 11:25:58 -0700 Subject: [PATCH 04/29] Got my branches a little tangled. These changes were intended for the last commit, but it has already been pushed to origin. --- tiny/rna/counter/hts_parsing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index 178c4e4c..5e991385 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -317,6 +317,10 @@ def not_implemented(self): StepVector = HTSeq.GenomicArrayOfSets Tags = DefaultDict[str, Set[str]] +use_cython_stepvector = True +if use_cython_stepvector: + from tiny.rna.counter.stepvector import StepVector + setattr(HTSeq.StepVector, 'StepVector', StepVector) class ReferenceTables: """A GFF parser which builds feature, alias, and class reference tables @@ -366,10 +370,6 @@ def __init__(self, gff_files: Dict[str, list], feature_selector, **kwargs): # Patch the GFF attribute parser to support comma separated attribute value lists setattr(HTSeq.features.GFF_Reader, 'parse_GFF_attribute_string', staticmethod(parse_GFF_attribute_string)) - # Patch HTSeq's StepVector with our Cython implementation (60% faster) - if use_cython_stepvector: - setattr(HTSeq.StepVector, 'StepVector', StepVector) - @report_execution_time("GFF parsing") def get(self) -> Tuple[StepVector, AliasTable, ClassTable, dict]: """Initiates GFF parsing and returns the resulting reference tables""" From 95dec150c0e580cf25f08399b7d3b196b25ac647 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sun, 14 Aug 2022 11:15:48 -0700 Subject: [PATCH 05/29] Native Python efficiency improvements: After doing a lot of profiling and fiddling with the routine in FeatureSelector.choose(), I'm once again back to flattening the list of Stage 2 candidates via a list comprehension. 1/2 of Stage 2 is performed in the comp., and step 2/2 (calculating min_rank) is now performed with one call on the resulting list. While runtimes are better with this approach, elimination by hierarchy is still flawed. I've also removed the inner generator expression in FeatureCounter.assign_features(). After the recent switch to calling union(*) on StepVector steps, it really doesn't make sense to screen for zero length step sets. This is much more efficient. Strands are now represented with a boolean value which simplifies some things. Strand selection is now performed via a chain of bitwise XOR operations. This prevents us from having to form and pass a tuple for each evaluation. --- tiny/rna/counter/features.py | 58 +++++++++++++++++++-------------- tiny/rna/counter/hts_parsing.py | 12 +++---- tiny/rna/counter/matching.py | 2 +- tiny/rna/counter/statistics.py | 8 ++--- 4 files changed, 45 insertions(+), 35 deletions(-) diff --git a/tiny/rna/counter/features.py b/tiny/rna/counter/features.py index 785b6567..ee43f0d5 100644 --- a/tiny/rna/counter/features.py +++ b/tiny/rna/counter/features.py @@ -43,12 +43,10 @@ def assign_features(self, al: dict) -> Tuple[dict, int]: feat_matches, assignment = set(), {} try: - feat_matches = feat_matches.union(*(match for match in - (Features.chrom_vectors[al['chrom']]['.'] # GenomicArrayOfSets -> ChromVector - .array[al['start']:al['end']] # ChromVector -> StepVector - .get_steps(values_only=True)) # StepVector -> {features} - # If an alignment does not map to a feature, an empty set is returned - if len(match) != 0)) + feat_matches = feat_matches.union(*( + Features.chrom_vectors[al['chrom']]['.'] # GenomicArrayOfSets -> ChromVector + .array[al['start']:al['end']] # ChromVector -> StepVector + .get_steps(values_only=True))) # StepVector -> {features} except KeyError as ke: self.stats.chrom_misses[ke.args[0]] += 1 @@ -133,33 +131,45 @@ def choose(cls, candidates: Set[feature_record_tuple], alignment: dict) -> Mappi selections: a set of features which passed selection """ - hits, min_rank = [], sys.maxsize - - for feat, strand, matches in candidates: - for rule, rank, iv_match in matches: - if rank > min_rank: continue - if alignment not in iv_match: continue - if rank < min_rank: min_rank = rank - hits.append((rank, rule, feat, strand)) + hits = [(rank, rule, feat, strand) + for feat, strand, matches in candidates + for rule, rank, iv_match in matches + if alignment in iv_match] if not hits: return {} - selections = defaultdict(set) - for hit in hits: - if hit[0] != min_rank: continue - _, rule, feat, strand = hit + min_rank = min(hits, key=lambda x: x[0]) - strand = (alignment['strand'], strand) - nt5end = alignment['nt5'] - length = alignment['len'] + for rank, rule, feat, strand in hits: + if rank != min_rank: continue rule_def = FeatureSelector.rules_table[rule] - if strand not in rule_def["Strand"]: continue - if nt5end not in rule_def["nt5end"]: continue - if length not in rule_def["Length"]: continue + if alignment['nt5end'] not in rule_def["nt5end"]: continue + if alignment['Length'] not in rule_def["Length"]: continue + if alignment['Strand'] ^ strand not in rule_def["Strand"]: continue + selections[feat].add(rule) + + return selections + + """ + # MEGAZORD list comprehension??: + required = FeatureSelector.rules_table + min_rank = min(rank for _, _, matches in candidates for _, rank, _ in matches) + hits = [(rank, rule, feat) + for feat, strand, matches in candidates + for rule, rank, iv_match in matches + if rank == min_rank + if alignment in iv_match + if alignment['nt5end'] in required[rule]['nt5end'] + if alignment['Length'] in required[rule]['Length'] + if (strand ^ alignment['Strand']) in required[rule]["Strand"]] + + selections = defaultdict(set) + for rank, rule, feat in hits: selections[feat].add(rule) return selections + """ @staticmethod def build_selectors(rules_table) -> List[dict]: diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index 5e991385..e1bb5eac 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -72,24 +72,24 @@ def _parse_alignments(self, file_obj) -> Iterator[dict]: # Note: we assume sRNA sequencing data is NOT reversely stranded if (int(cols[1]) & 16): - strand = '-' + strand = False # - try: nt5 = complement[seq[-1]] except KeyError: nt5 = chr(seq[-1]) else: - strand = '+' + strand = True # + nt5 = chr(seq[0]) yield { "name": cols[0].decode(), - "len": length, + "Length": length, "seq": seq, - "nt5": nt5, + "nt5end": nt5, "chrom": cols[2].decode(), "start": start, "end": start + length, - "strand": strand + "Strand": strand } except Exception as e: # Append to error message while preserving exception provenance and traceback @@ -548,7 +548,7 @@ def _finalize_features(self): for sub_iv in merged_sub_ivs: finalized_match_tuples = self.selector.build_interval_selectors(sub_iv, sorted_matches.copy()) - self.feats[sub_iv] += (tagged_id, sub_iv.strand, tuple(finalized_match_tuples)) + self.feats[sub_iv] += (tagged_id, sub_iv.strand == '+', tuple(finalized_match_tuples)) @staticmethod def _merge_adjacent_subintervals(unmerged_sub_ivs: List[HTSeq.GenomicInterval]) -> list: diff --git a/tiny/rna/counter/matching.py b/tiny/rna/counter/matching.py index 67767aa5..a948ad7b 100644 --- a/tiny/rna/counter/matching.py +++ b/tiny/rna/counter/matching.py @@ -28,7 +28,7 @@ def __init__(self, strand): self.select = (self.strand == 'sense') def __contains__(self, x): - return self.select == (x[0] == x[1]) + return self.select ^ x def __repr__(self): return str(self.strand) diff --git a/tiny/rna/counter/statistics.py b/tiny/rna/counter/statistics.py index d08249d8..1ac80d1c 100644 --- a/tiny/rna/counter/statistics.py +++ b/tiny/rna/counter/statistics.py @@ -42,7 +42,7 @@ def count_bundle(self, aln_bundle: iter) -> dict: bundle_read = aln_bundle[0] loci_counts = len(aln_bundle) - nt5, seqlen = bundle_read['nt5'], len(bundle_read['seq']) + nt5, seqlen = bundle_read['nt5end'], bundle_read['Length'] # Calculate counts for multi-mapping read_counts = int(bundle_read['name'].split('=')[1]) @@ -477,18 +477,18 @@ def record_alignment_details(self, aln, bundle, assignments): # Perform reverse complement for anti-sense reads read = aln['seq'] \ - if aln['strand'] == '+' \ + if aln['Strand'] == '+' \ else aln['seq'][::-1].translate(self.complement) # sequence, cor_counts, strand, start, end, feat1;feat2;feat3 - self.alignments.append((read, bundle['corr_count'], aln['strand'], aln['start'], aln['end'], + self.alignments.append((read, bundle['corr_count'], aln['Strand'], aln['start'], aln['end'], ';'.join(assignments))) def record_diagnostics(self, assignments, n_candidates, aln, bundle): """Records basic diagnostic info""" if len(assignments) == 0: - if aln['strand'] == '+': + if aln['Strand'] == '+': self.alignment_diags['Uncounted alignments (+)'] += 1 else: self.alignment_diags['Uncounted alignments (-)'] += 1 From 220bfb2191c033c3a645983e8f487e0dbc29e193 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sun, 14 Aug 2022 11:18:41 -0700 Subject: [PATCH 06/29] Native Python performance improvement: By slightly adjusting the way case-insensitive dictionary entries are constructed, the construction time of these dictionaries has been substantially reduced which makes GFF parsing faster. During a few test runs I saw reductions in the ballpark of 50% for the GFF parsing stage. --- tiny/rna/counter/hts_parsing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index e1bb5eac..f5a3ae79 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -228,13 +228,14 @@ def parse_GFF_attribute_string(attrStr, extra_return_first_value=False, gff_vers class CaseInsensitiveAttrs(Dict[str, tuple]): """A dictionary subclass that allows for case-insensitive queries against feature attributes""" + template = namedtuple("Entry", "orig_key orig_val ci_val")(None, None, None) + def __init__(self): super().__init__() - self.Entry = namedtuple("Entry", "orig_key orig_val ci_val") def __setitem__(self, key: str, val: tuple): lowercase_val = tuple(v.lower() for v in val) - super().__setitem__(key.lower(), self.Entry(key, val, lowercase_val)) + super().__setitem__(key.lower(), self.template._replace(orig_key=key, orig_val=val, ci_val=lowercase_val)) def __getitem__(self, key: str): # Allows case-insensitive key lookups which return original case values From f6b0f925e578499e00871a7349ac8c1b55471e08 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sun, 14 Aug 2022 21:36:08 -0700 Subject: [PATCH 07/29] Bugfix --- tiny/rna/counter/features.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny/rna/counter/features.py b/tiny/rna/counter/features.py index ee43f0d5..5ba05033 100644 --- a/tiny/rna/counter/features.py +++ b/tiny/rna/counter/features.py @@ -138,7 +138,7 @@ def choose(cls, candidates: Set[feature_record_tuple], alignment: dict) -> Mappi if not hits: return {} selections = defaultdict(set) - min_rank = min(hits, key=lambda x: x[0]) + min_rank = min(hits, key=lambda x: x[0])[0] for rank, rule, feat, strand in hits: if rank != min_rank: continue From 8f2c0378f971b462d28cf70aaa0b29c90c5d9ed3 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Mon, 22 Aug 2022 16:59:59 -0700 Subject: [PATCH 08/29] Refactored the StepVector's core C++ so that it is a more proper definition of a templated class. Cython doesn't particularly care, but this is useful for C++ to C++ direct testing for easier debugging. This has also led to some much needed improvements to the PyRef wrapper for PyObjects. This refactoring has also fixed a major issue with the StepVector following recent changes which caused massive accumulations of features. It has also led to a slight reduction in memory. At the end of the day, the procedure and design of our specialized Cython StepVector has been substantially improved. --- tiny/rna/counter/hts_parsing.py | 14 +- tiny/rna/counter/stepvector/_stepvector.pxd | 3 - tiny/rna/counter/stepvector/src/PyRef.h | 84 +++++-- .../rna/counter/stepvector/src/StepVector.cpp | 219 ------------------ tiny/rna/counter/stepvector/src/StepVector.h | 173 ++++++++++++++ 5 files changed, 243 insertions(+), 250 deletions(-) delete mode 100644 tiny/rna/counter/stepvector/src/StepVector.cpp diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index f5a3ae79..7eb187c5 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -318,10 +318,6 @@ def not_implemented(self): StepVector = HTSeq.GenomicArrayOfSets Tags = DefaultDict[str, Set[str]] -use_cython_stepvector = True -if use_cython_stepvector: - from tiny.rna.counter.stepvector import StepVector - setattr(HTSeq.StepVector, 'StepVector', StepVector) class ReferenceTables: """A GFF parser which builds feature, alias, and class reference tables @@ -350,13 +346,15 @@ class ReferenceTables: source_filter = [] type_filter = [] - def __init__(self, gff_files: Dict[str, list], feature_selector, **kwargs): - self.all_features = kwargs.get('all_features', False) + def __init__(self, gff_files: Dict[str, list], feature_selector, **prefs): + self.all_features = prefs.get('all_features', False) self.selector = feature_selector - self._set_filters(**kwargs) + self._set_filters(**prefs) self.gff_files = gff_files # ----------------------------------------------------------- Primary Key: - if use_cython_stepvector: + if prefs['step_vector'] == 'Cython': + from tiny.rna.counter.stepvector import StepVector + setattr(HTSeq.StepVector, 'StepVector', StepVector) self.feats = HTSeq.GenomicArray("auto", stranded=False) # Root Match ID else: self.feats = HTSeq.GenomicArrayOfSets("auto", stranded=False) # Root Match ID diff --git a/tiny/rna/counter/stepvector/_stepvector.pxd b/tiny/rna/counter/stepvector/_stepvector.pxd index df485bd9..daccea6a 100644 --- a/tiny/rna/counter/stepvector/_stepvector.pxd +++ b/tiny/rna/counter/stepvector/_stepvector.pxd @@ -13,9 +13,6 @@ cdef extern from "src/PyRef.h" namespace "PyPtr": PyObject* get() const PyRef& operator+(const PyRef &po) -cdef extern from "src/StepVector.cpp": - pass - cdef extern from "src/StepVector.h" namespace "sparse_vectors": cdef cppclass _StepVector[T]: ctypedef ccmap[long int, T].const_iterator const_iterator diff --git a/tiny/rna/counter/stepvector/src/PyRef.h b/tiny/rna/counter/stepvector/src/PyRef.h index 37014b28..3458d649 100644 --- a/tiny/rna/counter/stepvector/src/PyRef.h +++ b/tiny/rna/counter/stepvector/src/PyRef.h @@ -1,30 +1,38 @@ #include "Python.h" #include +#include namespace PyPtr { class PyRef { PyObject* obj; + bool debug = false; public: PyObject* get() const {return obj;} // Default constructor PyRef() { - obj = NULL; + obj = PySet_New(NULL); } // Obj constructor - PyRef(PyObject* obj) { - if (PySet_Check(obj)){ - this->obj = obj; + PyRef(PyObject* payload) { + if (PySet_Check(payload)){ + obj = payload; + Py_XINCREF(obj); } else { // PySet_New treats the argument as an iterable // We don't want that, e.g. if adding a tuple // PySet_Add simply adds without iteration - this->obj = PySet_New(NULL); - PySet_Add(this->obj, obj); + obj = PySet_New(NULL); + PySet_Add(obj, payload); + } + + if (debug){ + std::cout << "Object ctor: "; + PyObject_Print(payload, stdout, 0); + std::cout << " (obj: " << obj << ")" << std::endl; } - Py_XINCREF(this->obj); } // Destructor @@ -35,50 +43,86 @@ namespace PyPtr { // Copy constructor PyRef(const PyRef& other) { obj = PySet_New(other.obj); - Py_XINCREF(obj); - Py_XDECREF(other.obj); + + if (debug) { + std::cout << "Copy ctor: "; + PyObject_Print(other.obj, stdout, 0); + std::cout << " (obj: " << other.obj << " -> " << obj << ")"; + std::cout << std::endl << std::flush; + } } // Move constructor PyRef(PyRef&& other) { + if (debug) { + std::cout << "Move ctor: "; + PyObject_Print(other.obj, stdout, 0); + std::cout << " (obj: " << other.obj << ")"; + std::cout << std::endl << std::flush; + } + obj = other.obj; Py_XDECREF(other.obj); } // Assignment PyRef& operator=(const PyRef& other) { + if (debug) { + std::cout << "Assignment: "; + PyObject_Print(other.obj, stdout, 0); + std::cout << " (obj: " << other.obj << ")"; + std::cout << std::endl << std::flush; + } + Py_XDECREF(obj); - obj = other.obj; - Py_XINCREF(obj); + obj = PySet_New(other.obj); return *this; } // Move assignment PyRef& operator=(PyRef&& other) { + if (debug) { + std::cout << "Move assignment: "; + PyObject_Print(other.obj, stdout, 0); + std::cout << " (obj: " << other.obj << ")"; + std::cout << std::endl << std::flush; + } + Py_XDECREF(obj); - obj = PySet_New(other.obj); - Py_XINCREF(obj); + obj = other.obj; + // Py_XINCREF(obj); other.obj = NULL; return *this; } PyRef& operator+= (const PyRef& other) { + if (debug) { + std::cout << "+=" << std::endl; + } + if (PySet_Check(other.obj)){ - _PySet_Update(this->obj, PyObject_GetIter(other.obj)); + _PySet_Update(obj, PyObject_GetIter(other.obj)); } else { - PySet_Add(this->obj, other.obj); + std::cerr << "Error: rhs value holds a non-set type PyObject." << std::endl; } return *this; } - PyRef operator+ (const PyRef& other) { + PyRef operator+ (const PyRef& rhs) { + if (debug){ + PyObject_Print(obj, stdout, 0); + std::cout << " + "; + PyObject_Print(rhs.obj, stdout, 0); + std::cout << std::endl; + } + PyRef result(obj); - if (PySet_Check(other.obj)){ - _PySet_Update(result.obj, PyObject_GetIter(other.obj)); + if (PySet_Check(rhs.obj)){ + _PySet_Update(result.obj, PyObject_GetIter(rhs.obj)); } else { - PySet_Add(result.obj, other.obj); + std::cerr << "Error: rhs value holds a non-set type PyObject." << std::endl; } - Py_XINCREF(result.obj); + return result; } diff --git a/tiny/rna/counter/stepvector/src/StepVector.cpp b/tiny/rna/counter/stepvector/src/StepVector.cpp deleted file mode 100644 index f07ca296..00000000 --- a/tiny/rna/counter/stepvector/src/StepVector.cpp +++ /dev/null @@ -1,219 +0,0 @@ -#include "StepVector.h" - -using namespace sparse_vectors; - -template _StepVector::_StepVector() { m[min_index] = T(); } - -template const long int _StepVector::min_index = LONG_MIN; - -template const long int _StepVector::max_index = LONG_MAX; - -template const T _StepVector::operator[](long int i) const { - const_iterator it = m.upper_bound(i); - it--; - return it->second; -} - -template -void _StepVector::set_value(long int from, long int to, T value) { - if (from > to) - throw std::out_of_range("Indices reversed in StepVector."); - - // Unless the new step extends to the end, we need to insert a new - // value afterwards unless the step to the right has the same value - if (to < max_index) { - T next_value = (*this)[to + 1]; - if (!(next_value == value)) - m[to + 1] = next_value; - } - - // Find the left step, i.e., the step whose start is smaller or equal - // to 'from': - typename std::map::iterator left = m.upper_bound(from); - left--; - assert(left->first <= from); - - // Get rid of the steps present between from and to - typename std::map::iterator it = m.lower_bound(from); - if (it->first == from) - it++; - assert(it->first > from); - if (it->first <= to) { - m.erase(it, m.upper_bound(to)); - } - - if (!(left->second == value)) { - if (left->first != from) - // Insert a new step - m[from] = value; - else { - // We have from == left->first, so the step is already present. - // Would changing m[from] to value make it equal to its left - // neighbor? - if (left == m.begin()) - // no, there is no left neighbor - m[from] = value; - else { - typename std::map::iterator leftleft = left; - leftleft--; - if (!(leftleft->second == value)) - // ok, change the value - m[from] = value; - else - // no, rather delete the step - m.erase(left); - } - } - } -} - -bool debug = false; -template -void _StepVector::add_value(long int from, long int to, T value) { - if (debug) { - std::cout << std::endl << "Begin: " << std::flush; - PyObject_Print(value.get(), stdout, 0); - std::cout << std::endl << std::flush; - } - if (from > to) - throw std::out_of_range("Indices reversed in StepVector."); - - if (to < max_index) { - T next_value = (*this)[to + 1]; - auto before = m[to + 1]; - m[to + 1] = next_value; - if (debug) { - std::cout << to + 1 << ": setting \"next\" from: "; - PyObject_Print(before.get(), stdout, 0); - std::cout << " to "; - PyObject_Print(m[to + 1].get(), stdout, 0); - std::cout << " (" << m[to+1].get() << ") " << std::endl; - } - } - - typename std::map::iterator it = m.upper_bound(from); - it--; - bool need_to_insert_step_at_from = it->first < from; - T old_val_at_from; - if (need_to_insert_step_at_from) { - old_val_at_from = it->second; - it++; - } - // Now, it points to the first element with it->first >= from - - for (; it != m.end() && it->first <= to; it++){ - if (debug){ - std::cout << it->first << ": adding to "; - PyObject_Print(it->second.get(), stdout, Py_PRINT_RAW); - std::cout << " (" << it->second.get() << ") " << std::endl; - } - - it->second += value; - } - - if (need_to_insert_step_at_from){ - m[from] = old_val_at_from + value; - if (debug){ - std::cout << from << ": inserting step into "; - PyObject_Print(old_val_at_from.get(), stdout, Py_PRINT_RAW); - std::cout << " (" << old_val_at_from.get() << ") " << std::endl; - } - } -} - -template -void _StepVector::apply_to_values(long int from, long int to, void (*func)(T &val)) { - if (from > to) - throw std::out_of_range("Indices reversed in StepVector."); - - if (to < max_index) { - T next_value = (*this)[to + 1]; - m[to + 1] = next_value; - } - - typename std::map::iterator it = m.upper_bound(from); - it--; - bool need_to_insert_step_at_from = it->first < from; - T old_val_at_from; - if (need_to_insert_step_at_from) { - old_val_at_from = it->second; - it++; - } - // Now, it points to the first element with it->first >= from - - for (; it != m.end() && it->first <= to; it++) - func(it->second); - - if (need_to_insert_step_at_from) { - func(old_val_at_from); - m[from] = old_val_at_from; - } -} - -template -typename _StepVector::const_iterator -_StepVector::get_values(long int from) const { - return --m.upper_bound(from); -} - -template< class T > -long int _StepVector::num_values( ) const{ - return this->m.size(); -} - -template -typename _StepVector::const_iterator -_StepVector::begin() const { - return m.begin(); -} - -template -typename _StepVector::const_iterator -_StepVector::end() const { - return m.end(); -} - -//template -//iterator::iterator(){ -// current = m.begin() -// last = m.end() -//} -// -//template -//iterator::iterator(typename const_iterator from){ -// current = from -// last = m.end() -//} -// -//template -//std::pair& iterator::operator*(){return current;} -// -//template -//iterator iterator::operator++(){ -// if (current == last){ -// PyErr_SetString(PyExc_StopIteration, "Reached StepVector end"); -// return NULL; -// } else { -// return *current++; -// } -//} -// -//template -//iterator iterator::operator--(){ -// if (current == begin()){ -// PyErr_SetString(PyExc_StopIteration, "Reached StepVector start");} -// return NULL; -// } else { -// return *current--; -// } -//} -// -//template -//bool iterator::operator==(const iterator other){ -// return current == other.current; -//} -// -//template -//bool iterator::operator!=(const iterator other){ -// return current != other.current; -//} \ No newline at end of file diff --git a/tiny/rna/counter/stepvector/src/StepVector.h b/tiny/rna/counter/stepvector/src/StepVector.h index ee64a316..09225ef3 100644 --- a/tiny/rna/counter/stepvector/src/StepVector.h +++ b/tiny/rna/counter/stepvector/src/StepVector.h @@ -27,6 +27,179 @@ namespace sparse_vectors { const_iterator begin( ) const; const_iterator end( ) const; }; + + template _StepVector::_StepVector() { + m[min_index] = T(); + } + + template const long int _StepVector::min_index = LONG_MIN; + + template const long int _StepVector::max_index = LONG_MAX; + + template const T _StepVector::operator[](long int i) const { + const_iterator it = m.upper_bound(i); + it--; + return it->second; + } + + template + void _StepVector::set_value(long int from, long int to, T value) { + if (from > to) + throw std::out_of_range("Indices reversed in StepVector."); + + // Unless the new step extends to the end, we need to insert a new + // value afterwards unless the step to the right has the same value + if (to < max_index) { + T next_value = (*this)[to + 1]; + if (!(next_value == value)) + m[to + 1] = next_value; + } + + // Find the left step, i.e., the step whose start is smaller or equal + // to 'from': + typename std::map::iterator left = m.upper_bound(from); + left--; + assert(left->first <= from); + + // Get rid of the steps present between from and to + typename std::map::iterator it = m.lower_bound(from); + if (it->first == from) + it++; + assert(it->first > from); + if (it->first <= to) { + m.erase(it, m.upper_bound(to)); + } + + if (!(left->second == value)) { + if (left->first != from) + // Insert a new step + m[from] = value; + else { + // We have from == left->first, so the step is already present. + // Would changing m[from] to value make it equal to its left + // neighbor? + if (left == m.begin()) + // no, there is no left neighbor + m[from] = value; + else { + typename std::map::iterator leftleft = left; + leftleft--; + if (!(leftleft->second == value)) + // ok, change the value + m[from] = value; + else + // no, rather delete the step + m.erase(left); + } + } + } + } + + bool debug = false; + template + void _StepVector::add_value(long int from, long int to, T value) { + if (debug) { + std::cout << std::endl << "Begin: " << std::flush; + PyObject_Print(value.get(), stdout, 0); + std::cout << std::endl << std::flush; + } + if (from > to) + throw std::out_of_range("Indices reversed in StepVector."); + + if (to < max_index) { + T next_value = (*this)[to + 1]; // copy ctor + auto before = m[to + 1]; // copy ctor + m[to + 1] = next_value; // assignment operator + if (debug) { + std::cout << to + 1 << ": setting \"next\" from: "; + PyObject_Print(before.get(), stdout, 0); + std::cout << " (" << before.get() << ") to "; + PyObject_Print(m[to + 1].get(), stdout, 0); + std::cout << " (" << m[to+1].get() << ") " << std::endl; + } + } + + typename std::map::iterator it = m.upper_bound(from); + it--; + bool need_to_insert_step_at_from = it->first < from; + T old_val_at_from; + if (need_to_insert_step_at_from) { + old_val_at_from = it->second; // assignment operator + it++; + } + // Now, it points to the first element with it->first >= from + + for (; it != m.end() && it->first <= to; it++){ + if (debug){ + std::cout << it->first << ": adding to "; + PyObject_Print(it->second.get(), stdout, Py_PRINT_RAW); + std::cout << " (" << it->second.get() << ") " << std::endl; + } + + it->second += value; + } + + if (need_to_insert_step_at_from){ + m[from] = old_val_at_from + value; // + operator and move assignment operator + if (debug){ + std::cout << from << ": inserting step into "; + PyObject_Print(old_val_at_from.get(), stdout, Py_PRINT_RAW); + std::cout << " (" << old_val_at_from.get() << ") " << std::endl; + } + } + } + + template + void _StepVector::apply_to_values(long int from, long int to, void (*func)(T &val)) { + if (from > to) + throw std::out_of_range("Indices reversed in StepVector."); + + if (to < max_index) { + T next_value = (*this)[to + 1]; + m[to + 1] = next_value; + } + + typename std::map::iterator it = m.upper_bound(from); + it--; + bool need_to_insert_step_at_from = it->first < from; + T old_val_at_from; + if (need_to_insert_step_at_from) { + old_val_at_from = it->second; + it++; + } + // Now, it points to the first element with it->first >= from + + for (; it != m.end() && it->first <= to; it++) + func(it->second); + + if (need_to_insert_step_at_from) { + func(old_val_at_from); + m[from] = old_val_at_from; + } + } + + template + typename _StepVector::const_iterator + _StepVector::get_values(long int from) const { + return --m.upper_bound(from); + } + + template< class T > + long int _StepVector::num_values( ) const{ + return this->m.size(); + } + + template + typename _StepVector::const_iterator + _StepVector::begin() const { + return m.begin(); + } + + template + typename _StepVector::const_iterator + _StepVector::end() const { + return m.end(); + } }; #endif //_STEP_VECTOR_H_ \ No newline at end of file From c09d1cb33ad8603bf59129e4d01101cd664ade12 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Mon, 22 Aug 2022 17:11:09 -0700 Subject: [PATCH 09/29] Refactored CaseInsensitiveDict to use regular tuples for internally storing keys/values rather than NamedTuples. The overhead for the convenience of having named indices was far too high, and definitely not worth it for this particular feature --- tiny/rna/counter/hts_parsing.py | 42 ++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index 7eb187c5..b65cecd4 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -3,7 +3,7 @@ import sys import re -from collections import Counter, defaultdict, namedtuple +from collections import Counter, defaultdict from typing import Tuple, List, Dict, Iterator, Optional, DefaultDict, Set, Union from inspect import stack @@ -226,22 +226,36 @@ def parse_GFF_attribute_string(attrStr, extra_return_first_value=False, gff_vers class CaseInsensitiveAttrs(Dict[str, tuple]): - """A dictionary subclass that allows for case-insensitive queries against feature attributes""" - - template = namedtuple("Entry", "orig_key orig_val ci_val")(None, None, None) + """A dictionary subclass that allows for case-insensitive queries against feature attributes + + From a bird's eye view, this class holds the feature attribute's name as the key, + and a tuple of values associated with that attribute name. The attribute's values did + not contain any commas, this tuple will have a length of 1. Otherwise, the original value + is tokenized on comma and each token is stored in a separate tuple index. + + Internally, each key is stored in lowercase form, and its associated value is a (nested) tuple + that contains (at the following indices): + [0]: The key (attribute name) in its original case + [1]: A tuple of values in their original case + [2]: A tuple of values in lowercase form + + Interactions with the Dict base class involve handling the "internal" nested tuple described + above. Functions which call self[item] in turn call these methods, and therefore the handling of the + internal tuple is abstracted away; these functions only deal with the key/values in their original form. + """ def __init__(self): super().__init__() - def __setitem__(self, key: str, val: tuple): - lowercase_val = tuple(v.lower() for v in val) - super().__setitem__(key.lower(), self.template._replace(orig_key=key, orig_val=val, ci_val=lowercase_val)) + def __setitem__(self, key: str, vals: tuple): + lowercase_vals = tuple(v.lower() for v in vals) + super().__setitem__(key.lower(), (key, vals, lowercase_vals)) def __getitem__(self, key: str): # Allows case-insensitive key lookups which return original case values # Ensure that KeyError contains the original key if key.lower() not in self: raise KeyError(key) - return super().__getitem__(key.lower()).orig_val + return super().__getitem__(key.lower())[1] def __contains__(self, key: str): # Allows case-insensitive membership queries by key @@ -249,11 +263,11 @@ def __contains__(self, key: str): def __str__(self): # Returns original case keys/values - return str({v.orig_key: v.orig_val for v in super().values()}) + return str({v[0]: v[1] for v in super().values()}) def __repr__(self): # Returns both original case and lowercase keys/values - return str({f"{k}/{v.orig_key}": f"{v.ci_val}/{v.orig_val}" for k,v in super().items()}) + return str({f"{k}/{v[0]}": f"{v[2]}/{v[1]}" for k,v in super().items()}) def setdefault(self, key: str, value: Optional[Tuple]=None): if key not in self: @@ -269,12 +283,12 @@ def get(self, key: str, default=None): def keys(self): # Roughly mimics a KeysView with original case for v in super(CaseInsensitiveAttrs, self).values(): - yield v.orig_key + yield v[0] def values(self): # Roughly mimics a ValuesView with original case for v in super(CaseInsensitiveAttrs, self).values(): - yield v.orig_val + yield v[1] def items(self): # Roughly mimics an ItemsView with original case @@ -295,12 +309,12 @@ def contains_ident(self, query: Tuple[Union[str, Wildcard], Union[str, Wildcard] if key_type is val_type is str: # Allows case-insensitive membership queries by (key, value) return key in self and \ - val in super(CaseInsensitiveAttrs, self).__getitem__(key).ci_val + val in super(CaseInsensitiveAttrs, self).__getitem__(key)[2] if key_type is str and val_type is Wildcard: return key in self if key_type is Wildcard and val_type is str: for v in super(CaseInsensitiveAttrs, self).values(): - if val in v.ci_val: return True + if val in v[2]: return True else: return False # Dict methods not implemented which are invalid if delegated to dict class From 5593853d08eabc29a288d29042a3563410f74514 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Mon, 22 Aug 2022 17:22:41 -0700 Subject: [PATCH 10/29] counter.py: added a commandline option to allow users to choose between our Cython or HTSeq's StepVector. setup.py: refactored args for Cython.Extension, added '-O3', and added an extension entry for test_stepvec.pyx _stepvector.pyx: misc. improvements --- setup.py | 18 +++++++++++++++--- tiny/rna/counter/counter.py | 2 ++ tiny/rna/counter/stepvector/_stepvector.pyx | 8 ++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index cc79fd94..b3e03fab 100644 --- a/setup.py +++ b/setup.py @@ -30,13 +30,25 @@ def in_conda_env(self): return all([os.getenv(conda_var) for conda_var in ["CONDA_PREFIX", "CONDA_DEFAULT_ENV"]]) debug_cython = False +cxx_extension_args = { + 'extra_compile_args': [ + '-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include', + '-stdlib=libc++', '-std=c++11', + '-O3'], + 'extra_link_args': ['-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'], + 'language': 'c++' +} cython_extensions = [ setuptools.Extension( "tiny.rna.counter.stepvector._stepvector", sources=['tiny/rna/counter/stepvector/_stepvector.pyx'], - extra_compile_args=['-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include', '-stdlib=libc++', '-std=c++11'], - extra_link_args=['-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'], - language='c++') + **cxx_extension_args + ), + setuptools.Extension( + "tiny.rna.counter.stepvector.test_stepvec", + sources=['tiny/rna/counter/stepvector/test_stepvec.pyx'], + **cxx_extension_args + ) ] setuptools.setup( diff --git a/tiny/rna/counter/counter.py b/tiny/rna/counter/counter.py index 7733907e..f84fe926 100644 --- a/tiny/rna/counter/counter.py +++ b/tiny/rna/counter/counter.py @@ -60,6 +60,8 @@ def get_args(): optional_args.add_argument('-d', '--report-diags', action='store_true', help='Produce diagnostic information about uncounted/eliminated ' 'selection elements.') + optional_args.add_argument('-sv', '--step-vector', choices=['Cython', 'HTSeq'], default='Cython', + help='Select which StepVector is used for interval -> feature resolution.') args = arg_parser.parse_args() setattr(args, 'normalize_by_hits', args.normalize_by_hits.lower() in ['t', 'true']) diff --git a/tiny/rna/counter/stepvector/_stepvector.pyx b/tiny/rna/counter/stepvector/_stepvector.pyx index 47f30f8a..59807f10 100644 --- a/tiny/rna/counter/stepvector/_stepvector.pyx +++ b/tiny/rna/counter/stepvector/_stepvector.pyx @@ -132,9 +132,8 @@ cdef class StepVector: stop_s = "inf" else: stop_s = str(self.stop) - return "<%s object, type '%s', index range %s:%s, %d step(s)>" % ( - self.__class__.__name__, self.typecode(), start_s, - stop_s, self.num_steps()) + return "" % ( + self.__class__.__name__, start_s, stop_s, self.num_steps()) def typecode(self): return chr(self._typecode) @@ -168,7 +167,8 @@ cdef class StepVector: # Todo def __eq__(self, other): pass - def __reduce__(self): pass + def __reduce__(self): + print("The __reduce__() function is not yet implemented") def apply(self, func, start=None, stop=None): pass @property From 78180d92ce861c294ae45f26eb28f725c1523612 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Mon, 22 Aug 2022 17:23:01 -0700 Subject: [PATCH 11/29] Added _chromvector.pyx and test_stepvec.pyx --- tiny/rna/counter/stepvector/_chromvector.pyx | 259 +++++++++++++++++++ tiny/rna/counter/stepvector/test_stepvec.pyx | 32 +++ 2 files changed, 291 insertions(+) create mode 100644 tiny/rna/counter/stepvector/_chromvector.pyx create mode 100644 tiny/rna/counter/stepvector/test_stepvec.pyx diff --git a/tiny/rna/counter/stepvector/_chromvector.pyx b/tiny/rna/counter/stepvector/_chromvector.pyx new file mode 100644 index 00000000..e0429fe5 --- /dev/null +++ b/tiny/rna/counter/stepvector/_chromvector.pyx @@ -0,0 +1,259 @@ +from ._stepvector import StepVector +from HTSeq._HTSeq import GenomicInterval + +cdef class ChromVector(object): + """Counting vector covering a chromosome. + + This class supports only the tinyRNA Cython implementation of the + HTSeq StepVector + """ + + cdef public object array + cdef public GenomicInterval iv + cdef public int offset + cdef public bint is_vector_of_sets + cdef public str _storage + cdef public str typecode + cdef public str memmap_dir + + @classmethod + def create(cls, GenomicInterval iv, str typecode, str storage, str memmap_dir=""): + """Create ChromVector from GenomicInterval + + Args: + iv (GenomicInterval): A GenomicInterval describing the chromosome + vector. + typecode ('d', 'i', 'l', 'b', or 'O'): What kind of data will be + stored inside this chromosome vector. 'd' for double, 'i' for int, + 'l' for long int, 'b' for boolean, 'O' for arbitrary objects + (e.g. sets). + storage ('step', 'ndarray', or 'memmap'): What kind of storage to + use. 'ndarray' is appropriate for short chromosomes and stores + each position in the genome into memory. 'memmap' stores all + positions, but maps the memory onto disk for larger chromosomes. + 'step' is a sparse representation similar to CSR matrices whereby + only the boundaries between genomic stretches with differing + data content are stored - see HTSeq.StepVector. + memmap_dir (str): If using 'memmap' storage, what folder to store + the memory maps. These can get quite big. + + Returns: + An instance of ChromVector with the requested options. + + """ + ncv = cls() + ncv.iv = iv + ncv.array = StepVector.create() + + ncv._storage = storage + ncv.typecode = typecode + # NOTE: As long as autochromosomes in GenomicArray are infinite length + # this has pretty limited use, but that might change + ncv.offset = iv.start + ncv.is_vector_of_sets = False + ncv.memmap_dir = memmap_dir + return ncv + + @classmethod + def _create_view(cls, ChromVector vec, GenomicInterval iv): + if iv.length == 0: + raise IndexError("Cannot subset to zero-length interval.") + v = cls() + v.iv = iv + v.array = vec.array + v.offset = vec.offset + v.is_vector_of_sets = vec.is_vector_of_sets + v._storage = vec._storage + return v + + def extend_to_include(self, iv): + if iv.strand != self.iv.strand: + raise ValueError( + 'The new interval must match the current strandedness', + ) + + # Step 1: extend the interval + length = self.iv.length + startdiff = max(self.iv.start - iv.start, 0) + self.iv.extend_to_include(iv) + self.offset = self.iv.start + + # Step 2: extend the array if needed, and shift-copy the old values + if self._storage == 'ndarray': + if self.typecode != 'O': + array = numpy.zeros(shape=(self.iv.length,), dtype=self.typecode) + else: + array = numpy.empty(shape=(self.iv.length,), dtype=self.typecode) + array[:] = None + array[startdiff: startdiff + length] = self.array[:] + elif self._storage == 'memmap': + array = numpy.memmap( + shape=(self.iv.length,), dtype=self.typecode, + filename=os.path.join( + self.memmap_dir, + self.iv.chrom + self.iv.strand + str(self.iv.start) + '_' \ + + str(self.iv.length) + ".nmm"), + mode='w+', + ) + array[startdiff: startdiff + length] = self.array[:] + else: + # The StepVector is created in ChromVector.create without explicit + # boundaries, so it's already bound by 0, +inf. So we do not need + # to extend it here, but rather just set the slice to the right + # value + array = self.array + self.array = array + + def __getitem__(self, index): + """Index or slice the chromosome. + + The index can be a few things: + - an integer: get the value of the vector at that chromosome coordinate + - a 1-step slice e.g "4:7": get a view of the chromosome region + between those coordinates. The array data are not copied. + - a GenomicInterval: similar to slices, with the additional choice of + strandedness. If this argument is stranded but the chromosome itself + is not stranded, a nonstranded view of the chromosome region is + returned. + + """ + cdef slice index_slice + cdef long int index_int + cdef long int start, stop + cdef GenomicInterval iv + + if isinstance(index, int): + index_int = index + if index_int < self.iv.start or index_int >= self.iv.end: + raise IndexError + return self.array[index_int - self.offset] + + elif isinstance(index, slice): + index_slice = index + if index_slice.start is None: + start = self.iv.start + else: + start = index_slice.start + if start < self.iv.start: + raise IndexError("start too small") + + if index_slice.stop is None: + stop = self.iv.end + else: + stop = index_slice.stop + if stop > self.iv.end: + raise IndexError("stop too large") + + iv = GenomicInterval(self.iv.chrom, start, stop, self.iv.strand) + + if not self.iv.contains(iv): + raise IndexError + return ChromVector._create_view(self, iv) + + elif isinstance(index, GenomicInterval): + if not self.iv.contains(index): + raise IndexError + + if self.iv.strand is strand_nostrand and \ + index.strand is not strand_nostrand: + iv = index.copy() # Is this correct now? + iv.strand = strand_nostrand + else: + iv = index + + return ChromVector._create_view(self, iv) + + else: + raise TypeError("Illegal index type") + + def __setitem__(self, index, value): + cdef slice index_slice + cdef long int start, stop + + if isinstance(value, ChromVector): + if self.array is value.array and value.iv.start == index.start and \ + value.iv.end == index.stop and (index.step is None or index.step == 1): + return + else: + raise NotImplementedError( + "Required assignment signature not yet implemented.") + + if isinstance(index, int): + self.array[index - self.iv.start] = value + + elif isinstance(index, slice): + index_slice = index + if index_slice.start is not None: + start = index_slice.start + if start < self.iv.start: + raise IndexError("start too small") + else: + start = self.iv.start + if index_slice.stop is not None: + stop = index_slice.stop + if stop > self.iv.end: + raise IndexError("stop too large") + else: + stop = self.iv.end + if start > stop: + raise IndexError("Start of interval is after its end.") + if start == stop: + raise IndexError("Cannot assign to zero-length interval.") + self.array[start - self.offset: stop - + self.iv.start: index.step] = value + + elif isinstance(index, GenomicInterval): + if index.chrom != self.iv.chrom: + raise KeyError("Chromosome name mismatch.") + if self.iv.strand is not strand_nostrand and \ + self.iv.strand is not self.index.strand: + raise KeyError("Strand mismatch.") + self.array[index.iv.start - self.iv.start, + index.iv.end - self.iv.start] = value + else: + raise TypeError("Illegal index type") + + def __iadd__(self, value): + if not self.is_vector_of_sets: + self.array[self.iv.start - self.offset: self.iv.end - + self.offset].__iadd__(value) + else: + def addval(x): + y = x.copy() + y.add(value) + return y + + self.apply(addval) + return self + + def __iter__(self): + return self.values() + + def values(self): + return iter(self.array[self.iv.start - self.offset: self.iv.end - self.offset]) + + def steps(self): + return _HTSeq_internal.ChromVector_steps(self) + + def apply(self, fun): + for iv, value in self.steps(): + self.array[iv.start - self.offset: iv.end - + self.offset] = fun(value) + + def __repr__(self): + return "<%s object, %s, %s>" % (self.__class__.__name__, str(self.iv), self._storage) + + def __reduce__(self): + assert self.__class__ is ChromVector + return(_ChromVector_unpickle, + (self.array, self.iv, self.offset, self.is_vector_of_sets, self._storage)) + + +def _ChromVector_unpickle(array, iv, offset, is_vector_of_sets, _storage): + cv = ChromVector() + cv.array = array + cv.iv = iv + cv.offset = offset + cv.is_vector_of_sets = is_vector_of_sets + cv._storage = _storage + return cv \ No newline at end of file diff --git a/tiny/rna/counter/stepvector/test_stepvec.pyx b/tiny/rna/counter/stepvector/test_stepvec.pyx new file mode 100644 index 00000000..fb7a3dcf --- /dev/null +++ b/tiny/rna/counter/stepvector/test_stepvec.pyx @@ -0,0 +1,32 @@ +# distutils: language = c++ +# cython: language_level = 3 + +from cpython.ref cimport PyObject +from cython.operator cimport dereference as deref, preincrement as inc, address + +from ._stepvector cimport _StepVector, PyRef +ctypedef PyObject* PyPtr + +def main(): + cdef PyRef r1, r2, r3, c1, c2 + + vctre = new _StepVector[PyRef]() + f1 = {"featA"} + f2 = {"featB", "featC"} + f3 = {"featD"} + r1 = PyRef(f1) + r2 = PyRef(f2) + r3 = PyRef(f3) + + vctre.add_value(11, 99, r1) + vctre.add_value(22, 44, r2) + vctre.add_value(33, 98, r3) + + it = vctre.begin() + while it != vctre.end(): + step = deref(it) + print(f"{ step.first}: { (step.second).get()}") + inc(it) + +if __name__ == '__main__': + main() \ No newline at end of file From 68162a8780ee606e7981e16fd97e765335007361 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Mon, 22 Aug 2022 18:02:17 -0700 Subject: [PATCH 12/29] Added a basic usage Python-space unit test for the Cython StepVector --- tests/unit_tests_stepvector.py | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tests/unit_tests_stepvector.py diff --git a/tests/unit_tests_stepvector.py b/tests/unit_tests_stepvector.py new file mode 100644 index 00000000..05f03106 --- /dev/null +++ b/tests/unit_tests_stepvector.py @@ -0,0 +1,88 @@ +import unittest +import HTSeq + +from tiny.rna.counter.stepvector._stepvector import StepVector + + +class StepVectorTests(unittest.TestCase): + + """Does our Cython StepVector accept and return steps as expected?""" + + def test_genomicarray_with_cython_stepvec(self): + # Patch the StepVector reference in the HTSeq module and use a GenomicArray + # instead of a GenomicArrayOfSets, just as we do in ReferenceTables + setattr(HTSeq.StepVector, 'StepVector', StepVector) + gas = HTSeq.GenomicArray('auto', stranded=False) + + iv1 = HTSeq.GenomicInterval("chr", 0, 5) + iv2 = HTSeq.GenomicInterval("chr", 5, 7) + iv3 = HTSeq.GenomicInterval("chr", 4, 6) + iv4 = HTSeq.GenomicInterval("chr", 7, 9) + ivs = [iv1, iv2, iv3, iv4] + + f1 = {'featA'} + f2 = {'featB', 'featC'} + f3 = {'featD'} + f4 = {'featE', 'featF', 'featG'} + fs = [f1, f2, f3, f4] + + """ + iv1: 0 |-----| 5 featA + iv2: 5 |--| 7 featB, featC + iv3: 4 |--| 6 featD + iv4: 7 |--| 9 featE, featF, featG + """ + + for iv, feat in zip(ivs, fs): + gas[iv] += feat + + exp_overlaps = [ + [(0, 4, f1), (4, 5, f1 | f3)], # f1 overlaps f3 + [(5, 6, f2 | f3), (6, 7, f2)], # f2 overlaps f3 + [(4, 5, f1 | f3), (5, 6, f3 | f2)], # f3 overlaps f1 and f2 + [(7, 9, f4)], # f4 does not overlap anything else + ] + + for iv, expected in zip(ivs, exp_overlaps): + actual = list(gas['chr']['.'].array[iv.start:iv.end].get_steps()) + self.assertEqual(actual, expected) + + def test_compare_to_htseq_steps(self): + """Test currently doesn't work (and it won't be easy to get it to work) + + This is because HTSeq's StepVector with typecode 'O' handles .add_value() using the + apply() function and a local function which copies the set OUT of the StepVector, + adds to it, then puts it back. This is exactly the kind of thing that our + specialized StepVector was intended to avoid. + """ + + from HTSeq.StepVector import StepVector as hStepVector + def htseq_add(x): + y = x.copy() + y.add(value) + return y + + sv_len = 200 + ours = StepVector.create(length=sv_len) + theirs = hStepVector.create(length=sv_len, typecode='O') + + iv1 = slice(0, 99) + iv2 = slice(99, 101) + iv3 = slice(98, 100) + iv4 = slice(101, 150) + ivs = [iv1, iv2, iv3, iv4] + + f1 = {'featA'} + f2 = {'featB', 'featC'} + f3 = {'featD'} + f4 = {'featE', 'featF', 'featG'} + fs = [f1, f2, f3, f4] + + for iv, feat in zip(ivs, fs): + value = feat + theirs[iv].apply(htseq_add) + ours[iv] += feat + + +if __name__ == '__main__': + unittest.main() From 3a8baa87e2328fbd2f74e0e392046ab5e72bf66c Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Mon, 22 Aug 2022 18:37:42 -0700 Subject: [PATCH 13/29] A slightly more efficient routine for converting values to lowercase in CaseInsensitiveDict --- tiny/rna/counter/hts_parsing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index b65cecd4..a1460fd8 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -248,7 +248,7 @@ def __init__(self): super().__init__() def __setitem__(self, key: str, vals: tuple): - lowercase_vals = tuple(v.lower() for v in vals) + lowercase_vals = tuple(map(str.lower, vals)) super().__setitem__(key.lower(), (key, vals, lowercase_vals)) def __getitem__(self, key: str): From 0e79b38006f35523df19024abedbc4a120b7a11f Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sat, 24 Sep 2022 15:05:24 -0700 Subject: [PATCH 14/29] Adding beta command line arguments for tiny-count to CWL and Run Config --- tiny/cwl/tools/tiny-count.cwl | 15 +++++++++++++++ tiny/cwl/workflows/tinyrna_wf.cwl | 8 +++++++- tiny/rna/counter/counter.py | 8 ++++++++ tiny/rna/counter/hts_parsing.py | 9 +++++---- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/tiny/cwl/tools/tiny-count.cwl b/tiny/cwl/tools/tiny-count.cwl index c899854b..b67204c8 100644 --- a/tiny/cwl/tools/tiny-count.cwl +++ b/tiny/cwl/tools/tiny-count.cwl @@ -53,6 +53,21 @@ inputs: inputBinding: prefix: -dc + id_attr: + type: string? + inputBinding: + prefix: -id + + stepvector: + type: string? + inputBinding: + prefix: -sv + + multi_id: + type: boolean? + inputBinding: + prefix: -md + all_features: type: boolean? inputBinding: diff --git a/tiny/cwl/workflows/tinyrna_wf.cwl b/tiny/cwl/workflows/tinyrna_wf.cwl index 4786878f..8aa9ab0e 100644 --- a/tiny/cwl/workflows/tinyrna_wf.cwl +++ b/tiny/cwl/workflows/tinyrna_wf.cwl @@ -82,10 +82,13 @@ inputs: aligned_seqs: File[]? is_pipeline: boolean? counter_diags: boolean? + counter_id_attr: string? counter_decollapse: boolean? + counter_stepvector: string? counter_all_features: boolean? counter_type_filter: string[]? counter_source_filter: string[]? + counter_allow_multi_id: boolean? counter_normalize_by_hits: boolean? # deseq inputs @@ -208,11 +211,14 @@ steps: out_prefix: run_name all_features: counter_all_features source_filter: counter_source_filter + type_filter: counter_type_filter normalize_by_hits: source: counter_normalize_by_hits valueFrom: $(String(self)) # convert boolean -> string decollapse: counter_decollapse - type_filter: counter_type_filter + id_attr: counter_id_attr + multi_id: counter_allow_multi_id + stepvector: counter_stepvector is_pipeline: {default: true} diagnostics: counter_diags fastp_logs: preprocessing/json_report_file diff --git a/tiny/rna/counter/counter.py b/tiny/rna/counter/counter.py index f84fe926..c6979086 100644 --- a/tiny/rna/counter/counter.py +++ b/tiny/rna/counter/counter.py @@ -60,8 +60,16 @@ def get_args(): optional_args.add_argument('-d', '--report-diags', action='store_true', help='Produce diagnostic information about uncounted/eliminated ' 'selection elements.') + + # Beta arguments optional_args.add_argument('-sv', '--step-vector', choices=['Cython', 'HTSeq'], default='Cython', help='Select which StepVector is used for interval -> feature resolution.') + optional_args.add_argument('-id', '--id-attr', metavar='KEY', default="id", + help="Set the column 9 attribute key that should serve as the ID " + "attribute. This is usually 'id' or 'gene_id'.") + optional_args.add_argument('-md', '--multi-id', action='store_true', + help="Don't treat features with multiple ID values as an error. " + "Only the first value will be used as the feature's ID.") args = arg_parser.parse_args() setattr(args, 'normalize_by_hits', args.normalize_by_hits.lower() in ['t', 'true']) diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index a1460fd8..c730c177 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -362,6 +362,8 @@ class ReferenceTables: def __init__(self, gff_files: Dict[str, list], feature_selector, **prefs): self.all_features = prefs.get('all_features', False) + self.multi_id = prefs.get('multi_id', False) + self.id_attr = prefs.get('id_attr', 'ID') self.selector = feature_selector self._set_filters(**prefs) self.gff_files = gff_files @@ -614,15 +616,14 @@ def chrom_vector_setdefault(self, chrom): if chrom not in self.feats.chrom_vectors: self.feats.add_chrom(chrom) - @staticmethod - def get_feature_id(row): - id_collection = row.attr.get("ID", None) + def get_feature_id(self, row): + id_collection = row.attr.get(self.id_attr, None) if id_collection is None: raise ValueError(f"Feature {row.name} does not contain an ID attribute.") if len(id_collection) == 0: raise ValueError("A feature's ID attribute cannot be empty. This value is required.") - if len(id_collection) > 1: + if len(id_collection) > 1 and not self.multi_id: err_msg = "A feature's ID attribute cannot contain multiple values. Only one ID per feature is allowed." raise ValueError(err_msg) From acc23bee6bc56eebeac922f35d6d64f555c03754 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Wed, 28 Sep 2022 17:01:10 -0700 Subject: [PATCH 15/29] Adding a basic native C++ test for the wrapped core of the Cython StepVector --- tests/cython_tests/stepvector/Makefile | 15 ++++++ tests/cython_tests/stepvector/test_core.cpp | 51 +++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100755 tests/cython_tests/stepvector/Makefile create mode 100644 tests/cython_tests/stepvector/test_core.cpp diff --git a/tests/cython_tests/stepvector/Makefile b/tests/cython_tests/stepvector/Makefile new file mode 100755 index 00000000..35b0756d --- /dev/null +++ b/tests/cython_tests/stepvector/Makefile @@ -0,0 +1,15 @@ +CXX := clang++ + +PY_VERS := python3.9 +PY_CFLAGS := $(shell ${PY_VERS}-config --cflags --libs) +PY_LDFLAGS := $(shell ${PY_VERS}-config --ldflags --embed) + +INCLUDE := -I$(realpath ../../../tiny/rna/counter/stepvector/src) +LDFLAGS := ${PY_LDFLAGS} -L${CONDA_PREFIX}/lib -rpath ${CONDA_PREFIX}/lib +CXXFLAGS := -std=c++17 -stdlib=libc++ ${INCLUDE} ${PY_CFLAGS} + +test_core : test_core.cpp + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ + +clean : + @rm -f test_core \ No newline at end of file diff --git a/tests/cython_tests/stepvector/test_core.cpp b/tests/cython_tests/stepvector/test_core.cpp new file mode 100644 index 00000000..895f8f35 --- /dev/null +++ b/tests/cython_tests/stepvector/test_core.cpp @@ -0,0 +1,51 @@ +#include "StepVector.h" +#include "PyRef.h" + +using sparse_vectors::_StepVector; +using PyPtr::PyRef; + +int main(){ + Py_Initialize(); + + _StepVector sv; + + PyObject *c1 = PySet_New(NULL); // {featA, featB} + PyObject *c2 = PySet_New(NULL); // {featC} + PyObject *c3 = PySet_New(NULL); // {featD} + + PyObject *sA = PyUnicode_FromString("featA"); + PyObject *sB = PyUnicode_FromString("featB"); + PyObject *sC = PyUnicode_FromString("featC"); + PyObject *sD = PyUnicode_FromString("featD"); + + PySet_Add(c1, sA); + PySet_Add(c1, sB); + PySet_Add(c2, sC); + PySet_Add(c3, sD); + + PyRef p1 = PyRef(c1); + PyRef p2 = PyRef(c2); + PyRef p3 = PyRef(c3); + + sv.add_value(0, 10, p1); + sv.add_value(8, 15, p2); + sv.add_value(9, 20, p3); + + std::cout << std::endl; + + for (auto it = sv.get_values(-1); it != sv.end(); ++it){ + std::cout << it->first << ": "; + PyObject_Print(it->second.get(), stdout, 0); + std::cout << std::endl; + } + + /* Expect: + -9223372036854775808: set() + 0: {'featA', 'featB'} + 8: {'featA', 'featC', 'featB'} + 9: {'featD', 'featA', 'featC', 'featB'} + 11: {'featD', 'featC'} + 16: {'featD'} + 21: set() + */ +} \ No newline at end of file From faeeeb0e64a15b9f0c9abdd7e22f140846756ecd Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Wed, 28 Sep 2022 17:02:04 -0700 Subject: [PATCH 16/29] Adding a basic Cython test for the StepVector --- tests/cython_tests/__init__.py | 0 tests/cython_tests/stepvector/__init__.py | 0 tests/cython_tests/stepvector/test_cython.pyx | 54 +++++++++++++++++++ tiny/rna/counter/stepvector/test_stepvec.pyx | 32 ----------- 4 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 tests/cython_tests/__init__.py create mode 100644 tests/cython_tests/stepvector/__init__.py create mode 100644 tests/cython_tests/stepvector/test_cython.pyx delete mode 100644 tiny/rna/counter/stepvector/test_stepvec.pyx diff --git a/tests/cython_tests/__init__.py b/tests/cython_tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/cython_tests/stepvector/__init__.py b/tests/cython_tests/stepvector/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/cython_tests/stepvector/test_cython.pyx b/tests/cython_tests/stepvector/test_cython.pyx new file mode 100644 index 00000000..7a7e15c9 --- /dev/null +++ b/tests/cython_tests/stepvector/test_cython.pyx @@ -0,0 +1,54 @@ +# distutils: language = c++ +# cython: language_level = 3 +import sys + +from cpython.ref cimport PyObject +from libcpp.pair cimport pair +from cython.operator cimport dereference as deref, preincrement as inc, postincrement as pinc + +from tiny.rna.counter.stepvector._stepvector cimport _StepVector, PyRef +ctypedef PyObject* PyPtr + +def main(): + cdef PyRef p1, p2, p3 + + sv = new _StepVector[PyRef]() + + c1 = {"featA", "featB"} + c2 = {"featC"} + c3 = {"featD"} + p1 = PyRef(c1) + p2 = PyRef(c2) + p3 = PyRef(c3) + + sv.add_value(0, 10, p1) + sv.add_value(8, 15, p2) + sv.add_value(9, 20, p3) + + it = sv.get_values(-1) + expected = iter([ + ((-1 * sys.maxsize - 1), set()), + (0, {'featA', 'featB'}), + (8, {'featA', 'featB', 'featC'}), + (9, {'featA', 'featB', 'featC', 'featD'}), + (11, {'featC', 'featD'}), + (16, {'featD'}), + (21, set()) + ]) + + while it != sv.end(): + step = deref(pinc(it)) + exp_pos, exp_set = next(expected) + + try: + assert step.first == exp_pos + assert step.second.get() == exp_set + except AssertionError as e: + print(f"Expected {exp_pos}: {exp_set}") + print(f"Received {step.first}: {step.second.get()}") + raise + + print("StepVector basic test passed.") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/tiny/rna/counter/stepvector/test_stepvec.pyx b/tiny/rna/counter/stepvector/test_stepvec.pyx deleted file mode 100644 index fb7a3dcf..00000000 --- a/tiny/rna/counter/stepvector/test_stepvec.pyx +++ /dev/null @@ -1,32 +0,0 @@ -# distutils: language = c++ -# cython: language_level = 3 - -from cpython.ref cimport PyObject -from cython.operator cimport dereference as deref, preincrement as inc, address - -from ._stepvector cimport _StepVector, PyRef -ctypedef PyObject* PyPtr - -def main(): - cdef PyRef r1, r2, r3, c1, c2 - - vctre = new _StepVector[PyRef]() - f1 = {"featA"} - f2 = {"featB", "featC"} - f3 = {"featD"} - r1 = PyRef(f1) - r2 = PyRef(f2) - r3 = PyRef(f3) - - vctre.add_value(11, 99, r1) - vctre.add_value(22, 44, r2) - vctre.add_value(33, 98, r3) - - it = vctre.begin() - while it != vctre.end(): - step = deref(it) - print(f"{ step.first}: { (step.second).get()}") - inc(it) - -if __name__ == '__main__': - main() \ No newline at end of file From c31f82f29d7f18a70437548c23551549b0407f12 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Wed, 28 Sep 2022 17:04:52 -0700 Subject: [PATCH 17/29] Improved Cython-related code in setup.py so that macOS-specific compile args aren't used on other platforms. Also cleaning up some boilerplate code --- setup.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index 6652ec49..d23fdb43 100644 --- a/setup.py +++ b/setup.py @@ -29,26 +29,26 @@ def run(self): def in_conda_env(self): return all([os.getenv(conda_var) for conda_var in ["CONDA_PREFIX", "CONDA_DEFAULT_ENV"]]) -debug_cython = False + +pyx_files = [ + 'tiny/rna/counter/stepvector/_stepvector.pyx', + 'tests/cython_tests/stepvector/test_cython.pyx' +] cxx_extension_args = { - 'extra_compile_args': [ - '-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include', - '-stdlib=libc++', '-std=c++11', - '-O3'], - 'extra_link_args': ['-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'], + 'extra_compile_args': ['-stdlib=libc++', '-std=c++11', '-O3'], + 'extra_link_args': [], 'language': 'c++' } +if sys.platform == "darwin": + cxx_extension_args['extra_compile_args'] += ['-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include'] + cxx_extension_args['extra_link_args'] += ['-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'] cython_extensions = [ setuptools.Extension( - "tiny.rna.counter.stepvector._stepvector", - sources=['tiny/rna/counter/stepvector/_stepvector.pyx'], - **cxx_extension_args - ), - setuptools.Extension( - "tiny.rna.counter.stepvector.test_stepvec", - sources=['tiny/rna/counter/stepvector/test_stepvec.pyx'], + pyx_filename.replace('/', '.').rstrip('.pyx'), + sources=[pyx_filename], **cxx_extension_args ) + for pyx_filename in pyx_files ] setuptools.setup( @@ -73,7 +73,7 @@ def in_conda_env(self): ext_modules=cythonize( cython_extensions, compiler_directives={'language_level': '3'}, - gdb_debug=debug_cython + gdb_debug=False ), scripts=['tiny/rna/tiny-deseq.r'], classifiers=[ From 9deede8916fdf8da7c69cdf61b693565a8ccc948 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Wed, 28 Sep 2022 17:07:57 -0700 Subject: [PATCH 18/29] Debug statements in PyRef have been changed to preprocessor directives. This has the advantage of 1) skipping the condition checks if not built with debugging enabled, and 2) the debug member variable in the PyRef class was bad design, and this allows it to be removed. --- tiny/rna/counter/stepvector/src/PyRef.h | 45 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/tiny/rna/counter/stepvector/src/PyRef.h b/tiny/rna/counter/stepvector/src/PyRef.h index 3458d649..8a89190c 100644 --- a/tiny/rna/counter/stepvector/src/PyRef.h +++ b/tiny/rna/counter/stepvector/src/PyRef.h @@ -2,13 +2,21 @@ #include #include +//#define DEBUG + namespace PyPtr { class PyRef { PyObject* obj; - bool debug = false; public: - PyObject* get() const {return obj;} + PyObject* get() const { + #ifdef DEBUG + std::cout << "Retrieve: "; + PyObject_Print(obj, stdout, 0); + std::cout << " (obj: " << obj << ")" << std::endl; + #endif + return obj; + } // Default constructor PyRef() { @@ -28,15 +36,20 @@ namespace PyPtr { PySet_Add(obj, payload); } - if (debug){ + #ifdef DEBUG std::cout << "Object ctor: "; PyObject_Print(payload, stdout, 0); std::cout << " (obj: " << obj << ")" << std::endl; - } + #endif } // Destructor ~PyRef() { + #ifdef DEBUG + std::cout << "Object destructor: "; + PyObject_Print(obj, stdout, 0); + std::cout << " (obj: " << obj << ")" << std::endl; + #endif Py_XDECREF(obj); } @@ -44,22 +57,22 @@ namespace PyPtr { PyRef(const PyRef& other) { obj = PySet_New(other.obj); - if (debug) { + #ifdef DEBUG std::cout << "Copy ctor: "; PyObject_Print(other.obj, stdout, 0); std::cout << " (obj: " << other.obj << " -> " << obj << ")"; std::cout << std::endl << std::flush; - } + #endif } // Move constructor PyRef(PyRef&& other) { - if (debug) { + #ifdef DEBUG std::cout << "Move ctor: "; PyObject_Print(other.obj, stdout, 0); std::cout << " (obj: " << other.obj << ")"; std::cout << std::endl << std::flush; - } + #endif obj = other.obj; Py_XDECREF(other.obj); @@ -67,12 +80,12 @@ namespace PyPtr { // Assignment PyRef& operator=(const PyRef& other) { - if (debug) { + #ifdef DEBUG std::cout << "Assignment: "; PyObject_Print(other.obj, stdout, 0); std::cout << " (obj: " << other.obj << ")"; std::cout << std::endl << std::flush; - } + #endif Py_XDECREF(obj); obj = PySet_New(other.obj); @@ -81,12 +94,12 @@ namespace PyPtr { // Move assignment PyRef& operator=(PyRef&& other) { - if (debug) { + #ifdef DEBUG std::cout << "Move assignment: "; PyObject_Print(other.obj, stdout, 0); std::cout << " (obj: " << other.obj << ")"; std::cout << std::endl << std::flush; - } + #endif Py_XDECREF(obj); obj = other.obj; @@ -96,9 +109,9 @@ namespace PyPtr { } PyRef& operator+= (const PyRef& other) { - if (debug) { + #ifdef DEBUG std::cout << "+=" << std::endl; - } + #endif if (PySet_Check(other.obj)){ _PySet_Update(obj, PyObject_GetIter(other.obj)); @@ -109,12 +122,12 @@ namespace PyPtr { } PyRef operator+ (const PyRef& rhs) { - if (debug){ + #ifdef DEBUG PyObject_Print(obj, stdout, 0); std::cout << " + "; PyObject_Print(rhs.obj, stdout, 0); std::cout << std::endl; - } + #endif PyRef result(obj); if (PySet_Check(rhs.obj)){ From 20086b2bdcba5ba5d4ed433ff6c097c1b55b21e8 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Wed, 28 Sep 2022 17:08:32 -0700 Subject: [PATCH 19/29] Small performance improvements --- tiny/rna/counter/features.py | 15 ++++++--------- tiny/rna/counter/hts_parsing.py | 7 ++----- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/tiny/rna/counter/features.py b/tiny/rna/counter/features.py index a322587e..22bb7038 100644 --- a/tiny/rna/counter/features.py +++ b/tiny/rna/counter/features.py @@ -40,20 +40,17 @@ def __init__(self, gff_file_set, selection_rules, **prefs): def assign_features(self, al: dict) -> Tuple[dict, int]: """Determines features associated with the interval then performs rule-based feature selection""" - feat_matches, assignment = set(), {} - try: - feat_matches = feat_matches.union(*( - Features.chrom_vectors[al['Chrom']]['.'] # GenomicArrayOfSets -> ChromVector - .array[al['Start']:al['End']] # ChromVector -> StepVector - .get_steps(values_only=True))) # StepVector -> {features} + feat_matches = set().union( + *Features.chrom_vectors[al['Chrom']]['.'] # GenomicArrayOfSets -> ChromVector + .array[al['Start']:al['End']] # ChromVector -> StepVector + .get_steps(values_only=True)) # StepVector -> {features} except KeyError as ke: self.stats.chrom_misses[ke.args[0]] += 1 + return {}, 0 # If features are associated with the alignment interval, perform selection - if len(feat_matches): - assignment = self.selector.choose(feat_matches, al) - + assignment = self.selector.choose(feat_matches, al) if feat_matches else {} return assignment, len(feat_matches) def count_reads(self, library: dict): diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index d938cc03..c974d816 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -268,18 +268,15 @@ def parse_GFF_attribute_string(attrStr, extra_return_first_value=False, gff_vers ID." """ - if attrStr.endswith("\n"): - attrStr = attrStr[:-1] - # Modification: store attributes in a dict subclass that allows case-insensitive ops attribute_dict = CaseInsensitiveAttrs() first_val = "_unnamed_" if gff_version == 2: - iterator = HTSeq._HTSeq.quotesafe_split(attrStr.encode()) + iterator = HTSeq._HTSeq.quotesafe_split(attrStr.rstrip().encode()) else: # GFF3 does not care about quotes - iterator = attrStr.encode().split(b';') + iterator = attrStr.rstrip().encode().split(b';') for i, attr in enumerate(iterator): attr = attr.decode() From db3df05a3df172b9ba78e722514536ecedb07d81 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Wed, 28 Sep 2022 17:28:00 -0700 Subject: [PATCH 20/29] Adding a very basic check to make sure that the Cython StepVector has been built on the user's machine. If not, tiny-count produces an error and exists. This should only be relevant if the user is running tinyRNA from the project directory, so the error message has been written accordingly --- tiny/rna/counter/hts_parsing.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index c974d816..631f39aa 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -448,9 +448,15 @@ def __init__(self, gff_files: Dict[str, list], feature_selector, **prefs): self.gff_files = gff_files # ----------------------------------------------------------- Primary Key: if prefs['step_vector'] == 'Cython': - from tiny.rna.counter.stepvector import StepVector - setattr(HTSeq.StepVector, 'StepVector', StepVector) - self.feats = HTSeq.GenomicArray("auto", stranded=False) # Root Match ID + try: + from tiny.rna.counter.stepvector import StepVector + setattr(HTSeq.StepVector, 'StepVector', StepVector) + self.feats = HTSeq.GenomicArray("auto", stranded=False) # Root Match ID + except ModuleNotFoundError: + print("The Cython StepVector has not yet been built.\n" + "Run: python setup.py build_ext --inplace", + file=sys.stderr) + sys.exit(1) else: self.feats = HTSeq.GenomicArrayOfSets("auto", stranded=False) # Root Match ID From 737a33335e7c7b2ecb3a6f6f5510521edf074c93 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Wed, 28 Sep 2022 18:05:30 -0700 Subject: [PATCH 21/29] Removing the id_attr beta option from tiny-count. Since multiple annotation files can be used for counting and they may be heterogenous in type, this option will need to be defined for each file if we decide to include it. ReferenceTables has been changed to also use "gene_id" as a feature's ID attribute. "ID" is still considered the primary attribute if both are present. This should make things easier for users with GFF2/GTF files --- START_HERE/run_config.yml | 7 +++++++ tiny/cwl/tools/tiny-count.cwl | 5 ----- tiny/cwl/workflows/tinyrna_wf.cwl | 2 -- tiny/rna/counter/counter.py | 3 --- tiny/rna/counter/hts_parsing.py | 7 +++---- tiny/templates/run_config_template.yml | 7 +++++++ 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/START_HERE/run_config.yml b/START_HERE/run_config.yml index ea9d4aa3..9de0e9cc 100644 --- a/START_HERE/run_config.yml +++ b/START_HERE/run_config.yml @@ -244,6 +244,13 @@ counter_source_filter: [] ##-- If source AND type filters are given, a line must match both to be included --## counter_type_filter: [] +##-- Select the StepVector implementation that is used. Options: HTSeq or Cython --## +counter_stepvector: 'Cython' + +##-- If False: a feature with multiple values in its ID attribute is treated as an error --## +##-- If True: multiple ID values are allowed, but only the first value is used --## +counter_allow_multi_id: True + ##-- If True: produce diagnostic logs to indicate what was eliminated and why --## counter_diags: False diff --git a/tiny/cwl/tools/tiny-count.cwl b/tiny/cwl/tools/tiny-count.cwl index 273275d4..cfaf77b4 100644 --- a/tiny/cwl/tools/tiny-count.cwl +++ b/tiny/cwl/tools/tiny-count.cwl @@ -53,11 +53,6 @@ inputs: inputBinding: prefix: -dc - id_attr: - type: string? - inputBinding: - prefix: -id - stepvector: type: string? inputBinding: diff --git a/tiny/cwl/workflows/tinyrna_wf.cwl b/tiny/cwl/workflows/tinyrna_wf.cwl index abc9fe4b..ccd59926 100644 --- a/tiny/cwl/workflows/tinyrna_wf.cwl +++ b/tiny/cwl/workflows/tinyrna_wf.cwl @@ -82,7 +82,6 @@ inputs: aligned_seqs: File[]? is_pipeline: boolean? counter_diags: boolean? - counter_id_attr: string? counter_decollapse: boolean? counter_stepvector: string? counter_all_features: boolean? @@ -216,7 +215,6 @@ steps: source: counter_normalize_by_hits valueFrom: $(String(self)) # convert boolean -> string decollapse: counter_decollapse - id_attr: counter_id_attr multi_id: counter_allow_multi_id stepvector: counter_stepvector is_pipeline: {default: true} diff --git a/tiny/rna/counter/counter.py b/tiny/rna/counter/counter.py index e6c6a733..195d3ce5 100644 --- a/tiny/rna/counter/counter.py +++ b/tiny/rna/counter/counter.py @@ -64,9 +64,6 @@ def get_args(): # Beta arguments optional_args.add_argument('-sv', '--step-vector', choices=['Cython', 'HTSeq'], default='Cython', help='Select which StepVector is used for interval -> feature resolution.') - optional_args.add_argument('-id', '--id-attr', metavar='KEY', default="id", - help="Set the column 9 attribute key that should serve as the ID " - "attribute. This is usually 'id' or 'gene_id'.") optional_args.add_argument('-md', '--multi-id', action='store_true', help="Don't treat features with multiple ID values as an error. " "Only the first value will be used as the feature's ID.") diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index 631f39aa..533983bf 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -441,8 +441,7 @@ class ReferenceTables: def __init__(self, gff_files: Dict[str, list], feature_selector, **prefs): self.all_features = prefs.get('all_features', False) - self.multi_id = prefs.get('multi_id', False) - self.id_attr = prefs.get('id_attr', 'ID') + self.allow_multi_id = prefs.get('multi_id', False) self.selector = feature_selector self._set_filters(**prefs) self.gff_files = gff_files @@ -702,13 +701,13 @@ def chrom_vector_setdefault(self, chrom): self.feats.add_chrom(chrom) def get_feature_id(self, row): - id_collection = row.attr.get(self.id_attr, None) + id_collection = row.attr.get('ID', row.attr.get('gene_id', None)) if id_collection is None: raise ValueError(f"Feature {row.name} does not contain an ID attribute.") if len(id_collection) == 0: raise ValueError("A feature's ID attribute cannot be empty. This value is required.") - if len(id_collection) > 1 and not self.multi_id: + if len(id_collection) > 1 and not self.allow_multi_id: err_msg = "A feature's ID attribute cannot contain multiple values. Only one ID per feature is allowed." raise ValueError(err_msg) diff --git a/tiny/templates/run_config_template.yml b/tiny/templates/run_config_template.yml index a1ec8610..fd6f1817 100644 --- a/tiny/templates/run_config_template.yml +++ b/tiny/templates/run_config_template.yml @@ -244,6 +244,13 @@ counter_source_filter: [] ##-- If source AND type filters are given, a line must match both to be included --## counter_type_filter: [] +##-- Select the StepVector implementation that is used. Options: HTSeq or Cython --## +counter_stepvector: 'Cython' + +##-- If False: a feature with multiple values in its ID attribute is treated as an error --## +##-- If True: multiple ID values are allowed, but only the first value is used --## +counter_allow_multi_id: True + ##-- If True: produce diagnostic logs to indicate what was eliminated and why --## counter_diags: False From ae88eb6823b3b7c6c6f03fc25fd7891ac105a2db Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Wed, 28 Sep 2022 19:03:15 -0700 Subject: [PATCH 22/29] Excluding the tests directory from installation since the compiled Cython under tests/cython_tests/stepvector would otherwise cause it to be included. --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index fc4c9ca0..906f1d82 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include README.md +exclude tests graft tiny prune **/__pycache__ prune **/*_Outputs From 3d2a851a8fce4545f74935c9e5aa9d86c417d93a Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sat, 1 Oct 2022 16:03:00 -0700 Subject: [PATCH 23/29] Polished the new command line argument helpstrings and updated documentation --- doc/Parameters.md | 28 ++++++++++++++++++++++++---- tiny/rna/counter/counter.py | 13 ++++++------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/doc/Parameters.md b/doc/Parameters.md index aec57919..d870c454 100644 --- a/doc/Parameters.md +++ b/doc/Parameters.md @@ -63,14 +63,14 @@ Optional arguments: ## tiny-count -### All-Features +### All Features | Run Config Key | Commandline Argument | |------------------------|------------------------| | counter_all_features: | `--all-features` | By default, tiny-count will only evaluate alignments to features which match a `Select for...` & `with value...` of at least one rule in your Features Sheet. It is this matching feature set, and only this set, which is included in `feature_counts.csv` and therefore available for analysis by tiny-deseq.r and tiny-plot. Switching this option "on" will include all features in every input GFF file, regardless of attribute matches, for tiny-count and downstream steps. -### Normalize-by-Hits +### Normalize by Hits | Run Config Key | Commandline Argument | |----------------------------|---------------------------| | counter-normalize-by-hits: | `--normalize-by-hits T/F` | @@ -92,6 +92,20 @@ The SAM files produced by the tinyRNA pipeline are collapsed by default; alignme You can optionally filter features in your GFF files by specifying sources and/or types that are desired. Source and type refer to GFF columns 2 and 3 respectively. If source _and_ type filters are specified, each feature must match one of the sources _and_ one of the types in order to be included in the counting process. For both filters, an empty list is the same as "allow all." +### StepVector + | Run Config Key | Commandline Argument | +|--------------------|----------------------| +| counter_stepvector | `--step-vector` | + +A custom Cython implementation of HTSeq's StepVector is used for finding features that overlap each alignment interval. While the core C++ component of the StepVector is the same, we have found that our Cython implementation can result in runtimes up to 50% faster than HTSeq's implementation. This parameter allows you to use HTSeq's StepVector if you wish (for example, if the Cython StepVector is incompatible with your system) + +### Allow Features with Multiple ID Values + | Run Config Key | Commandline Argument | +|------------------------|----------------------| +| counter_allow_multi_id | `--multi-id` | + +By default, an error will be produced if a GFF file contains a feature with multiple comma separated values listed under its ID attribute. Switching this option "on" instructs tiny-count to accept these features without error, but only the first listed value is used as the ID. + ### Is Pipeline | Run Config Key | Commandline Argument | |----------------|----------------------| @@ -127,10 +141,10 @@ Required arguments: Optional arguments: -h, --help show this help message and exit - -sf [SOURCE [SOURCE ...]], --source-filter [SOURCE [SOURCE ...]] + -sf [SOURCE ...], --source-filter [SOURCE ...] Only produce counts for features whose GFF column 2 matches the source(s) listed - -tf [TYPE [TYPE ...]], --type-filter [TYPE [TYPE ...]] + -tf [TYPE ...], --type-filter [TYPE ...] Only produce counts for features whose GFF column 3 matches the type(s) listed -nh T/F, --normalize-by-hits T/F @@ -139,6 +153,12 @@ Optional arguments: -dc, --decollapse Create a decollapsed copy of all SAM files listed in your Samples Sheet. This option is ignored for non- collapsed inputs. + -sv {Cython,HTSeq}, --step-vector {Cython,HTSeq} + Select which StepVector implementation is used to find + features overlapping an interval. + -md, --multi-id Don't treat features with multiple ID values as an + error. Only the first value will be used as the + feature's ID. -a, --all-features Represent all features in output counts table, even if they did not match a Select for / with value. -p, --is-pipeline Indicates that tiny-count was invoked as part of a diff --git a/tiny/rna/counter/counter.py b/tiny/rna/counter/counter.py index 195d3ce5..37329760 100644 --- a/tiny/rna/counter/counter.py +++ b/tiny/rna/counter/counter.py @@ -51,6 +51,12 @@ def get_args(): optional_args.add_argument('-dc', '--decollapse', action='store_true', help='Create a decollapsed copy of all SAM files listed in your ' 'Samples Sheet. This option is ignored for non-collapsed inputs.') + optional_args.add_argument('-sv', '--step-vector', choices=['Cython', 'HTSeq'], default='Cython', + help='Select which StepVector implementation is used to find ' + 'features overlapping an interval.') + optional_args.add_argument('-md', '--multi-id', action='store_true', + help="Don't treat features with multiple ID values as an error. " + "Only the first value will be used as the feature's ID.") optional_args.add_argument('-a', '--all-features', action='store_true', help='Represent all features in output counts table, ' 'even if they did not match a Select for / with value.') @@ -61,13 +67,6 @@ def get_args(): help='Produce diagnostic information about uncounted/eliminated ' 'selection elements.') - # Beta arguments - optional_args.add_argument('-sv', '--step-vector', choices=['Cython', 'HTSeq'], default='Cython', - help='Select which StepVector is used for interval -> feature resolution.') - optional_args.add_argument('-md', '--multi-id', action='store_true', - help="Don't treat features with multiple ID values as an error. " - "Only the first value will be used as the feature's ID.") - args = arg_parser.parse_args() setattr(args, 'normalize_by_hits', args.normalize_by_hits.lower() in ['t', 'true']) From d77f910a2a971e11a0674e886177cd5ea9ee19a5 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sat, 1 Oct 2022 16:17:37 -0700 Subject: [PATCH 24/29] Updated environment.yml to include Cython, and updated the lockfiles from its contents. Since subdependencies aren't version locked, these changes include many, many updates that have nothing to do with Cython. Testing shows expected outputs on macOS and Linux using these lockfiles. Unfortunately I don't have a better solution for this at the moment. --- conda/conda-linux-64.lock | 242 +++++++++++++++++++------------------- conda/conda-osx-64.lock | 225 ++++++++++++++++++----------------- conda/environment.yml | 1 + 3 files changed, 233 insertions(+), 235 deletions(-) diff --git a/conda/conda-linux-64.lock b/conda/conda-linux-64.lock index df6acb44..88ba3f65 100644 --- a/conda/conda-linux-64.lock +++ b/conda/conda-linux-64.lock @@ -1,23 +1,22 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: d9433cf548826db3a4d918dce3ebcfa5793ad683ff22014c94c1796381c86f17 +# input_hash: ef26dba2670179c6605442a613aa0567cc43b352efe64eaefbb5399ddc41e110 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2#19f9db5f4f1b7f5ef5f6d67207f25f38 -https://conda.anaconda.org/main/linux-64/ca-certificates-2022.07.19-h06a4308_0.tar.bz2#ef4b29ea120fdfa542adbce43d035c95 +https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2022.9.24-ha878542_0.tar.bz2#41e4e87062433e283696cf384f952ef6 https://conda.anaconda.org/main/noarch/font-ttf-dejavu-sans-mono-2.37-hd3eb1b0_0.tar.bz2#801b4743a301a4e11cbd9609d418e101 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/main/noarch/font-ttf-ubuntu-0.83-h8b1ccd4_0.tar.bz2#0b017158a7cd7c1683e00b4ae688dce4 https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-2.6.32-he073ed8_15.tar.bz2#5dd5127afd710f91f6a75821bac0a4f0 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.36.1-hea4e1c9_2.tar.bz2#bd4f2e711b39af170e7ff15163fe87ee -https://conda.anaconda.org/conda-forge/linux-64/libgcc-devel_linux-64-9.4.0-hd854feb_16.tar.bz2#49d4af017741e08423d1009cb547f80e -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-3.0.0-1.tar.bz2#d7c7e92a8ccc518709474dd3eda896b9 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-devel_linux-64-9.5.0-h367e8d2_16.tar.bz2#b2f784aca08d3e45288c8ea6c7602b86 https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-12.1.0-hdcd56e2_16.tar.bz2#b02605b875559ff99f04351fd5040760 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-devel_linux-64-9.4.0-hd854feb_16.tar.bz2#d1421e969cb4f032c0a17da76cf0a375 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-devel_linux-64-9.5.0-h367e8d2_16.tar.bz2#e5cfd476f10829efccf955635377cfba https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-12.1.0-ha89aaad_16.tar.bz2#6f5ba041a41eb102a1027d9e68731be7 https://conda.anaconda.org/conda-forge/linux-64/mscorefonts-0.0.1-2.tar.bz2#c9fd57fc9f2c51ff7b049b64fba8a7df -https://conda.anaconda.org/main/noarch/tzdata-2022a-hda174b7_0.tar.bz2#a99750e9650510d276b56d2b000f4569 +https://conda.anaconda.org/conda-forge/noarch/tzdata-2022d-h191b570_0.tar.bz2#456b5b1d99e7a9654b331bcd82e71042 https://conda.anaconda.org/main/noarch/fonts-anaconda-1-h8fa9717_0.tar.bz2#7a445a2d9fbe2a88de8f09e9527f4ce1 https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-12.1.0-h69a702a_16.tar.bz2#6bf15e29a20f614b18ae89368260d0a2 https://conda.anaconda.org/conda-forge/linux-64/libgomp-12.1.0-h8d9b700_16.tar.bz2#f013cf7749536ce43d82afbffdf499ab @@ -28,10 +27,9 @@ https://conda.anaconda.org/main/noarch/fonts-conda-ecosystem-1-hd3eb1b0_0.tar.bz https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.36-hf3e587d_10.tar.bz2#9d5cdbfe24b182d4c749b86d500ac9d2 https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-12.1.0-h8d9b700_16.tar.bz2#4f05bc9844f7c101e6e147dab3c88d5c https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.3.2-h166bdaf_0.tar.bz2#b7607b7b62dce55c194ad84f99464e5f -https://conda.anaconda.org/main/linux-64/brotli-1.0.9-he6710b0_2.tar.bz2#bdc971e2a9affb5125b68ad708172dab https://conda.anaconda.org/main/linux-64/bzip2-1.0.8-h7b6447c_0.tar.bz2#f52e60deb7f4c82821be9a868e889348 https://conda.anaconda.org/main/linux-64/c-ares-1.18.1-h7f8727e_0.tar.bz2#62ca289a9704ed465d3324295ef851d9 -https://conda.anaconda.org/conda-forge/linux-64/expat-2.4.8-h27087fc_0.tar.bz2#e1b07832504eeba765d648389cc387a9 +https://conda.anaconda.org/main/linux-64/expat-2.4.9-h6a678d5_0.tar.bz2#d254ca6cf5c657cecd6912b0671379cd https://conda.anaconda.org/main/linux-64/fribidi-1.0.10-h7b6447c_0.tar.bz2#65d93572c4ff2ee0cc8241221aee7f12 https://conda.anaconda.org/main/linux-64/giflib-5.2.1-h7b6447c_0.tar.bz2#299bf4271d710651a1d71d3795580c2d https://conda.anaconda.org/main/linux-64/graphite2-1.3.14-h295c915_1.tar.bz2#09fcddfe703ce76b4b2d7dd0b6d2f928 @@ -40,52 +38,53 @@ https://conda.anaconda.org/conda-forge/linux-64/icu-68.2-h9c3ff4c_0.tar.bz2#6618 https://conda.anaconda.org/conda-forge/linux-64/isa-l-2.30.0-ha770c72_4.tar.bz2#e9f011f428f6a0bba8a2613d7c107b88 https://conda.anaconda.org/main/linux-64/jpeg-9e-h7f8727e_0.tar.bz2#6e1c0fb5260c590f7ef5235cb3d05863 https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3 -https://conda.anaconda.org/main/linux-64/lerc-3.0-h295c915_0.tar.bz2#88199c75f2ac211728febced7785c24e -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.10-h7f98852_0.tar.bz2#ffa3a757a97e851293909b49f49f28fb +https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f +https://conda.anaconda.org/main/linux-64/libbrotlicommon-1.0.9-h5eee18b_7.tar.bz2#787927eff72e62c270f614cbcba9479c +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.13-h166bdaf_0.tar.bz2#4b5bee2e957570197327d0b20a718891 https://conda.anaconda.org/main/linux-64/libev-4.33-h7f8727e_1.tar.bz2#eeca774c6154c49340dd403b1928d5e9 -https://conda.anaconda.org/main/linux-64/libffi-3.4.2-h295c915_4.tar.bz2#9420f780186158fa096601855f2f2b92 -https://conda.anaconda.org/main/linux-64/libiconv-1.16-h7f8727e_2.tar.bz2#a8345526ee5452b2c8de4c5ac5b1c05f +https://conda.anaconda.org/main/linux-64/libffi-3.3-he6710b0_2.tar.bz2#822b5201dde61bc838072dc5b670c88c +https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-h166bdaf_0.tar.bz2#b62b52da46c39ee2bc3c162ac7f1804d https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-h7f98852_0.tar.bz2#39b1328babf85c7c3a61636d9cd50206 https://conda.anaconda.org/main/linux-64/libogg-1.3.5-h27cfd23_1.tar.bz2#48ff183e45bf81c02fa5f9ce1ad669dc -https://conda.anaconda.org/main/linux-64/libopenblas-0.3.20-h043d6bf_1.tar.bz2#61f860d152bd9b7dd57e2ec4dabbdeaf +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.21-pthreads_h78a6416_3.tar.bz2#8c5963a49b6035c40646a763293fbb35 https://conda.anaconda.org/main/linux-64/libopus-1.3.1-h7b6447c_0.tar.bz2#049062fe7765952d7db02925f43e8fe5 -https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-9.4.0-h79bfe98_16.tar.bz2#3082a94f07235397be398173d6d81d79 +https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-9.5.0-hf86b28c_16.tar.bz2#ec3fd069cef36cc9d3a8ad258e7fe3ed https://conda.anaconda.org/main/linux-64/libtool-2.4.6-h295c915_1008.tar.bz2#501f0b2e570c92def56f3f3290b886d2 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d https://conda.anaconda.org/main/linux-64/libuv-1.40.0-h7b6447c_0.tar.bz2#440323c587792bbd97e2b3e62e214176 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.3-h166bdaf_2.tar.bz2#99c0160c84e61348aa8eb2949b24e6d3 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.4-h166bdaf_0.tar.bz2#ac2ccf7323d21f2994e4d1f5da664f37 https://conda.anaconda.org/main/linux-64/libxcb-1.15-h7f8727e_0.tar.bz2#49e523de8d364b7fabc3850eccbe9bda -https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.12-h166bdaf_2.tar.bz2#8302381297332ea50532cf2c67961080 +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.12-h166bdaf_3.tar.bz2#29b2d63b0e21b765da0418bc452538c9 https://conda.anaconda.org/main/linux-64/lz4-c-1.9.3-h295c915_1.tar.bz2#f35d4bf2fbb39e334992f6dd39f8721e https://conda.anaconda.org/conda-forge/linux-64/make-4.3-hd18ef5c_1.tar.bz2#4049ebfd3190b580dffe76daed26155a https://conda.anaconda.org/main/linux-64/ncurses-6.3-h5eee18b_3.tar.bz2#c5ca7fea42593496aa508ba8171f53fb https://conda.anaconda.org/conda-forge/linux-64/ninja-1.11.0-h924138e_0.tar.bz2#18c563c26253a21c1aa9d662e874b0cd https://conda.anaconda.org/main/linux-64/nspr-4.33-h295c915_0.tar.bz2#28808460055d2c42c58edd8639ce530c -https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.4-ha44fe06_0.tar.bz2#085a6a921d41e7f7caa21ad0ca33490f https://conda.anaconda.org/main/linux-64/openssl-1.1.1q-h7f8727e_0.tar.bz2#ad51928702694e3f6d25b7d4229c84e6 https://conda.anaconda.org/main/linux-64/pcre-8.45-h295c915_0.tar.bz2#4bac8a874e62f41ebab8345ca6076eb6 https://conda.anaconda.org/main/linux-64/pixman-0.40.0-h7f8727e_1.tar.bz2#c197093127a4cca3bb3227aab1a68573 -https://conda.anaconda.org/main/linux-64/pkg-config-0.29.2-h1bed415_8.tar.bz2#b3165258ae86c7e235a8a171eda1c0a9 -https://conda.anaconda.org/main/linux-64/tbb-2020.3-hfd86e86_0.tar.bz2#925e0f059d35762aaee4d4ae28c9f6b7 +https://conda.anaconda.org/conda-forge/linux-64/tbb-2021.6.0-h924138e_0.tar.bz2#d2f40df5cf33c6885428d0d33b8ea3c8 https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.0.10-h7f98852_0.tar.bz2#d6b0b50b49eccfe0be0373be628be0f3 https://conda.anaconda.org/main/linux-64/xorg-xproto-7.0.31-h27cfd23_1007.tar.bz2#7638aa708e96e15149bb713633c2cdf7 -https://conda.anaconda.org/main/linux-64/xz-5.2.5-h7f8727e_1.tar.bz2#94cb94dc47405b24b1b5bd0a3275ab59 -https://conda.anaconda.org/main/linux-64/zlib-1.2.12-h7f8727e_2.tar.bz2#601d9449c280b9b145dc8c83b6f06119 -https://conda.anaconda.org/conda-forge/linux-64/blas-1.1-openblas.tar.bz2#c259e7a3ea775c6dcba191072cab3adb -https://conda.anaconda.org/bioconda/linux-64/fastp-0.23.2-hb7a2d85_2.tar.bz2#f0918ffac216d8b49bd440911d66965c -https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-9.4.0-h03d3576_16.tar.bz2#e46c9e254375d427655b6881886b87ef -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-15_linux64_openblas.tar.bz2#04eb983975a1be3e57d6d667414cd774 +https://conda.anaconda.org/main/linux-64/xz-5.2.6-h5eee18b_0.tar.bz2#82ef85822b3b1deca12197d9db565aee +https://conda.anaconda.org/main/linux-64/zlib-1.2.12-h5eee18b_3.tar.bz2#e3aea5293bc691638d2de998a6dff9f8 +https://conda.anaconda.org/bioconda/linux-64/fastp-0.23.2-h5f740d0_3.tar.bz2#001589355bded3e913b0c7740d39ac10 +https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-9.5.0-h6c5bc03_16.tar.bz2#5be4a0800b96a0cbfcac8b56e2b1177e +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-16_linux64_openblas.tar.bz2#d9b7a8639171f6c6fa0a983edabcfe2b +https://conda.anaconda.org/main/linux-64/libbrotlidec-1.0.9-h5eee18b_7.tar.bz2#c0608d376a30b5aa4d2f2c22978135db +https://conda.anaconda.org/main/linux-64/libbrotlienc-1.0.9-h5eee18b_7.tar.bz2#cc6a6d362912a2d112310bd3f8acd44e https://conda.anaconda.org/main/linux-64/libedit-3.1.20210910-h7f8727e_0.tar.bz2#5f295795bb8e419eacb4d6ffbba68ceb https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.10-h9b69904_4.tar.bz2#390026683aef81db27ff1b8570ca1336 -https://conda.anaconda.org/main/linux-64/libllvm11-11.1.0-h3826bc1_1.tar.bz2#c261302f1b58f6b2851f856d230dc947 -https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-h727a467_0.tar.bz2#a22567abfea169ff8048506b1ca9b230 -https://conda.anaconda.org/main/linux-64/libpng-1.6.37-hbc83047_0.tar.bz2#9cb85601944ca7383b1769bff74b94e8 +https://conda.anaconda.org/main/linux-64/libllvm11-11.1.0-h9e868ea_5.tar.bz2#12707b0bf5a1096840b088925de5e27d +https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-hdcd2b5c_1.tar.bz2#6fe9e31c2b8d0b022626ccac13e6ca3c +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.38-h753d276_0.tar.bz2#575078de1d3a3114b3ce131bd1508d0c https://conda.anaconda.org/main/linux-64/libssh2-1.10.0-h8f2d780_0.tar.bz2#f99f13daacf379a329e041ec148c4db7 https://conda.anaconda.org/main/linux-64/libvorbis-1.3.7-h7b6447c_0.tar.bz2#e2260ef2263c0747bb17dc3622620928 https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h72842e0_0.tar.bz2#bd14fdf5b9ee5568056a40a6a2f41866 -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.29-haf5c9bc_1.tar.bz2#c01640c8bad562720d6caff0402dbd96 +https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.30-haf5c9bc_1.tar.bz2#62b588b2a313ac3d9c2ead767baa3b5d https://conda.anaconda.org/main/linux-64/nodejs-16.13.1-hb931c9a_0.tar.bz2#05d36c77b57cfdb28928728874e4aa04 +https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.21-pthreads_h320a7e8_3.tar.bz2#29155b9196b9d78022f11d86733e25a7 https://conda.anaconda.org/main/linux-64/pcre2-10.37-he7ceb23_1.tar.bz2#e4045830821b30220cde347dff25e772 https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-2_h7f98852_perl5.tar.bz2#09ba115862623f00962e9809ea248f1a https://conda.anaconda.org/main/linux-64/readline-8.1.2-h7f8727e_1.tar.bz2#150ac0eb929bab9dec912797bdfc7b95 @@ -93,71 +92,78 @@ https://conda.anaconda.org/main/linux-64/tk-8.6.12-h1ccaba5_0.tar.bz2#5b8375e209 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-hd9c2040_1000.tar.bz2#9e856f78d5c80d5a78f61e72d1d473a3 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.7.2-h7f98852_0.tar.bz2#12a61e640b8894504326aadafccbb790 https://conda.anaconda.org/main/linux-64/zstd-1.5.2-ha4553b6_0.tar.bz2#b1737dfd2e06ad69ec5cfec341e207c6 +https://conda.anaconda.org/conda-forge/linux-64/blas-1.1-openblas.tar.bz2#c259e7a3ea775c6dcba191072cab3adb +https://conda.anaconda.org/main/linux-64/brotli-bin-1.0.9-h5eee18b_7.tar.bz2#d9d570587ccfd7158b66feb823c990c6 https://conda.anaconda.org/conda-forge/linux-64/bwidget-1.9.14-ha770c72_1.tar.bz2#5746d6202ba2abad4a4707f2a2462795 -https://conda.anaconda.org/main/linux-64/freetype-2.11.0-h70c0345_0.tar.bz2#d2b24736491290a6c6d0138932111150 -https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-9.4.0-h391b98a_10.tar.bz2#74b03e7cf35c6315f905b730ffbcf1c7 +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-hca18f0e_0.tar.bz2#4e54cbfc47b8c74c2ecc1e7730d8edce +https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-9.5.0-h4258300_10.tar.bz2#897012edfc84de8c62de3d29a5c6bc9b https://conda.anaconda.org/main/linux-64/gettext-0.21.0-hf68c758_0.tar.bz2#7b50d8014e3ffd9258944e356c2fec93 -https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-9.4.0-h0003116_16.tar.bz2#a464df4d5a4fbe43e347923d6221ef84 -https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-9.4.0-h03d3576_16.tar.bz2#98494038827f8906f3029b3db94930ff +https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-9.5.0-h3c9b8b6_16.tar.bz2#794900c13250baa8cd3404d70606cfb0 +https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-9.5.0-h6c5bc03_16.tar.bz2#49d1d0e8f97e628be5b448fe71cd4f82 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.19.3-h3790be6_0.tar.bz2#7d862b05445123144bec92cb1acc8ef8 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-15_linux64_openblas.tar.bz2#f45968428e445fd0c6472b561145812a https://conda.anaconda.org/conda-forge/linux-64/libclang-11.1.0-default_ha53f305_1.tar.bz2#b9b71585ca4fcb5d442c5a9df5dd7e98 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-15_linux64_openblas.tar.bz2#b7078220384b8bf8db1a45e66412ac4f -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.4.0-h0fcbabc_0.tar.bz2#3c343938f969d6434bb648119d576c0f +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-16_linux64_openblas.tar.bz2#955d993f41f9354bf753d29864ea20ad +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.4.0-h0e0dad5_3.tar.bz2#5627d42c13a9b117ae1701c6e195624f https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.33-h15afd5d_2.tar.bz2#811bb9f5437fc6a04ba30d10f7a0c3eb -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.29-h28c427c_1.tar.bz2#36dbdbf505b131c7e79a3857f3537185 -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.39.2-h4ff8645_0.tar.bz2#2cf5cb4cd116a78e639977eb61ad9987 +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.30-h28c427c_1.tar.bz2#0bd292db365c83624316efc2764d9f16 +https://conda.anaconda.org/main/linux-64/sqlite-3.39.3-h5082296_0.tar.bz2#34fa0120fc3c9719213dbe2eb443e63d https://conda.anaconda.org/main/linux-64/tktable-2.10-h14c3975_0.tar.bz2#6814c3b5b7f94675eddb272a9c2496ca https://conda.anaconda.org/conda-forge/linux-64/xorg-libxt-1.2.1-h7f98852_2.tar.bz2#60d6eec5273f1c9af096c10c268912e3 -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.0-h8e229c2_0.tar.bz2#f314f79031fec74adc9bff50fbaffd89 -https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-9.4.0-hf0ab688_10.tar.bz2#d5bb7daa1db6baf5880c21cd0f26061c -https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-9.4.0-h0316aca_10.tar.bz2#e12e127b391c92fa2f0d09dce6661ecb +https://conda.anaconda.org/main/linux-64/brotli-1.0.9-h5eee18b_7.tar.bz2#712827b1699cc71e6240626c413408f3 +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.0-hc2a2eb6_1.tar.bz2#139ace7da04f011abbd531cb2a9840ee +https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-9.5.0-hdb51d14_10.tar.bz2#05c8e77b0910e2551582ad509f5d82bc +https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-9.5.0-h43f449f_10.tar.bz2#082df69bef15ac548ed328686fe99da5 https://conda.anaconda.org/main/linux-64/lcms2-2.12-h3be6417_0.tar.bz2#653b4f425f6d4a21f04619b1f5f8aa43 -https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.83.1-h7bff187_0.tar.bz2#d0c278476dba3b29ee13203784672ab1 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.72.1-h2d90d5f_0.tar.bz2#ebeadbb5fbc44052eeb6f96a2136e3c2 +https://conda.anaconda.org/main/linux-64/libcurl-7.84.0-h91b91d3_0.tar.bz2#0609d762533acd27ac555f3d3f06034c +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.68.4-h3e27bee_0.tar.bz2#23767bef4fd0fb2bda64405df72c9454 https://conda.anaconda.org/conda-forge/linux-64/libpq-13.5-hd57d9b9_1.tar.bz2#a0f425d61c7df890d6381ea352c3f1d7 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.3-h522a892_1.tar.bz2#424fabaabbfb6ec60492d3aba900f513 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.4-h522a892_0.tar.bz2#802e43f480122a85ae6a34c1909f8f98 https://conda.anaconda.org/conda-forge/linux-64/nss-3.78-h2350873_0.tar.bz2#ab3df39f96742e6f1a9878b09274c1dc -https://conda.anaconda.org/conda-forge/linux-64/python-3.9.13-h9a8a25e_0_cpython.tar.bz2#69bc307cc4d7396c5fccb26bbcc9c379 +https://conda.anaconda.org/main/linux-64/python-3.9.13-haa1d7c7_1.tar.bz2#19b599ad36b65ad4fdb59b5237cdf8b1 https://conda.anaconda.org/main/linux-64/sed-4.8-h7b6447c_0.tar.bz2#cf7fd03eb1f493dfed4d65ef7f1da70c https://conda.anaconda.org/conda-forge/noarch/bagit-1.8.1-pyhd8ed1ab_0.tar.bz2#fb50f22b353ed3a12f5e8b7c01b58832 -https://conda.anaconda.org/main/linux-64/certifi-2022.6.15-py39h06a4308_0.tar.bz2#242a8d9e6b3461a6788f602263b2c5f2 -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.1.0-pyhd8ed1ab_0.tar.bz2#abc0453b6e7bfbb87d275d58e333fc98 -https://conda.anaconda.org/conda-forge/linux-64/curl-7.83.1-h7bff187_0.tar.bz2#ba33b9995f5e691e4f439422d6efafc7 +https://conda.anaconda.org/conda-forge/noarch/certifi-2022.9.24-pyhd8ed1ab_0.tar.bz2#f66309b099374af91369e67e84af397d +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.1.1-pyhd8ed1ab_0.tar.bz2#c1d5b294fbf9a795dec349a6f4d8be8e +https://conda.anaconda.org/main/linux-64/curl-7.84.0-h5eee18b_0.tar.bz2#8e227be6ade98ae6572c9f23d7b34f35 https://conda.anaconda.org/main/noarch/cycler-0.11.0-pyhd3eb1b0_0.tar.bz2#dfb0ca8055c9a72aaca7b845a6edea17 -https://conda.anaconda.org/main/noarch/dataclasses-0.8-pyh6d0b6a4_7.tar.bz2#c8c177c84b3e5e6145156045660868e9 -https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.72.1-h6239696_0.tar.bz2#a3a99cc33279091262bbc4f5ee7c4571 +https://conda.anaconda.org/main/linux-64/cython-0.29.24-py39hdbfa776_0.tar.bz2#52d886d72dc4d2cb19dbbf9bbf6e20e6 +https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.6-h04a7f16_0.tar.bz2#b24a1e18325a6e8f8b6b4a2ec5860ce2 +https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.68.4-h9c3ff4c_0.tar.bz2#1e3e305bf1db79b99c3255b557751519 +https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.18.5-h76c114f_0.tar.bz2#2372f6206f9861a1af8e3b086a053b2b +https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363 https://conda.anaconda.org/main/linux-64/humanfriendly-10.0-py39h06a4308_0.tar.bz2#df148e261d4e930e99f6a8a27a822455 -https://conda.anaconda.org/main/noarch/idna-3.3-pyhd3eb1b0_0.tar.bz2#f2d821eedf78bfe0e9f1d8faf9c8b23e +https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed https://conda.anaconda.org/main/linux-64/libgd-2.3.3-h695aa2c_1.tar.bz2#e17d73f9dbc6791d2462d0b3ea348c65 https://conda.anaconda.org/main/linux-64/lockfile-0.12.2-py39h06a4308_0.tar.bz2#34a76c18f0ccc3ad6f0f5d37f1c9fd25 https://conda.anaconda.org/main/linux-64/mistune-0.8.4-py39h27cfd23_1000.tar.bz2#f6c734d73e6e3dbc1b62766cf18ff3e4 https://conda.anaconda.org/main/noarch/munkres-1.1.4-py_0.tar.bz2#67857cc5e9044770d2b15f9d94a4521d https://conda.anaconda.org/main/linux-64/mypy_extensions-0.4.3-py39h06a4308_1.tar.bz2#763d81e8d77153a6fd3b1981d20023b1 -https://conda.anaconda.org/conda-forge/noarch/networkx-2.8.5-pyhd8ed1ab_0.tar.bz2#4359cc8df0609a2b88aec55d6d2fedaf -https://conda.anaconda.org/conda-forge/noarch/pathspec-0.9.0-pyhd8ed1ab_0.tar.bz2#f93dc0ccbc0a8472624165f6e256c7d1 +https://conda.anaconda.org/conda-forge/noarch/networkx-2.8.6-pyhd8ed1ab_0.tar.bz2#52243eec8c5b9d22924d1c463b01a1d8 +https://conda.anaconda.org/main/linux-64/numpy-base-1.23.1-py39h1e6e340_0.tar.bz2#bd603c793e710951545e12011b88a0d5 +https://conda.anaconda.org/conda-forge/noarch/pathspec-0.10.1-pyhd8ed1ab_0.tar.bz2#2357d88e11ea38523d50b9e2fc98dfcf https://conda.anaconda.org/main/linux-64/pillow-9.2.0-py39hace64e9_1.tar.bz2#f916184be2f9f18cb1add691f7588d57 -https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.2-pyhd8ed1ab_1.tar.bz2#2fb3f88922e7aec26ba652fcdfe13950 +https://conda.anaconda.org/main/linux-64/platformdirs-2.5.2-py39h06a4308_0.tar.bz2#5f63c002eb6d388263c9df043fdcb973 https://conda.anaconda.org/main/noarch/pycparser-2.21-pyhd3eb1b0_0.tar.bz2#e70944ff53d19af4f65073a4f21c7b05 -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.0.9-pyhd8ed1ab_0.tar.bz2#e8fbc1b54b25f4b08281467bc13b70cc +https://conda.anaconda.org/main/linux-64/pyparsing-3.0.9-py39h06a4308_0.tar.bz2#ffceed627867508d73af1636ab5ebf42 https://conda.anaconda.org/main/linux-64/pysocks-1.7.1-py39h06a4308_0.tar.bz2#c5f3f7f10ad30f0945e75252615ab6e5 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.9-2_cp39.tar.bz2#39adde4247484de2bb4000122fdcf665 -https://conda.anaconda.org/main/linux-64/pytz-2022.1-py39h06a4308_0.tar.bz2#4f78e6b22e43dcc597168917d225a733 +https://conda.anaconda.org/conda-forge/noarch/pytz-2022.2.1-pyhd8ed1ab_0.tar.bz2#974bca71d00364630f63f31fa7e059cb https://conda.anaconda.org/main/linux-64/ruamel.yaml.clib-0.2.6-py39h7f8727e_0.tar.bz2#3d402ae0b182259d07f8cd7723bec2e3 https://conda.anaconda.org/bioconda/noarch/shellescape-3.4.1-py_1.tar.bz2#fe54078488e8cb18154999306fa28742 https://conda.anaconda.org/main/noarch/six-1.16.0-pyhd3eb1b0_1.tar.bz2#0a3ab834ba49016e183e715bff48a2b6 https://conda.anaconda.org/main/linux-64/tomli-2.0.1-py39h06a4308_0.tar.bz2#3c925f0e6cbc92a445946470f9d3732e -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.3.0-pyha770c72_0.tar.bz2#a9d85960bc62d53cc4ea0d1d27f73c98 +https://conda.anaconda.org/main/linux-64/tornado-6.2-py39h5eee18b_0.tar.bz2#97a705471f27f23009d060bf8e7a66a2 +https://conda.anaconda.org/main/linux-64/typing_extensions-4.3.0-py39h06a4308_0.tar.bz2#259aebda0d152d106e7243bd9fc946af https://conda.anaconda.org/main/linux-64/webencodings-0.5.1-py39h06a4308_1.tar.bz2#f8d03a15b974fc7810d9f54ae849fc8e https://conda.anaconda.org/main/noarch/wheel-0.37.1-pyhd3eb1b0_0.tar.bz2#9800fd2a86354f58731a7ad0eb5dd537 -https://conda.anaconda.org/main/linux-64/zipp-3.8.0-py39h06a4308_0.tar.bz2#215b8728e7dd452f9dde848fabe76c66 -https://conda.anaconda.org/bioconda/linux-64/bowtie-1.3.1-py39hd400a0c_2.tar.bz2#3b677eed79d8ec7e588276a18b08ede9 -https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py39he91dace_0.tar.bz2#61e961a94c8fd535e4496b17e7452dfe +https://conda.anaconda.org/conda-forge/noarch/zipp-3.8.1-pyhd8ed1ab_0.tar.bz2#a3508a0c850745b875de88aea4c40cc5 +https://conda.anaconda.org/bioconda/linux-64/bowtie-1.3.1-py39h0991082_4.tar.bz2#464484255f7a4159d24a2244cabab374 +https://conda.anaconda.org/main/linux-64/cffi-1.15.1-py39h74dc2b5_0.tar.bz2#2953f22399cb56381016d7a039d0c5ac https://conda.anaconda.org/conda-forge/linux-64/click-8.1.3-py39hf3d152e_0.tar.bz2#40edd9ebc04e4b4ec27c1008e5e3f99d https://conda.anaconda.org/main/linux-64/coloredlogs-15.0.1-py39h06a4308_0.tar.bz2#7a05a5dc89479b0acb12d6fca220aae6 -https://conda.anaconda.org/conda-forge/linux-64/g-ir-build-tools-1.72.0-py39h3707be2_1.tar.bz2#be26993d888f7f3816a628e7b5331a22 -https://conda.anaconda.org/conda-forge/linux-64/glib-2.72.1-h6239696_0.tar.bz2#1698b7684d3c6a4d1de2ab946f5b0fb5 +https://conda.anaconda.org/conda-forge/linux-64/glib-2.68.4-h9c3ff4c_0.tar.bz2#8b8a2eca3496b279cd4f45888490437f +https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.18.5-hf529b03_0.tar.bz2#b1f3d74a3b66432adddfc118ff02238a https://conda.anaconda.org/main/noarch/html5lib-1.1-pyhd3eb1b0_0.tar.bz2#7a9fa936878258757f23ffe4b2e8ecf7 https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.4-py39hf3d152e_0.tar.bz2#4c2a0eabf0b8980b2c755646a6f750eb https://conda.anaconda.org/conda-forge/noarch/isodate-0.6.1-pyhd8ed1ab_0.tar.bz2#4a62c93c1b5c0b920508ae3fd285eaf5 @@ -165,55 +171,50 @@ https://conda.anaconda.org/conda-forge/linux-64/keepalive-0.5-py39hf3d152e_5.tar https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py39hf939315_0.tar.bz2#e8d1310648c189d6d11a2e13f73da1fe https://conda.anaconda.org/conda-forge/linux-64/lxml-4.8.0-py39hb9d737c_2.tar.bz2#4372febe83f17a2e2cc9d9cac95357f0 https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.4-py39hf939315_0.tar.bz2#35b4a1a56408657cd2c6ce7145c21ecf -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.23.1-py39hba7629e_0.tar.bz2#ee8dff1fb28e0e2c458e845aae9d915a +https://conda.anaconda.org/main/linux-64/numpy-1.23.1-py39hf838250_0.tar.bz2#02e526bc277f6fb1759628d874aae8c1 https://conda.anaconda.org/main/noarch/packaging-21.3-pyhd3eb1b0_0.tar.bz2#e482aa236f5541461d285cc979323035 https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.1-py39hb9d737c_0.tar.bz2#5852c69cad74811dc3c95f9ab6a184ef https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py39he80948d_8.tar.bz2#9dbac74c150d2542eca77c02da307168 -https://conda.anaconda.org/bioconda/linux-64/pysam-0.19.1-py39h5030a8b_0.tar.bz2#4d5b179e9363755d2b9b3b97a75070f6 +https://conda.anaconda.org/bioconda/linux-64/pysam-0.19.1-py39h9abd093_1.tar.bz2#5c6ce222fa4a9bce08fcb9b54302809f https://conda.anaconda.org/main/noarch/python-dateutil-2.8.2-pyhd3eb1b0_0.tar.bz2#2eb923cc014094f4acf7f849d67d73f8 https://conda.anaconda.org/conda-forge/linux-64/setuptools-63.1.0-py39hf3d152e_0.tar.bz2#7014afab44a0e69fba02c8bf8d084e7f -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.2-py39hb9d737c_0.tar.bz2#a3c57360af28c0d9956622af99a521cd -https://conda.anaconda.org/conda-forge/linux-64/typed-ast-1.5.4-py39hb9d737c_0.tar.bz2#8425d811ad227a7974949cf3071da6a9 https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py39hb9d737c_1.tar.bz2#ef84376736d1e8a814ccb06d1d814e6f -https://conda.anaconda.org/conda-forge/noarch/black-22.6.0-pyhd8ed1ab_0.tar.bz2#f8fb78b07826bbf84deda3cf128cfbe2 +https://conda.anaconda.org/conda-forge/linux-64/black-22.8.0-py39hf3d152e_0.tar.bz2#0f3f74e247a7e3bdaff3dcb1e82da1e9 https://conda.anaconda.org/main/linux-64/bottleneck-1.3.5-py39h7deecbd_0.tar.bz2#14dc3c2e8eb68b1111a08de9a3ce8a00 https://conda.anaconda.org/main/linux-64/brotlipy-0.7.0-py39h27cfd23_1003.tar.bz2#c203ffc7bbba991c7089d4b383cfd92c -https://conda.anaconda.org/main/linux-64/cairo-1.16.0-h19f5f5c_2.tar.bz2#ec69c918d097c392992a7fbe52d252d3 +https://conda.anaconda.org/main/linux-64/cairo-1.16.0-hf32fb01_1.tar.bz2#4c1adb402948673d3fc537e8a41a14fc https://conda.anaconda.org/conda-forge/linux-64/cryptography-37.0.4-py39hd97740a_0.tar.bz2#edc3668e7b71657237f94cf25e286478 https://conda.anaconda.org/main/linux-64/dbus-1.13.18-hb2f20db_0.tar.bz2#498d0788982ec4c5d13a44359b9c7afb -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.34.4-py39hb9d737c_0.tar.bz2#7980ace37ccb3399672c3a9840e039ed -https://conda.anaconda.org/main/linux-64/gdk-pixbuf-2.42.8-h433bba3_0.tar.bz2#6333e79ba7d103847ab629db09048ed4 -https://conda.anaconda.org/main/linux-64/gstreamer-1.18.5-ha1a6a79_0.tar.bz2#473cda6a24471acbfec11ab2bee3d16c -https://conda.anaconda.org/main/linux-64/gts-0.7.6-hb67d8dd_3.tar.bz2#7959ca29b7f34b7f2164884b7dbc2d6d +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.37.3-py39hb9d737c_0.tar.bz2#21622fe576fcce5b861036e8d7282470 +https://conda.anaconda.org/main/linux-64/gobject-introspection-1.68.0-py39h2109141_1.tar.bz2#b7fc8ab104ca2e2348de248c5db56bde https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-4.11.4-hd8ed1ab_0.tar.bz2#9a1925fdb91c81437b8012e48ede6851 https://conda.anaconda.org/main/linux-64/numexpr-2.8.3-py39hd2a5715_0.tar.bz2#854304a1c6108c9536816d55577d7fe0 https://conda.anaconda.org/main/linux-64/pip-22.1.2-py39h06a4308_0.tar.bz2#7b64a88c719bbdac8d60467d4d8c2b0a https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.17.16-py39h3811e60_0.tar.bz2#9deea133cdd90530091e26db39acfac4 https://conda.anaconda.org/conda-forge/linux-64/sparqlwrapper-1.8.5-py39hf3d152e_1006.tar.bz2#413eb7275e941588d6eaeb5d78b06210 https://conda.anaconda.org/conda-forge/noarch/argcomplete-2.0.0-pyhd8ed1ab_0.tar.bz2#e3ba29a61c20f2c9d085db29ea657406 -https://conda.anaconda.org/main/linux-64/gst-plugins-base-1.18.5-hc549b11_0.tar.bz2#e85c8d6014d04fc5588961be62e10026 -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-3.1.1-h83ec7ef_0.tar.bz2#ca8faaee04a83e3c4d6f708a35ac2ec3 -https://conda.anaconda.org/conda-forge/linux-64/libgirepository-1.72.0-h26ff761_1.tar.bz2#7341e252ebc81d86c5454672ca6579b0 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.5.2-py39h700656a_0.tar.bz2#ab1bcd0fd24e375f16d662e4cc783cab +https://conda.anaconda.org/main/linux-64/atk-1.0-2.36.0-h28cd5cc_0.tar.bz2#e403106108523a94d9f013c072faae7a +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-3.0.0-h83ec7ef_1.tar.bz2#5dbf3f949c17cc12482e5440b8c4770e +https://conda.anaconda.org/main/linux-64/matplotlib-base-3.5.2-py39hf590b9c_0.tar.bz2#a356ab9537c19bd6bae34f381b20a26c https://conda.anaconda.org/main/linux-64/pandas-1.4.3-py39h6a678d5_0.tar.bz2#1bd58a0c08f2e66684eccb3eab870226 https://conda.anaconda.org/main/noarch/pyopenssl-22.0.0-pyhd3eb1b0_0.tar.bz2#24300c22dced4609b77f3ace93b5beeb -https://conda.anaconda.org/conda-forge/linux-64/g-ir-host-tools-1.72.0-h1def2ed_1.tar.bz2#f0e73f96de6b7a2928380f5ac125cfb6 +https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-hda022c4_4.tar.bz2#afebab1f5049d66baaaec67d9ce893f0 https://conda.anaconda.org/bioconda/linux-64/htseq-2.0.2-py39h919a90d_0.tar.bz2#05348cda39a1b7a2d955aa4c7828de89 https://conda.anaconda.org/conda-forge/linux-64/pango-1.48.10-h54213e6_2.tar.bz2#b7ed7c76c9360db1f91afba2e220007b -https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-hda022c4_4.tar.bz2#afebab1f5049d66baaaec67d9ce893f0 -https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.11-pyhd8ed1ab_0.tar.bz2#0738978569b10669bdef41c671252dd1 -https://conda.anaconda.org/conda-forge/linux-64/gobject-introspection-1.72.0-py39habf54e5_1.tar.bz2#7bdf7002fa1ac148cc190cc99b0a139e -https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.52.5-hc3c00ef_1.tar.bz2#9cd526f006d048eb912e09c5982393ea https://conda.anaconda.org/conda-forge/linux-64/pyqt-impl-5.12.3-py39hde8b62d_8.tar.bz2#4863d6734a1bd7a86ac5ede53bf9b3c7 +https://conda.anaconda.org/main/linux-64/urllib3-1.26.11-py39h06a4308_0.tar.bz2#3763a6d835897d9817ebc9a9e3d29631 +https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h539f30e_1.tar.bz2#606777b4da3664d5c9415f5f165349fd +https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.52.2-hc3c00ef_0.tar.bz2#3cd7a9c521da1d3dbfdc37fc62ea9e47 +https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py39h0fcd23e_8.tar.bz2#d7d18728be87fdc0ddda3e65d41caa53 +https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py39h0fcd23e_8.tar.bz2#2098c2b2c9a38b43678a27819ff9433f https://conda.anaconda.org/conda-forge/linux-64/r-base-4.1.1-hb93adac_1.tar.bz2#393858be0a91b3df02d3cba3f0ad4b60 https://conda.anaconda.org/main/linux-64/requests-2.28.1-py39h06a4308_0.tar.bz2#022a67a9547db5fdfa024f8bf69ab2ad -https://conda.anaconda.org/main/linux-64/atk-1.0-2.36.0-ha1a6a79_0.tar.bz2#387649f0dd6e7540ac4ec9ebb40f89c4 https://conda.anaconda.org/bioconda/noarch/bioconductor-biocgenerics-0.40.0-r41hdfd78af_0.tar.bz2#73c383d354e038bb8b4de75715747071 https://conda.anaconda.org/bioconda/noarch/bioconductor-genomeinfodbdata-1.2.7-r41hdfd78af_2.tar.bz2#b23c1905f6ee6dddcfa3af515aa4844f -https://conda.anaconda.org/bioconda/linux-64/bioconductor-zlibbioc-1.40.0-r41h5c21468_1.tar.bz2#9d6da3b64060f38a57fa3fdb9e801210 +https://conda.anaconda.org/bioconda/linux-64/bioconductor-zlibbioc-1.40.0-r41hc0cfd56_2.tar.bz2#b363f6e6159be35bbea95b978c8ef3fe https://conda.anaconda.org/conda-forge/noarch/cachecontrol-0.12.11-pyhd8ed1ab_0.tar.bz2#6eefee9888f33f150b5d44d616b1a613 -https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py39h0fcd23e_8.tar.bz2#d7d18728be87fdc0ddda3e65d41caa53 -https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py39h0fcd23e_8.tar.bz2#2098c2b2c9a38b43678a27819ff9433f +https://conda.anaconda.org/conda-forge/linux-64/graphviz-2.49.1-h85b4f2f_0.tar.bz2#9705e060a5e6395db9b0784220aa0b92 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py39hf3d152e_8.tar.bz2#466425e3ee3b190e06b8a5a7098421aa https://conda.anaconda.org/conda-forge/linux-64/r-backports-1.4.1-r41hcfec24a_0.tar.bz2#f431d585821313884e52d64b5ef0d8cd https://conda.anaconda.org/conda-forge/noarch/r-bh-1.78.0_0-r41hc72bb7e_0.tar.bz2#4963f5da888bdbfb202b3612e11ba972 https://conda.anaconda.org/conda-forge/linux-64/r-bit-4.0.4-r41hcfec24a_0.tar.bz2#f1a85a1b4a48261f3cbf3f2d1ee1f4a2 @@ -224,7 +225,7 @@ https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.1-r41hc72bb7e_0.tar.b https://conda.anaconda.org/conda-forge/linux-64/r-curl-4.3.2-r41hcfec24a_0.tar.bz2#18de0d29e4da1b0c3d2200896a7c5cf8 https://conda.anaconda.org/conda-forge/noarch/r-dbi-1.1.3-r41hc72bb7e_0.tar.bz2#844d1b00d0665a78c7a0bc55c5c038b2 https://conda.anaconda.org/conda-forge/linux-64/r-digest-0.6.29-r41h03ef668_0.tar.bz2#9b5bcee4e903920236c5911fdd3478d8 -https://conda.anaconda.org/conda-forge/noarch/r-evaluate-0.15-r41hc72bb7e_0.tar.bz2#c85313c76eb5e48dc016402d32567494 +https://conda.anaconda.org/conda-forge/noarch/r-evaluate-0.16-r41hc72bb7e_0.tar.bz2#4e6d48071b7aee6d8f4e82b72f082fc6 https://conda.anaconda.org/conda-forge/linux-64/r-fansi-1.0.3-r41h06615bd_0.tar.bz2#f7eb22307beab0fdd027497ad586238d https://conda.anaconda.org/conda-forge/linux-64/r-farver-2.1.1-r41h7525677_0.tar.bz2#06cbfe381a5f7eefd7bce35be1a7a643 https://conda.anaconda.org/conda-forge/linux-64/r-fastmap-1.1.0-r41h03ef668_0.tar.bz2#93b346ace02eba68f1f6c772e796b41d @@ -232,12 +233,12 @@ https://conda.anaconda.org/conda-forge/noarch/r-formatr-1.12-r41hc72bb7e_0.tar.b https://conda.anaconda.org/conda-forge/linux-64/r-fs-1.5.2-r41h7525677_1.tar.bz2#79e6d70cc797f5534d3375ebd0f0f5f5 https://conda.anaconda.org/conda-forge/noarch/r-futile.options-1.0.1-r41hc72bb7e_1002.tar.bz2#d8ce292272b4a660fe3ac7ee93c13c9f https://conda.anaconda.org/conda-forge/linux-64/r-glue-1.6.2-r41h06615bd_0.tar.bz2#ac6fda8a0ebf198ee98252c60146c548 -https://conda.anaconda.org/conda-forge/noarch/r-gtable-0.3.0-r41hc72bb7e_3.tar.bz2#c2265bc91dceb28f25d533e9d93212fa +https://conda.anaconda.org/conda-forge/noarch/r-gtable-0.3.1-r41hc72bb7e_0.tar.bz2#db42f11efeba36388f28393a629c8fde https://conda.anaconda.org/conda-forge/linux-64/r-jsonlite-1.8.0-r41h06615bd_0.tar.bz2#4d063eba6eabed26214bb23f18f891d2 https://conda.anaconda.org/conda-forge/noarch/r-labeling-0.4.2-r41hc72bb7e_1.tar.bz2#7536c98f2faf4bf5faf4334fb79f1328 https://conda.anaconda.org/conda-forge/linux-64/r-lattice-0.20_45-r41hcfec24a_0.tar.bz2#895ccbaf82eba857612be799cd5744b4 https://conda.anaconda.org/conda-forge/linux-64/r-magrittr-2.0.3-r41h06615bd_0.tar.bz2#08ef457aa9684c94036fb5e9e2753a9f -https://conda.anaconda.org/conda-forge/linux-64/r-mass-7.3_58-r41h06615bd_0.tar.bz2#9d38f0cd8ddbb228d030faedd7fe8e96 +https://conda.anaconda.org/conda-forge/linux-64/r-mass-7.3_58.1-r41h06615bd_0.tar.bz2#86d5c685acac50458692d96775c71d2f https://conda.anaconda.org/conda-forge/linux-64/r-matrixstats-0.62.0-r41h06615bd_0.tar.bz2#55eb496da17cdfb0ed1f1d9610be5c00 https://conda.anaconda.org/conda-forge/linux-64/r-mime-0.12-r41hcfec24a_0.tar.bz2#f40c9f8645ea54ce0709a5436d569e2d https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r41hc72bb7e_1.tar.bz2#2c4f78477753ad83700a1f9d605a3002 @@ -247,75 +248,72 @@ https://conda.anaconda.org/conda-forge/noarch/r-praise-1.0.0-r41hc72bb7e_1005.ta https://conda.anaconda.org/conda-forge/linux-64/r-ps-1.7.1-r41h06615bd_0.tar.bz2#6c1e615b4e0767020c37c17252dd614a https://conda.anaconda.org/conda-forge/noarch/r-r6-2.5.1-r41hc72bb7e_0.tar.bz2#a560798c2d5ab0dbb30feddc6b8e63fb https://conda.anaconda.org/conda-forge/noarch/r-rcolorbrewer-1.1_3-r41h785f33e_0.tar.bz2#579b6792713b6c8538c1aa8e4ce636c0 -https://conda.anaconda.org/conda-forge/linux-64/r-rcpp-1.0.9-r41h7525677_0.tar.bz2#45ff7c84b07cfead9b33a77affe10bd3 -https://conda.anaconda.org/conda-forge/linux-64/r-rlang-1.0.4-r41h7525677_0.tar.bz2#de057907405162c18d3d64cd037913e8 +https://conda.anaconda.org/conda-forge/linux-64/r-rcpp-1.0.9-r41h7525677_1.tar.bz2#d7ad5262178fb2572c21ebe09f887379 +https://conda.anaconda.org/conda-forge/linux-64/r-rlang-1.0.6-r41h7525677_0.tar.bz2#14a6afa005c9d409845bf6ba66dc69a1 https://conda.anaconda.org/conda-forge/noarch/r-snow-0.4_4-r41hc72bb7e_0.tar.bz2#6e7a0c895d09c4e035262a850d69a994 https://conda.anaconda.org/conda-forge/linux-64/r-sys-3.4-r41hcfec24a_0.tar.bz2#bdf70442cc075fb748f874f4ebe922a8 https://conda.anaconda.org/conda-forge/linux-64/r-utf8-1.2.2-r41hcfec24a_0.tar.bz2#3b958ecf41127b730cc0f0865cc8953e -https://conda.anaconda.org/conda-forge/noarch/r-viridislite-0.4.0-r41hc72bb7e_0.tar.bz2#00ad214f0ade6d1a8e7fedb5eb06add6 +https://conda.anaconda.org/conda-forge/noarch/r-viridislite-0.4.1-r41hc72bb7e_0.tar.bz2#9e845780dbd53f680cc9d7a2602dca53 https://conda.anaconda.org/conda-forge/noarch/r-withr-2.5.0-r41hc72bb7e_0.tar.bz2#53558bed67c14dc998209e5a567b6cfd https://conda.anaconda.org/conda-forge/linux-64/r-xml-3.99_0.9-r41h06615bd_0.tar.bz2#e77ce67280ce31650de5a28b9745c1b6 https://conda.anaconda.org/conda-forge/noarch/r-xtable-1.8_4-r41hc72bb7e_3.tar.bz2#f44d2ad79a8d084f757cefd24156bdfc https://conda.anaconda.org/conda-forge/linux-64/rdflib-6.0.2-py39hf3d152e_0.tar.bz2#3be2b7263f9f7864492c3841036dac3a -https://conda.anaconda.org/bioconda/linux-64/bioconductor-biobase-2.54.0-r41h5c21468_1.tar.bz2#f340a88914092c651452c0d05448b0ce +https://conda.anaconda.org/bioconda/linux-64/bioconductor-biobase-2.54.0-r41hc0cfd56_2.tar.bz2#d69c5574021a948b27f1c0403366bad4 https://conda.anaconda.org/bioconda/noarch/bioconductor-matrixgenerics-1.6.0-r41hdfd78af_0.tar.bz2#69613f3c312fb36021947f3359e89435 -https://conda.anaconda.org/bioconda/linux-64/bioconductor-s4vectors-0.32.3-r41h5c21468_0.tar.bz2#38196da98ec15ad79fc693e6a0af7b30 -https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h539f30e_1.tar.bz2#606777b4da3664d5c9415f5f165349fd -https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py39hf3d152e_8.tar.bz2#466425e3ee3b190e06b8a5a7098421aa +https://conda.anaconda.org/bioconda/linux-64/bioconductor-s4vectors-0.32.4-r41hc0cfd56_0.tar.bz2#666dc31cccc2dab8ae2ff7414104c6dc +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.2-py39hf3d152e_1.tar.bz2#7a3ff2d1e66e674631a65ea32d5cdbbd +https://conda.anaconda.org/conda-forge/linux-64/pydot-1.4.2-py39hf3d152e_2.tar.bz2#74c8563d1af0163f52220ce86401547b +https://conda.anaconda.org/main/noarch/pydotplus-2.0.2-py_3.tar.bz2#cfb123d1e6b3375ae2acab4c290390ae https://conda.anaconda.org/conda-forge/linux-64/r-askpass-1.1-r41hcfec24a_2.tar.bz2#58e1d05890607b8534f1933bbc847901 https://conda.anaconda.org/conda-forge/linux-64/r-bit64-4.0.5-r41hcfec24a_0.tar.bz2#f632c48178171d62f2817b5e09bf2e97 https://conda.anaconda.org/conda-forge/linux-64/r-cachem-1.0.6-r41hcfec24a_0.tar.bz2#83ee52abba53943baba543605f3ebd91 -https://conda.anaconda.org/conda-forge/linux-64/r-cli-3.3.0-r41h7525677_0.tar.bz2#804f8d2da61218d567b49c20635b1a0d +https://conda.anaconda.org/conda-forge/linux-64/r-cli-3.4.1-r41h7525677_0.tar.bz2#fc4295f25cb07e43ad24328f6d3d04c3 https://conda.anaconda.org/conda-forge/linux-64/r-diffobj-0.3.5-r41hcfec24a_0.tar.bz2#01693a46ed0b5a2e355aefdffc626d67 https://conda.anaconda.org/conda-forge/linux-64/r-ellipsis-0.3.2-r41hcfec24a_0.tar.bz2#44d25949d98811e21e9d19527b6e3cfe https://conda.anaconda.org/conda-forge/noarch/r-lambda.r-1.2.4-r41hc72bb7e_1.tar.bz2#0cdb436f2dd68815329b28ba9ffce962 -https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.1-r41hc72bb7e_0.tar.bz2#c7f563a8fc6d5d9c52da2e390fa5f856 +https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.2-r41hc72bb7e_0.tar.bz2#b22d5a6a675d3a2ce268b5f7a913e41e https://conda.anaconda.org/conda-forge/linux-64/r-locfit-1.5_9.6-r41h06615bd_0.tar.bz2#2e5180f51d32c4fdcda341f3feaf322d https://conda.anaconda.org/conda-forge/linux-64/r-matrix-1.4_1-r41h0154571_0.tar.bz2#46bdce8fcf422c4b74e0af3091ddfdfc https://conda.anaconda.org/conda-forge/noarch/r-munsell-0.5.0-r41hc72bb7e_1004.tar.bz2#f5bd0c85c189a1797f29f2731c13a4dd -https://conda.anaconda.org/conda-forge/linux-64/r-nlme-3.1_158-r41h8da6f51_0.tar.bz2#5b014d8cc0a1c1c67aa154d65d44dde0 +https://conda.anaconda.org/conda-forge/linux-64/r-nlme-3.1_159-r41h8da6f51_0.tar.bz2#e562ef41913c6cf417bf6f93527c7e4e https://conda.anaconda.org/conda-forge/linux-64/r-processx-3.7.0-r41h06615bd_0.tar.bz2#b03077eb7df7e79e05ed2ccf5e2276a1 -https://conda.anaconda.org/conda-forge/linux-64/r-rcpparmadillo-0.11.2.0.0-r41h9f5de39_0.tar.bz2#6257f717b5db156f08536bef6e0033f2 -https://conda.anaconda.org/conda-forge/linux-64/r-rcurl-1.98_1.7-r41h06615bd_0.tar.bz2#9bd21e0d168cfc92b072c3010be8efcd +https://conda.anaconda.org/conda-forge/linux-64/r-rcpparmadillo-0.11.2.3.1-r41h9f5de39_0.tar.bz2#758f62b21b99de95a10f59f0b1ed1f02 +https://conda.anaconda.org/conda-forge/linux-64/r-rcurl-1.98_1.8-r41h06615bd_0.tar.bz2#585dbbd460ca7570d4b617c59423a4b5 https://conda.anaconda.org/conda-forge/noarch/r-rprojroot-2.0.3-r41hc72bb7e_0.tar.bz2#4c97577b6f80853234f34b548d8140ec -https://conda.anaconda.org/conda-forge/linux-64/schema-salad-8.3.20220525163636-py39hb9d737c_0.tar.bz2#4045302780f5cb2f6b5bd9cb84f83c1f -https://conda.anaconda.org/bioconda/linux-64/bioconductor-iranges-2.28.0-r41h5c21468_1.tar.bz2#8eff233e3c53c2a2cb907e3f4c428874 -https://conda.anaconda.org/conda-forge/linux-64/graphviz-2.50.0-h85b4f2f_1.tar.bz2#bc6418fd87ea67cf14417337ced3daa2 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.2-py39hf3d152e_0.tar.bz2#d65d073d186977a2a9a9d5a68b2b77ef -https://conda.anaconda.org/conda-forge/noarch/r-callr-3.7.1-r41hc72bb7e_0.tar.bz2#03d548449f2330be28745ea8be93bf9e -https://conda.anaconda.org/conda-forge/noarch/r-desc-1.4.1-r41hc72bb7e_0.tar.bz2#259389838e07898dbc82ae38fba42541 +https://conda.anaconda.org/conda-forge/linux-64/schema-salad-8.3.20220916115321-py39hb9d737c_0.tar.bz2#b48c63610dff3fca5cc9d07ad45080cd +https://conda.anaconda.org/bioconda/linux-64/bioconductor-iranges-2.28.0-r41hc0cfd56_2.tar.bz2#27ab085114c78a2ea0a60d6dcaf0da5a +https://conda.anaconda.org/conda-forge/noarch/prov-1.5.1-py_1.tar.bz2#66e578dc9f4bfe65728ad547a81cd6dd +https://conda.anaconda.org/conda-forge/noarch/r-callr-3.7.2-r41hc72bb7e_0.tar.bz2#0493d6ed1666106a66d218c0eb44d475 +https://conda.anaconda.org/conda-forge/noarch/r-desc-1.4.2-r41hc72bb7e_0.tar.bz2#b63a8a0a0fa2fd16a324d50b4df98238 https://conda.anaconda.org/conda-forge/noarch/r-futile.logger-1.4.3-r41hc72bb7e_1003.tar.bz2#c29b2d0547e44751529e51f667d39b19 https://conda.anaconda.org/conda-forge/noarch/r-memoise-2.0.1-r41hc72bb7e_0.tar.bz2#ba52e00d6d8b5bd8edd1c164b74cca9d https://conda.anaconda.org/conda-forge/linux-64/r-mgcv-1.8_40-r41h0154571_0.tar.bz2#94ba55d80afb3db8a19d598e313ce078 -https://conda.anaconda.org/conda-forge/linux-64/r-openssl-2.0.2-r41hfaab4ff_0.tar.bz2#3334a5f769494453a7a80106ad90d04f -https://conda.anaconda.org/conda-forge/noarch/r-scales-1.2.0-r41hc72bb7e_0.tar.bz2#0fb58a9c9b925c7c5f491d2c7e2b78da -https://conda.anaconda.org/conda-forge/linux-64/r-survival-3.3_1-r41h06615bd_0.tar.bz2#ccaa8f0b9515fb59f29a74ec6078179d +https://conda.anaconda.org/conda-forge/linux-64/r-openssl-2.0.3-r41hfaab4ff_0.tar.bz2#4f16c420be4db502ec617f87f060d224 +https://conda.anaconda.org/conda-forge/noarch/r-scales-1.2.1-r41hc72bb7e_0.tar.bz2#9ba5c1de3a9758871584ba40c378db14 +https://conda.anaconda.org/conda-forge/linux-64/r-survival-3.4_0-r41h06615bd_0.tar.bz2#eca911448f3b42b6d2194589dcbe0800 https://conda.anaconda.org/conda-forge/linux-64/r-vctrs-0.4.1-r41h7525677_0.tar.bz2#5bc4133cae4a09d70dd11f3c29c57a4c -https://conda.anaconda.org/bioconda/linux-64/bioconductor-biocparallel-1.28.3-r41h619a076_0.tar.bz2#45583855420d68681a2cf46cc373e4b4 -https://conda.anaconda.org/bioconda/linux-64/bioconductor-delayedarray-0.20.0-r41h5c21468_1.tar.bz2#dddaa14aa801fd1d42ce75337a4f3537 +https://conda.anaconda.org/bioconda/linux-64/bioconductor-biocparallel-1.28.3-r41hc247a5b_1.tar.bz2#24b82b4c32a49a8d65211f587ff8d24e +https://conda.anaconda.org/bioconda/linux-64/bioconductor-delayedarray-0.20.0-r41hc0cfd56_2.tar.bz2#2118a38cef98b4db68f33c9632b28272 https://conda.anaconda.org/bioconda/noarch/bioconductor-genomeinfodb-1.30.0-r41hdfd78af_0.tar.bz2#c55150adbc50b1d923d48d9bb126cf0f -https://conda.anaconda.org/bioconda/linux-64/bioconductor-xvector-0.34.0-r41h5c21468_1.tar.bz2#5aa0af5ef4237d7d2f8112676b4811c4 -https://conda.anaconda.org/conda-forge/linux-64/pydot-1.4.2-py39hf3d152e_2.tar.bz2#74c8563d1af0163f52220ce86401547b -https://conda.anaconda.org/main/noarch/pydotplus-2.0.2-py_3.tar.bz2#cfb123d1e6b3375ae2acab4c290390ae +https://conda.anaconda.org/bioconda/linux-64/bioconductor-xvector-0.34.0-r41hc0cfd56_2.tar.bz2#be67668685ff98dc2d348ea17f43a1ab +https://conda.anaconda.org/conda-forge/noarch/cwltool-3.1.20220628170238-pyhc268e32_0.tar.bz2#b8780347d56585db8c9323522cbe9118 https://conda.anaconda.org/conda-forge/noarch/r-blob-1.2.3-r41hc72bb7e_0.tar.bz2#6ab0e11ba8562a5bb418e63e30013448 -https://conda.anaconda.org/conda-forge/noarch/r-httr-1.4.3-r41hc72bb7e_0.tar.bz2#7c69a246584f13167d43bf377d321a1f -https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.8.0-r41hc72bb7e_0.tar.bz2#e7c96c840e76a1bbe8b7e9f0ae2e1f2f +https://conda.anaconda.org/conda-forge/noarch/r-httr-1.4.4-r41hc72bb7e_0.tar.bz2#0f25e3d569e80378216435d8f324c1f5 +https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.8.1-r41hc72bb7e_0.tar.bz2#88ffc0fd48cf9bb5501bf54a8c885124 https://conda.anaconda.org/conda-forge/noarch/r-pkgload-1.3.0-r41hc72bb7e_0.tar.bz2#ad902132cde9da724b8c200b0e15e015 -https://conda.anaconda.org/bioconda/linux-64/bioconductor-biostrings-2.62.0-r41h5c21468_1.tar.bz2#27041ee91e49250a4a22ffa983b54abd -https://conda.anaconda.org/bioconda/linux-64/bioconductor-genomicranges-1.46.1-r41h5c21468_0.tar.bz2#5e9acd668eeec90f6737985e15bfb640 -https://conda.anaconda.org/conda-forge/noarch/prov-1.5.1-py_1.tar.bz2#66e578dc9f4bfe65728ad547a81cd6dd +https://conda.anaconda.org/bioconda/linux-64/bioconductor-biostrings-2.62.0-r41hc0cfd56_2.tar.bz2#397b7450e63f6cdb9d9ad82b4a056959 +https://conda.anaconda.org/bioconda/linux-64/bioconductor-genomicranges-1.46.1-r41hc0cfd56_1.tar.bz2#244ae6ab8cd592e58e18ddc8a740c3fc https://conda.anaconda.org/conda-forge/linux-64/r-rsqlite-2.2.8-r41h03ef668_0.tar.bz2#d2ffb5023b6587e998eaaa4bf2f94046 https://conda.anaconda.org/conda-forge/linux-64/r-tibble-3.1.8-r41h06615bd_0.tar.bz2#702c8e30bf36644c2c558729b9a3570e https://conda.anaconda.org/bioconda/noarch/bioconductor-keggrest-1.34.0-r41hdfd78af_0.tar.bz2#54056f47634552568aa26b1b09a2e16a https://conda.anaconda.org/bioconda/noarch/bioconductor-summarizedexperiment-1.24.0-r41hdfd78af_0.tar.bz2#f21eb229b8ee58680cb0179ef6d51f7e -https://conda.anaconda.org/conda-forge/noarch/cwltool-3.1.20220628170238-pyhc268e32_0.tar.bz2#b8780347d56585db8c9323522cbe9118 https://conda.anaconda.org/conda-forge/noarch/r-rematch2-2.1.2-r41hc72bb7e_1.tar.bz2#5c26fbf849007fc5022745ec14e38822 https://conda.anaconda.org/bioconda/noarch/bioconductor-annotationdbi-1.56.1-r41hdfd78af_0.tar.bz2#182f445903618187b8f657644da1a335 https://conda.anaconda.org/conda-forge/noarch/r-waldo-0.4.0-r41hc72bb7e_0.tar.bz2#974972fb4e38703ed8e1722f45600e8b https://conda.anaconda.org/bioconda/noarch/bioconductor-annotate-1.72.0-r41hdfd78af_0.tar.bz2#9594dd679fb3f7296b25dce8ebd387e4 https://conda.anaconda.org/conda-forge/linux-64/r-testthat-3.1.4-r41h7525677_0.tar.bz2#ca36c7db9d50f7481e2742454b32ad92 -https://conda.anaconda.org/bioconda/linux-64/bioconductor-genefilter-1.76.0-r41ha086028_1.tar.bz2#1579369ce7fc94ec1a35644e629b7c11 +https://conda.anaconda.org/bioconda/linux-64/bioconductor-genefilter-1.76.0-r41h38f54d8_2.tar.bz2#3e046b31c7d616dd436a2653bcb893e8 https://conda.anaconda.org/bioconda/noarch/bioconductor-geneplotter-1.72.0-r41hdfd78af_0.tar.bz2#32c0e22a0095ad9b592a6bf7e16a835a https://conda.anaconda.org/conda-forge/linux-64/r-isoband-0.2.5-r41h03ef668_0.tar.bz2#dfd1983b86abb444e3ae58e3b97cd148 https://conda.anaconda.org/conda-forge/noarch/r-ggplot2-3.3.6-r41hc72bb7e_0.tar.bz2#e305660b2a4c78aea808bc5c2aa910a3 -https://conda.anaconda.org/bioconda/linux-64/bioconductor-deseq2-1.34.0-r41h619a076_1.tar.bz2#64d08a7e5a19c97a9f9c189c4e955dfd +https://conda.anaconda.org/bioconda/linux-64/bioconductor-deseq2-1.34.0-r41hc247a5b_2.tar.bz2#85c15836de685e191335897422b3a280 diff --git a/conda/conda-osx-64.lock b/conda/conda-osx-64.lock index 0fb89978..f0653de9 100644 --- a/conda/conda-osx-64.lock +++ b/conda/conda-osx-64.lock @@ -1,11 +1,11 @@ # Generated by conda-lock. # platform: osx-64 -# input_hash: aa536c9750cc555218e1c792a2ed52764dab7748905fac0d76456af8b8e19346 +# input_hash: 616797432ed82f3efba6391bf708eea195d4c15c904a1345a9bb3e7f57314461 @EXPLICIT https://conda.anaconda.org/conda-forge/noarch/_r-mutex-1.0.1-anacondar_1.tar.bz2#19f9db5f4f1b7f5ef5f6d67207f25f38 https://conda.anaconda.org/main/osx-64/bzip2-1.0.8-h1de35cc_0.tar.bz2#ab3b4e83407001e7f1603ca1fa0f4873 https://conda.anaconda.org/main/osx-64/c-ares-1.18.1-hca72f7f_0.tar.bz2#c2c4131562aa7944a9bf6afc137bf576 -https://conda.anaconda.org/main/osx-64/ca-certificates-2022.07.19-hecd8cb5_0.tar.bz2#4883cd1a4f0c8aa3508d5c05e4c07d2e +https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2022.9.24-h033912b_0.tar.bz2#67b268c32433047914482def1ce215c2 https://conda.anaconda.org/main/noarch/font-ttf-dejavu-sans-mono-2.37-hd3eb1b0_0.tar.bz2#801b4743a301a4e11cbd9609d418e101 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb @@ -15,180 +15,178 @@ https://conda.anaconda.org/main/osx-64/giflib-5.2.1-haf1e3a3_0.tar.bz2#f75663534 https://conda.anaconda.org/main/osx-64/gsl-2.7.1-hdbe807d_1.tar.bz2#8d10f1c94483a865799b8166386cba71 https://conda.anaconda.org/conda-forge/osx-64/isa-l-2.30.0-h0d85af4_4.tar.bz2#e3b807a1a87e622538a5cd31be11fbf9 https://conda.anaconda.org/main/osx-64/jpeg-9e-hca72f7f_0.tar.bz2#60dd44b4f8b09370ebd52eca5b72a6c7 -https://conda.anaconda.org/conda-forge/osx-64/libcxx-14.0.6-hce7ea42_0.tar.bz2#ac504a8074ae8add8b837a93d7bc33ca -https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.10-h0d85af4_0.tar.bz2#98a4bf0073c56678453dd86845d3bcc9 +https://conda.anaconda.org/main/osx-64/libbrotlicommon-1.0.9-hca72f7f_7.tar.bz2#7dba7aa5b971febb55281659843586ba +https://conda.anaconda.org/main/osx-64/libcxx-14.0.6-h9765a3e_0.tar.bz2#af92f506156c9defc9b4dff41ecea1f9 +https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.13-h775f41a_0.tar.bz2#9751c945d288bc6cebcf91e934efd4aa https://conda.anaconda.org/main/osx-64/libev-4.33-h9ed2024_1.tar.bz2#849fdd014c201a9d2c2aa7c7db38a778 -https://conda.anaconda.org/conda-forge/noarch/libgfortran-devel_osx-64-9.3.0-h6c81a4c_23.tar.bz2#d66ac79367c179bf53520066430565e8 -https://conda.anaconda.org/main/osx-64/libgfortran-ng-3.0.1-h93005f0_2.tar.bz2#b9ee195bc9f08f7f301ba89753cf3e59 -https://conda.anaconda.org/main/osx-64/libiconv-1.16-hca72f7f_2.tar.bz2#c90372428e1e4eed4fdcd790979d06b4 +https://conda.anaconda.org/conda-forge/noarch/libgfortran-devel_osx-64-9.5.0-hc2d6858_25.tar.bz2#b124eb3b53fe93f865820c69b300d64c +https://conda.anaconda.org/conda-forge/osx-64/libiconv-1.17-hac89ed1_0.tar.bz2#691d103d11180486154af49c037b7ed9 https://conda.anaconda.org/main/osx-64/libtool-2.4.6-he9d5cce_1008.tar.bz2#919ef1cd3aa226d54ec682e936fe8910 https://conda.anaconda.org/main/osx-64/libuv-1.40.0-haf1e3a3_0.tar.bz2#619d1739110dd21b6c6d5f497fb843c7 -https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.2.3-hac89ed1_2.tar.bz2#61aed82bca4746f9d201712ca10a2c07 -https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.2.12-hfe4f2af_2.tar.bz2#1d676bbaa2599f22121dd5bbea455c2e -https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-14.0.4-ha654fa7_0.tar.bz2#5d5ab9ab83ce21422be84ecfd3142201 +https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.2.4-h775f41a_0.tar.bz2#28807bef802a354f9c164e7ab242c5cb +https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.2.12-hfd90126_3.tar.bz2#7fc40fa575092093bea11afa0c29ee10 +https://conda.anaconda.org/main/osx-64/llvm-openmp-14.0.6-h0dcd299_0.tar.bz2#5c740b4a18a558de0ccfe601703c423c https://conda.anaconda.org/conda-forge/osx-64/make-4.3-h22f3db7_1.tar.bz2#ac4a1dd58e6d821c518ae0011e8592b7 https://conda.anaconda.org/conda-forge/noarch/mscorefonts-0.0.1-3.tar.bz2#1e88c07eb93ef0b423c0acc694adcbef https://conda.anaconda.org/main/osx-64/ncurses-6.3-hca72f7f_3.tar.bz2#d561cc3036563ea3e25df461cba3cc52 https://conda.anaconda.org/conda-forge/osx-64/perl-5.32.1-2_h0d85af4_perl5.tar.bz2#dd13a8c2fac0cd8e102fcdc7bca1f077 https://conda.anaconda.org/main/osx-64/pixman-0.40.0-h9ed2024_1.tar.bz2#ecb58f854cae469740631c23643c9c95 -https://conda.anaconda.org/main/noarch/tzdata-2022a-hda174b7_0.tar.bz2#a99750e9650510d276b56d2b000f4569 -https://conda.anaconda.org/main/osx-64/xz-5.2.5-hca72f7f_1.tar.bz2#93e0326afd04cd186c1c504186084e70 -https://conda.anaconda.org/main/osx-64/zlib-1.2.12-h4dc903c_2.tar.bz2#c229f9e277891bb5d8c41082523cb64d -https://conda.anaconda.org/main/osx-64/brotli-1.0.9-hb1e8313_2.tar.bz2#2a310a7f0f0337a3953900be08438b06 -https://conda.anaconda.org/conda-forge/osx-64/expat-2.4.8-h96cf925_0.tar.bz2#529d357c143fb98b9af77d687f82a3e0 -https://conda.anaconda.org/bioconda/osx-64/fastp-0.23.2-h1c864af_2.tar.bz2#293a57a060eff6301314594613df6c22 +https://conda.anaconda.org/conda-forge/noarch/tzdata-2022d-h191b570_0.tar.bz2#456b5b1d99e7a9654b331bcd82e71042 +https://conda.anaconda.org/main/osx-64/xz-5.2.6-hca72f7f_0.tar.bz2#7ef814693308cc11af8f412caaaeaf9f +https://conda.anaconda.org/main/osx-64/zlib-1.2.12-h4dc903c_3.tar.bz2#6ef833cd86d5ba8254f731ae8ea8f8fc +https://conda.anaconda.org/main/osx-64/expat-2.4.9-he9d5cce_0.tar.bz2#49b93e578b929b87f43a15c63de595b5 +https://conda.anaconda.org/bioconda/osx-64/fastp-0.23.2-h15641a5_3.tar.bz2#1c9234fb9977b4e6dd408c08bc8fe7e0 https://conda.anaconda.org/main/noarch/fonts-anaconda-1-h8fa9717_0.tar.bz2#7a445a2d9fbe2a88de8f09e9527f4ce1 https://conda.anaconda.org/main/osx-64/gmp-6.2.1-he9d5cce_3.tar.bz2#e0c3ff3de3d5896b080f777be9badbbc https://conda.anaconda.org/main/osx-64/graphite2-1.3.14-he9d5cce_1.tar.bz2#620cdc76e94f58e22d16e8c9bbbd331d https://conda.anaconda.org/conda-forge/osx-64/icu-68.2-he49afe7_0.tar.bz2#c5de93735153d0d334a6b841e763fdb2 https://conda.anaconda.org/main/osx-64/isl-0.22.1-he9d5cce_3.tar.bz2#cd3138f80d8c0dcfff73e0243764aac5 -https://conda.anaconda.org/main/osx-64/lerc-3.0-he9d5cce_0.tar.bz2#d9e7e12a2bc017897a1a866e972a04de +https://conda.anaconda.org/conda-forge/osx-64/lerc-4.0.0-hb486fe8_0.tar.bz2#f9d6a4c82889d5ecedec1d90eb673c55 +https://conda.anaconda.org/main/osx-64/libbrotlidec-1.0.9-hca72f7f_7.tar.bz2#f78a158983f0bbaba56f34b976bc6dae +https://conda.anaconda.org/main/osx-64/libbrotlienc-1.0.9-hca72f7f_7.tar.bz2#36a1d885725d3ab4d1fadc5a29f978d2 https://conda.anaconda.org/main/osx-64/libedit-3.1.20210910-hca72f7f_0.tar.bz2#3c3da5b260a2b361ade4017f7776a9cf -https://conda.anaconda.org/main/osx-64/libffi-3.4.2-he9d5cce_4.tar.bz2#de2045de34211d4fa2d3abe1b4a2c780 +https://conda.anaconda.org/main/osx-64/libffi-3.3-hb1e8313_2.tar.bz2#0d866b17cc3b70288c14478faed26db7 https://conda.anaconda.org/main/osx-64/libgfortran5-11.2.0-h246ff09_26.tar.bz2#aec418d4f5f8bf6fd81ee64688c2c6a3 https://conda.anaconda.org/conda-forge/osx-64/libllvm13-13.0.1-h64f94b2_2.tar.bz2#bac1c6f12f44f40e19274e6e04e9bad5 https://conda.anaconda.org/conda-forge/osx-64/libllvm14-14.0.4-h41df66c_0.tar.bz2#fb8b5e6baa2ed5a65a4cfd992acffd04 -https://conda.anaconda.org/main/osx-64/libpng-1.6.37-ha441bb4_0.tar.bz2#256b1d19857d350dc549c64d6857c9a9 +https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.38-ha978bb4_0.tar.bz2#3954555b5b02b117119cb806d5dcfcd8 https://conda.anaconda.org/main/osx-64/lz4-c-1.9.3-h23ab428_1.tar.bz2#8ddb19388a0a9a6b9860d9dc495b5049 -https://conda.anaconda.org/conda-forge/osx-64/ninja-1.11.0-h1b54a9f_0.tar.bz2#02e4d7a0d1cda051ddf5e83725c4b2a6 -https://conda.anaconda.org/conda-forge/osx-64/openblas-0.3.1-hd130d23_0.tar.bz2#54535f43670ada1d83f75d57a8a0eb6a https://conda.anaconda.org/main/osx-64/openssl-1.1.1q-hca72f7f_0.tar.bz2#36499eb30b5facd90417ea74b2551c99 https://conda.anaconda.org/main/osx-64/pcre-8.45-h23ab428_0.tar.bz2#843db1a9eb10fcc9604c90b2e63272ee https://conda.anaconda.org/main/osx-64/pcre2-10.37-he7042d7_1.tar.bz2#9eae60b5dcb24bb8d36304e32cd94a04 -https://conda.anaconda.org/main/osx-64/pkg-config-0.29.2-h3efe00b_8.tar.bz2#3b186bff02439ca4238d5119a6db2606 https://conda.anaconda.org/main/osx-64/readline-8.1.2-hca72f7f_1.tar.bz2#59c80f25f83042f591ba630c1a52ca68 -https://conda.anaconda.org/main/osx-64/tapi-1100.0.11-h0025ef7_0.tar.bz2#57f7e2d52d273161ee20cced55a425a8 -https://conda.anaconda.org/main/osx-64/tbb-2020.3-h879752b_0.tar.bz2#495f2debd589d580e6d768bc497d8b9d +https://conda.anaconda.org/main/osx-64/tapi-1100.0.11-h1527ee5_1.tar.bz2#e1196997138701768732ba6cb54227c7 +https://conda.anaconda.org/conda-forge/osx-64/tbb-2021.6.0-hb8565cd_0.tar.bz2#e08aeee1188c571aae0b1e8dd11d3fa4 https://conda.anaconda.org/main/osx-64/tk-8.6.12-h5d9f67b_0.tar.bz2#d5b05e38d3bc8fe6d9bcdd91029db1c7 -https://conda.anaconda.org/conda-forge/osx-64/blas-1.1-openblas.tar.bz2#794dbf30b6f568889ba6b04a50fd7989 +https://conda.anaconda.org/main/osx-64/brotli-bin-1.0.9-hca72f7f_7.tar.bz2#caf1073dc2ca5aeb832a2877394eae12 https://conda.anaconda.org/conda-forge/osx-64/bwidget-1.9.14-h694c41f_1.tar.bz2#bfebc1f663c38331b8d0dca32a515e95 https://conda.anaconda.org/main/noarch/fonts-conda-ecosystem-1-hd3eb1b0_0.tar.bz2#81135874e3942ccc48b79c12ec094849 -https://conda.anaconda.org/main/osx-64/freetype-2.11.0-hd8bbffd_0.tar.bz2#161a798f97f48e75b94e831cdf75fc41 +https://conda.anaconda.org/conda-forge/osx-64/freetype-2.12.1-h3f81eb7_0.tar.bz2#6afb5b1664496c575117efe9aa2c9ba9 https://conda.anaconda.org/conda-forge/osx-64/krb5-1.19.3-hb49756b_0.tar.bz2#e60363be26ab2a74326c06195d638447 https://conda.anaconda.org/conda-forge/osx-64/libclang-cpp14-14.0.4-default_h55ffa42_0.tar.bz2#e26b7a841f7c4b1612b8df173bfcb2ad https://conda.anaconda.org/main/osx-64/libgfortran-5.0.0-11_2_0_h246ff09_26.tar.bz2#08bc8279e746e83bb38dbf815b1ec2a9 -https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.47.0-h942079c_0.tar.bz2#86fc370e607a269b64ac6fa5d29e55e8 +https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.47.0-h7cbc4dc_1.tar.bz2#a2bb3253049f51bc521d748451536d70 https://conda.anaconda.org/main/osx-64/libssh2-1.10.0-h0a4fc7d_0.tar.bz2#4041fb6cbb94d10dcf1e6dd85e8c0584 https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.9.12-h93ec3fd_0.tar.bz2#3bb7e5cd72ee6520ce6e26600a1283db https://conda.anaconda.org/conda-forge/osx-64/llvm-tools-14.0.4-h41df66c_0.tar.bz2#766281931b17e3f609abec4e0bbfa282 https://conda.anaconda.org/conda-forge/osx-64/mpfr-4.1.0-h0f52abe_1.tar.bz2#afe26b08c2d2265b4d663d199000e5da https://conda.anaconda.org/main/osx-64/nodejs-16.13.1-h470481b_0.tar.bz2#e7cf42ec09c1e7e317f715e8fdc274e2 https://conda.anaconda.org/conda-forge/osx-64/sigtool-0.1.3-h57ddcff_0.tar.bz2#e0dcb354df4ad4f79539d57e4f8ec1bd -https://conda.anaconda.org/conda-forge/osx-64/sqlite-3.39.2-hd9f0692_0.tar.bz2#eea4504fd46050a22d482addde6ae7c7 +https://conda.anaconda.org/main/osx-64/sqlite-3.39.3-h707629a_0.tar.bz2#938988d6b33f7a7fadf3302d74f0b91f https://conda.anaconda.org/main/osx-64/tktable-2.10-h1de35cc_0.tar.bz2#38711ae78e8c78aaa729be7f8d234122 https://conda.anaconda.org/main/osx-64/zstd-1.5.2-hcb37349_0.tar.bz2#0cf0e49baee6e0bc5c90983826cb22d4 +https://conda.anaconda.org/main/osx-64/brotli-1.0.9-hca72f7f_7.tar.bz2#31d6d04cd2388d8f19004501271b3545 https://conda.anaconda.org/conda-forge/osx-64/clang-14-14.0.4-default_h55ffa42_0.tar.bz2#bb9de1537a65d6dc6a25fb9f81f64bae https://conda.anaconda.org/conda-forge/osx-64/fontconfig-2.13.94-h10f422b_0.tar.bz2#04362a8c05bc8daac6b004979029a19b https://conda.anaconda.org/main/osx-64/gettext-0.21.0-h7535e17_0.tar.bz2#ea416bbaf2bd162d9dcc580f33bfe2fd https://conda.anaconda.org/conda-forge/osx-64/ld64_osx-64-609-h1e06c2b_10.tar.bz2#7923b80d48e60eb74976654192964ee6 -https://conda.anaconda.org/conda-forge/osx-64/libcurl-7.83.1-h372c54d_0.tar.bz2#393526f4c307ded911957e3723e34bed -https://conda.anaconda.org/main/osx-64/libopenblas-0.3.20-h9a5756b_1.tar.bz2#edbf06a59784def8303f891fc50f8009 -https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.4.0-hfca7e8f_0.tar.bz2#7b6d99d80fa5cbd448d84cbafa026425 +https://conda.anaconda.org/main/osx-64/libcurl-7.84.0-h6dfd666_0.tar.bz2#0fd33d6eaabaa1a7be5686946c92743c +https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.21-openmp_h947e540_2.tar.bz2#437823acf722bea4a3c842002e502e93 +https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.4.0-h5e0c7b4_3.tar.bz2#dc8de12a82ac940edaaee12803b86c42 https://conda.anaconda.org/conda-forge/osx-64/libxslt-1.1.33-h5739fc3_2.tar.bz2#14333dae9eef1116162a1bb6119d3537 https://conda.anaconda.org/conda-forge/osx-64/mpc-1.2.1-hbb51d92_0.tar.bz2#9f46d6ad4c460679ee997abc10da3bac -https://conda.anaconda.org/conda-forge/osx-64/python-3.9.13-h57e37ff_0_cpython.tar.bz2#0fced1dc8919c66139c45d1e2d8dc6a6 +https://conda.anaconda.org/main/osx-64/python-3.9.13-hdfd78df_1.tar.bz2#cd973b4386d2561d37ea607edf7ddab0 https://conda.anaconda.org/conda-forge/noarch/bagit-1.8.1-pyhd8ed1ab_0.tar.bz2#fb50f22b353ed3a12f5e8b7c01b58832 https://conda.anaconda.org/conda-forge/osx-64/cctools_osx-64-973.0.1-h3eff9a4_10.tar.bz2#a7552b2de2f52bbf8b726d982507b1ef -https://conda.anaconda.org/main/osx-64/certifi-2022.6.15-py39hecd8cb5_0.tar.bz2#f9fef6c73823920f42b9cad0b4ba42bc -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.1.0-pyhd8ed1ab_0.tar.bz2#abc0453b6e7bfbb87d275d58e333fc98 +https://conda.anaconda.org/conda-forge/noarch/certifi-2022.9.24-pyhd8ed1ab_0.tar.bz2#f66309b099374af91369e67e84af397d +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.1.1-pyhd8ed1ab_0.tar.bz2#c1d5b294fbf9a795dec349a6f4d8be8e https://conda.anaconda.org/conda-forge/osx-64/clang-14.0.4-h694c41f_0.tar.bz2#7c8851ee02c340f08b9f3d874c3b65a1 -https://conda.anaconda.org/conda-forge/osx-64/curl-7.83.1-h372c54d_0.tar.bz2#751e7246045a30deda6aa999c16f6cd4 +https://conda.anaconda.org/main/osx-64/curl-7.84.0-hca72f7f_0.tar.bz2#0d818bbef7ab86cb61d57c98f53fd916 https://conda.anaconda.org/main/noarch/cycler-0.11.0-pyhd3eb1b0_0.tar.bz2#dfb0ca8055c9a72aaca7b845a6edea17 -https://conda.anaconda.org/main/noarch/dataclasses-0.8-pyh6d0b6a4_7.tar.bz2#c8c177c84b3e5e6145156045660868e9 -https://conda.anaconda.org/conda-forge/osx-64/gfortran_impl_osx-64-9.3.0-h9cc0e5e_23.tar.bz2#e906ccf40e156c6aa1e7804a1db4b849 +https://conda.anaconda.org/main/osx-64/cython-0.29.24-py39he9d5cce_0.tar.bz2#ddb78fb9df07520276059e78bf6ed66e +https://conda.anaconda.org/conda-forge/osx-64/gfortran_impl_osx-64-9.5.0-h2221f41_25.tar.bz2#37710661b9bff8d53a019a78d9638959 https://conda.anaconda.org/main/osx-64/humanfriendly-10.0-py39hecd8cb5_0.tar.bz2#ae9a7721b804ce10ee0323aa59ef19de -https://conda.anaconda.org/main/noarch/idna-3.3-pyhd3eb1b0_0.tar.bz2#f2d821eedf78bfe0e9f1d8faf9c8b23e +https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed https://conda.anaconda.org/main/osx-64/lcms2-2.12-hf1fd2bf_0.tar.bz2#819762fdace9397c0e14a9ed2dcdb829 -https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-15_osx64_openblas.tar.bz2#48c26d27a96c71cfae2621311aa06215 +https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-16_osx64_openblas.tar.bz2#644d63e9379867490b67bace400b2a0f https://conda.anaconda.org/main/osx-64/libgd-2.3.3-he7e2580_1.tar.bz2#1256140bf83fffd8eaf48b9cb5036486 -https://conda.anaconda.org/conda-forge/osx-64/libglib-2.72.1-hfbcb929_0.tar.bz2#df60046c5ef9df0b5e2b2047915c934d -https://conda.anaconda.org/conda-forge/osx-64/libwebp-1.2.3-hf64df63_1.tar.bz2#6709a2e06240f9112b7185cc2c1dfa7e +https://conda.anaconda.org/conda-forge/osx-64/libglib-2.68.4-hd556434_0.tar.bz2#8e59bd8f9587320036d6dc88c438971f +https://conda.anaconda.org/conda-forge/osx-64/libwebp-1.2.4-hfa4350a_0.tar.bz2#d3b5a56369701e6a11bf300ba3394391 https://conda.anaconda.org/main/osx-64/lockfile-0.12.2-py39hecd8cb5_0.tar.bz2#4f00c99cf628547ceb0c9090a3e57988 https://conda.anaconda.org/main/osx-64/mistune-0.8.4-py39h9ed2024_1000.tar.bz2#2a4d604717a647ad21af766289cbbfc3 https://conda.anaconda.org/main/noarch/munkres-1.1.4-py_0.tar.bz2#67857cc5e9044770d2b15f9d94a4521d https://conda.anaconda.org/main/osx-64/mypy_extensions-0.4.3-py39hecd8cb5_1.tar.bz2#90d6a538bac7eee8a63b436d5c334384 -https://conda.anaconda.org/conda-forge/noarch/networkx-2.8.5-pyhd8ed1ab_0.tar.bz2#4359cc8df0609a2b88aec55d6d2fedaf -https://conda.anaconda.org/conda-forge/noarch/pathspec-0.9.0-pyhd8ed1ab_0.tar.bz2#f93dc0ccbc0a8472624165f6e256c7d1 -https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.2-pyhd8ed1ab_1.tar.bz2#2fb3f88922e7aec26ba652fcdfe13950 +https://conda.anaconda.org/conda-forge/noarch/networkx-2.8.6-pyhd8ed1ab_0.tar.bz2#52243eec8c5b9d22924d1c463b01a1d8 +https://conda.anaconda.org/conda-forge/osx-64/openblas-0.3.21-openmp_h2bb6d6c_2.tar.bz2#4cba23db22abbac1500d5bcfb894760b +https://conda.anaconda.org/conda-forge/noarch/pathspec-0.10.1-pyhd8ed1ab_0.tar.bz2#2357d88e11ea38523d50b9e2fc98dfcf +https://conda.anaconda.org/main/osx-64/platformdirs-2.5.2-py39hecd8cb5_0.tar.bz2#736c2b4161ae4f94b344b0b65467eac2 https://conda.anaconda.org/main/noarch/pycparser-2.21-pyhd3eb1b0_0.tar.bz2#e70944ff53d19af4f65073a4f21c7b05 -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.0.9-pyhd8ed1ab_0.tar.bz2#e8fbc1b54b25f4b08281467bc13b70cc +https://conda.anaconda.org/main/osx-64/pyparsing-3.0.9-py39hecd8cb5_0.tar.bz2#bcaea6d4a47e9c874becaa700c78dcf7 https://conda.anaconda.org/main/osx-64/pysocks-1.7.1-py39hecd8cb5_0.tar.bz2#113a443b9c706f9f9c6187c0eaa3de27 https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.9-2_cp39.tar.bz2#262f557ee8ca777fe2190956038024cd -https://conda.anaconda.org/main/osx-64/pytz-2022.1-py39hecd8cb5_0.tar.bz2#a2bbef1b1076826f7c9d3c457da6899c +https://conda.anaconda.org/conda-forge/noarch/pytz-2022.2.1-pyhd8ed1ab_0.tar.bz2#974bca71d00364630f63f31fa7e059cb https://conda.anaconda.org/main/osx-64/ruamel.yaml.clib-0.2.6-py39hca72f7f_0.tar.bz2#1c843886b427eb0491c0b5436909ef30 https://conda.anaconda.org/bioconda/noarch/shellescape-3.4.1-py_1.tar.bz2#fe54078488e8cb18154999306fa28742 https://conda.anaconda.org/main/noarch/six-1.16.0-pyhd3eb1b0_1.tar.bz2#0a3ab834ba49016e183e715bff48a2b6 https://conda.anaconda.org/main/osx-64/tomli-2.0.1-py39hecd8cb5_0.tar.bz2#1aee46dd32a05da622335a3d5dd5c031 -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.3.0-pyha770c72_0.tar.bz2#a9d85960bc62d53cc4ea0d1d27f73c98 +https://conda.anaconda.org/main/osx-64/tornado-6.2-py39hca72f7f_0.tar.bz2#56a1f3207b968f18a3cbebb6a1d1c023 +https://conda.anaconda.org/main/osx-64/typing_extensions-4.3.0-py39hecd8cb5_0.tar.bz2#633ce81dbae265cee67bdb4209f321e8 https://conda.anaconda.org/main/osx-64/webencodings-0.5.1-py39hecd8cb5_1.tar.bz2#41f445e36b6b6722a9444508eef069f8 https://conda.anaconda.org/main/noarch/wheel-0.37.1-pyhd3eb1b0_0.tar.bz2#9800fd2a86354f58731a7ad0eb5dd537 -https://conda.anaconda.org/main/osx-64/zipp-3.8.0-py39hecd8cb5_0.tar.bz2#b582c0d2087797ff5e9f9165ff33861d -https://conda.anaconda.org/bioconda/osx-64/bowtie-1.3.1-py39h888ac62_2.tar.bz2#9cf9bcf6628214828bced3549f64daf0 -https://conda.anaconda.org/conda-forge/osx-64/cffi-1.15.1-py39hae9ecf2_0.tar.bz2#558979c1c4b8d8912e4ec89b711ffe55 +https://conda.anaconda.org/conda-forge/noarch/zipp-3.8.1-pyhd8ed1ab_0.tar.bz2#a3508a0c850745b875de88aea4c40cc5 +https://conda.anaconda.org/conda-forge/osx-64/atk-1.0-2.36.0-he69c4ee_4.tar.bz2#7b97814b391b446512be7df30c90ad26 +https://conda.anaconda.org/bioconda/osx-64/bowtie-1.3.1-py39h8a1885a_4.tar.bz2#1409e66bc6389cbf2764ee349541c53c +https://conda.anaconda.org/main/osx-64/cffi-1.15.1-py39hc55c11b_0.tar.bz2#aec6df7253dcae13b2f8b9db21f48ff0 https://conda.anaconda.org/conda-forge/osx-64/clangxx-14.0.4-default_h55ffa42_0.tar.bz2#edefb0f4aa4e6dcc7f2d5436edc9e17e https://conda.anaconda.org/conda-forge/osx-64/click-8.1.3-py39h6e9494a_0.tar.bz2#c33ff559cea7a89797decd6db3716b77 https://conda.anaconda.org/main/osx-64/coloredlogs-15.0.1-py39hecd8cb5_0.tar.bz2#cc4b7bf82eb060071d5b17997cdc2217 -https://conda.anaconda.org/conda-forge/osx-64/g-ir-build-tools-1.72.0-py39h22ba2f8_1.tar.bz2#bbb83b9a1adeb457442ffcc3506a50d7 -https://conda.anaconda.org/conda-forge/osx-64/glib-tools-2.72.1-h2292cb8_0.tar.bz2#0b18da64eeec4eeedf3f5c9d48a1b5c1 +https://conda.anaconda.org/conda-forge/osx-64/gdk-pixbuf-2.42.6-h2e6141f_0.tar.bz2#be5dd0a4ffc76407baa987e0d99d6c97 +https://conda.anaconda.org/conda-forge/osx-64/glib-tools-2.68.4-he49afe7_0.tar.bz2#61f9fefa0cf81d0ced6185be9dd73983 +https://conda.anaconda.org/conda-forge/osx-64/gts-0.7.6-hccb3bdf_2.tar.bz2#ad799f4bcdacc510347c2d9d19b40bc3 https://conda.anaconda.org/main/noarch/html5lib-1.1-pyhd3eb1b0_0.tar.bz2#7a9fa936878258757f23ffe4b2e8ecf7 https://conda.anaconda.org/conda-forge/osx-64/importlib-metadata-4.11.4-py39h6e9494a_0.tar.bz2#adc0fa796414c4235c3ba372d2c93c26 https://conda.anaconda.org/conda-forge/noarch/isodate-0.6.1-pyhd8ed1ab_0.tar.bz2#4a62c93c1b5c0b920508ae3fd285eaf5 https://conda.anaconda.org/conda-forge/osx-64/keepalive-0.5-py39h6e9494a_5.tar.bz2#43beac10d8293bcf51b3d44804e6c1a0 https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.4.4-py39h7c694c3_0.tar.bz2#2c258bc5beb4eeb7dda38e4741869d9c -https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-15_osx64_openblas.tar.bz2#2d26f8ba1e89bcc2704ac7a38d4cc74c -https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.9.0-15_osx64_openblas.tar.bz2#8679c41f1fcb5b54aaecce18ec3e1875 +https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-16_osx64_openblas.tar.bz2#28592eab0f05bcf9969789e87f754e11 +https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.9.0-16_osx64_openblas.tar.bz2#406ad426aade5578b90544cc2ed4a79b https://conda.anaconda.org/conda-forge/osx-64/lxml-4.8.0-py39h63b48b0_2.tar.bz2#b0e98d0e6d6d70b707c62824e069ed61 https://conda.anaconda.org/conda-forge/osx-64/msgpack-python-1.0.4-py39h7c694c3_0.tar.bz2#ca68f51fbd9d873d14cf51167d3d9ade https://conda.anaconda.org/main/noarch/packaging-21.3-pyhd3eb1b0_0.tar.bz2#e482aa236f5541461d285cc979323035 https://conda.anaconda.org/main/osx-64/pillow-9.2.0-py39hde71d04_1.tar.bz2#1becd657bb1462b619cf734d4b546355 https://conda.anaconda.org/conda-forge/osx-64/psutil-5.9.1-py39h701faf5_0.tar.bz2#3c1cb37ed569be8ddf1e26da088b30d2 -https://conda.anaconda.org/bioconda/osx-64/pysam-0.19.1-py39h7ab3854_0.tar.bz2#31e20c36976c6312393c4cec3ba13fa0 +https://conda.anaconda.org/bioconda/osx-64/pysam-0.19.1-py39h8aa7adb_1.tar.bz2#783fc0cf097f5f78eaaf7cb0d81a38ce https://conda.anaconda.org/main/noarch/python-dateutil-2.8.2-pyhd3eb1b0_0.tar.bz2#2eb923cc014094f4acf7f849d67d73f8 https://conda.anaconda.org/conda-forge/osx-64/setuptools-63.1.0-py39h6e9494a_0.tar.bz2#e793bfac1b0a61a8cd88058f98208a6e -https://conda.anaconda.org/conda-forge/osx-64/tornado-6.2-py39h701faf5_0.tar.bz2#b11061c839af942c9f7767b0beb0f254 -https://conda.anaconda.org/conda-forge/osx-64/typed-ast-1.5.4-py39h701faf5_0.tar.bz2#9acade6a70c88198b48eb567a2df05d3 https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-14.0.0-py39h63b48b0_1.tar.bz2#19c1ac41e067315d023bed42e214c8ea -https://conda.anaconda.org/conda-forge/noarch/black-22.6.0-pyhd8ed1ab_0.tar.bz2#f8fb78b07826bbf84deda3cf128cfbe2 +https://conda.anaconda.org/conda-forge/osx-64/black-22.8.0-py39h6e9494a_0.tar.bz2#077112addbba9ee5cc51763e4844e478 https://conda.anaconda.org/main/osx-64/brotlipy-0.7.0-py39h9ed2024_1003.tar.bz2#ca2e4f5e0530e1b179958bb1b7116016 https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-64-14.0.4-h6df654d_0.tar.bz2#39c0169a3ab059d48b033a389becdfdc https://conda.anaconda.org/conda-forge/osx-64/cryptography-37.0.4-py39h9c2a9ce_0.tar.bz2#fbda1a6b92bc0a98f2564319682e8988 -https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.34.4-py39h701faf5_0.tar.bz2#d4d9ef66a5dca053a1eb3c06069987ba -https://conda.anaconda.org/conda-forge/osx-64/glib-2.72.1-h2292cb8_0.tar.bz2#3a991bc5c03b34f6cbb2acebac885db9 +https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.37.3-py39ha30fb19_0.tar.bz2#1b808f863d2d1dcd2abde339e7a4bb22 +https://conda.anaconda.org/conda-forge/osx-64/glib-2.68.4-he49afe7_0.tar.bz2#6f99fa3f2bdfbd3c3eb36795c0b6f52a https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-4.11.4-hd8ed1ab_0.tar.bz2#9a1925fdb91c81437b8012e48ede6851 -https://conda.anaconda.org/conda-forge/osx-64/numpy-1.23.1-py39hda39a74_0.tar.bz2#211ba9d7690a13e512b118d0576c4551 +https://conda.anaconda.org/conda-forge/osx-64/liblapacke-3.9.0-16_osx64_openblas.tar.bz2#fdde3ba87373e04a109c58b229676f8f https://conda.anaconda.org/main/osx-64/pip-22.1.2-py39hecd8cb5_0.tar.bz2#8721239788552789d98dc76295ffeb5f https://conda.anaconda.org/conda-forge/osx-64/ruamel.yaml-0.17.16-py39h89e85a6_0.tar.bz2#1c5ab4b00996df5579f24c3155b94ed5 https://conda.anaconda.org/conda-forge/osx-64/sparqlwrapper-1.8.5-py39h6e9494a_1006.tar.bz2#e1f785a09fb7ffde303c09e11bef4b94 https://conda.anaconda.org/conda-forge/noarch/argcomplete-2.0.0-pyhd8ed1ab_0.tar.bz2#e3ba29a61c20f2c9d085db29ea657406 -https://conda.anaconda.org/main/osx-64/bottleneck-1.3.5-py39h67323c0_0.tar.bz2#0d79760d544d0bd8acb4adc4ef813418 -https://conda.anaconda.org/main/osx-64/cairo-1.16.0-h691a603_2.tar.bz2#01b66631664d07946eabb1f1f17c0d36 +https://conda.anaconda.org/conda-forge/osx-64/blas-devel-3.9.0-16_osx64_openblas.tar.bz2#d3e6ab5f5bf356227e2816b0f9c48bad +https://conda.anaconda.org/main/osx-64/cairo-1.16.0-h8023c5d_1.tar.bz2#6773b4a22f7d6dceef607adf48ad156d https://conda.anaconda.org/conda-forge/osx-64/compiler-rt-14.0.4-h7fcd477_0.tar.bz2#d81ce4ea83433bea77d3977c939a6e59 -https://conda.anaconda.org/main/osx-64/gdk-pixbuf-2.42.8-hec4cd9e_0.tar.bz2#117ce16772868d877920178bd694a261 -https://conda.anaconda.org/main/osx-64/gts-0.7.6-h6759243_3.tar.bz2#ab667044e8133d96e27ff4689dcb86f3 -https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.5.2-py39h64a0072_0.tar.bz2#58b54971c408f8c359112350923365e1 -https://conda.anaconda.org/main/osx-64/numexpr-2.8.3-py39h0f1bd0b_0.tar.bz2#023ec981560b869c70b3f9be4eaf45a6 https://conda.anaconda.org/main/noarch/pyopenssl-22.0.0-pyhd3eb1b0_0.tar.bz2#24300c22dced4609b77f3ace93b5beeb +https://conda.anaconda.org/conda-forge/osx-64/blas-2.116-openblas.tar.bz2#34b710f5d7e0712f49a4971762718029 https://conda.anaconda.org/conda-forge/osx-64/clang_osx-64-14.0.4-h3a95cd4_2.tar.bz2#a069b871f2df19703e46b0e56626c3c9 -https://conda.anaconda.org/conda-forge/osx-64/harfbuzz-3.1.1-h159f659_0.tar.bz2#78bfa25e78c846358a8f7bfd619f926f -https://conda.anaconda.org/bioconda/osx-64/htseq-2.0.2-py39h6041a6e_0.tar.bz2#df827aa362726e693bbcbc127373bb8b -https://conda.anaconda.org/conda-forge/osx-64/libgirepository-1.72.0-h0bde3a9_1.tar.bz2#5d2eba5a95e773cdb80aff961b9a0b76 -https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.5.2-py39h6e9494a_0.tar.bz2#ec60569d63f655c498371e4774ba8b26 -https://conda.anaconda.org/main/osx-64/pandas-1.4.3-py39he9d5cce_0.tar.bz2#fcd3e91845db1728f2c64b0c2e26c7d3 -https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.11-pyhd8ed1ab_0.tar.bz2#0738978569b10669bdef41c671252dd1 +https://conda.anaconda.org/conda-forge/osx-64/harfbuzz-3.0.0-h159f659_1.tar.bz2#a66f3a32f19f91a61e18b55cee3a0aa8 +https://conda.anaconda.org/main/osx-64/urllib3-1.26.11-py39hecd8cb5_0.tar.bz2#25514a79e96b92ada394bc7fcfea64b6 https://conda.anaconda.org/conda-forge/osx-64/clangxx_osx-64-14.0.4-he1dbc44_2.tar.bz2#a82006ea176018f37f7c34c3c3d08630 -https://conda.anaconda.org/conda-forge/osx-64/g-ir-host-tools-1.72.0-h5734cbb_1.tar.bz2#c5bda43b26ba25527ed90babb6837484 -https://conda.anaconda.org/conda-forge/osx-64/gfortran_osx-64-9.3.0-h18f7dce_15.tar.bz2#48f985e599ff223cd8acea3595d2cbe5 +https://conda.anaconda.org/conda-forge/osx-64/gfortran_osx-64-9.5.0-h18f7dce_0.tar.bz2#a8676540ce4e469819cd80c5b7e8575e +https://conda.anaconda.org/main/osx-64/numpy-base-1.23.1-py39hbda7086_0.tar.bz2#beba856098dd929e0a2613abab7bf744 https://conda.anaconda.org/conda-forge/osx-64/pango-1.48.10-h056538c_2.tar.bz2#223f4846f40a8c14467bddb24f652985 https://conda.anaconda.org/main/osx-64/requests-2.28.1-py39hecd8cb5_0.tar.bz2#06accbedfc64e52e70623173902b5551 https://conda.anaconda.org/conda-forge/noarch/cachecontrol-0.12.11-pyhd8ed1ab_0.tar.bz2#6eefee9888f33f150b5d44d616b1a613 -https://conda.anaconda.org/conda-forge/osx-64/gobject-introspection-1.72.0-py39h139ca28_1.tar.bz2#45c46aa64eff9b4083b4be15973e6deb -https://conda.anaconda.org/conda-forge/osx-64/librsvg-2.52.5-h02b643e_1.tar.bz2#de47515a0bf98b4903e65dd53d07996c +https://conda.anaconda.org/conda-forge/osx-64/gtk2-2.24.33-h675d97a_1.tar.bz2#5042a5243c79120f5970ddf050e60bb4 +https://conda.anaconda.org/conda-forge/osx-64/librsvg-2.52.2-h02b643e_0.tar.bz2#5279c2320483aa4637923bd59e015f01 +https://conda.anaconda.org/main/osx-64/numpy-1.23.1-py39h0f1bd0b_0.tar.bz2#a611b48967f2f29e0b0391e55dcfcffd https://conda.anaconda.org/conda-forge/osx-64/r-base-4.1.1-h65845f3_1.tar.bz2#224d1b54bf09c33f27427327fa8b5d80 https://conda.anaconda.org/conda-forge/osx-64/rdflib-6.0.2-py39h6e9494a_0.tar.bz2#63879b5da7907f45f78626b6457ea3ae -https://conda.anaconda.org/main/osx-64/atk-1.0-2.36.0-hb642b71_0.tar.bz2#b8eac97ee6c0f859891a17db8677688e https://conda.anaconda.org/bioconda/noarch/bioconductor-biocgenerics-0.40.0-r41hdfd78af_0.tar.bz2#73c383d354e038bb8b4de75715747071 https://conda.anaconda.org/bioconda/noarch/bioconductor-genomeinfodbdata-1.2.7-r41hdfd78af_2.tar.bz2#b23c1905f6ee6dddcfa3af515aa4844f -https://conda.anaconda.org/bioconda/osx-64/bioconductor-zlibbioc-1.40.0-r41haba8685_1.tar.bz2#c3eb61b118b5d0962ea3df755c5cd055 +https://conda.anaconda.org/bioconda/osx-64/bioconductor-zlibbioc-1.40.0-r41h3be46a4_2.tar.bz2#163877a18aa0fd4b14445a37039a894e +https://conda.anaconda.org/main/osx-64/bottleneck-1.3.5-py39h67323c0_0.tar.bz2#0d79760d544d0bd8acb4adc4ef813418 +https://conda.anaconda.org/conda-forge/osx-64/graphviz-2.49.1-h77de9ca_0.tar.bz2#277e7399cedcb9202336667a026d39f3 +https://conda.anaconda.org/main/osx-64/matplotlib-base-3.5.2-py39hfb0c5b7_0.tar.bz2#a252ab3507e565e3efe7c5b113da384f +https://conda.anaconda.org/main/osx-64/numexpr-2.8.3-py39h0f1bd0b_0.tar.bz2#023ec981560b869c70b3f9be4eaf45a6 https://conda.anaconda.org/conda-forge/osx-64/r-backports-1.4.1-r41h28b5c78_0.tar.bz2#9c442653219c8fa9e4a9ed2a95644a4d https://conda.anaconda.org/conda-forge/noarch/r-bh-1.78.0_0-r41hc72bb7e_0.tar.bz2#4963f5da888bdbfb202b3612e11ba972 https://conda.anaconda.org/conda-forge/osx-64/r-bit-4.0.4-r41h28b5c78_0.tar.bz2#5a9176c391151c62561dd0d4f8f93abb @@ -199,7 +197,7 @@ https://conda.anaconda.org/conda-forge/noarch/r-crayon-1.5.1-r41hc72bb7e_0.tar.b https://conda.anaconda.org/conda-forge/osx-64/r-curl-4.3.2-r41h28b5c78_0.tar.bz2#a58249fbbac38b0703b5daefc94c8410 https://conda.anaconda.org/conda-forge/noarch/r-dbi-1.1.3-r41hc72bb7e_0.tar.bz2#844d1b00d0665a78c7a0bc55c5c038b2 https://conda.anaconda.org/conda-forge/osx-64/r-digest-0.6.29-r41h9951f98_0.tar.bz2#be0cbf1e80ee75a3a62ee35437995211 -https://conda.anaconda.org/conda-forge/noarch/r-evaluate-0.15-r41hc72bb7e_0.tar.bz2#c85313c76eb5e48dc016402d32567494 +https://conda.anaconda.org/conda-forge/noarch/r-evaluate-0.16-r41hc72bb7e_0.tar.bz2#4e6d48071b7aee6d8f4e82b72f082fc6 https://conda.anaconda.org/conda-forge/osx-64/r-fansi-1.0.3-r41h0f1d5c4_0.tar.bz2#e3abc84adc3f397a9f40f20a223b20ae https://conda.anaconda.org/conda-forge/osx-64/r-farver-2.1.1-r41h8619c4b_0.tar.bz2#ae637d1b8b18514f16a8f2c9076ce5b7 https://conda.anaconda.org/conda-forge/osx-64/r-fastmap-1.1.0-r41h9951f98_0.tar.bz2#b7a8bb6f3b19006349968d8e371fdf57 @@ -207,12 +205,12 @@ https://conda.anaconda.org/conda-forge/noarch/r-formatr-1.12-r41hc72bb7e_0.tar.b https://conda.anaconda.org/conda-forge/osx-64/r-fs-1.5.2-r41hc4bb905_1.tar.bz2#04bb08d1f7ae127ac52011eb9f372b33 https://conda.anaconda.org/conda-forge/noarch/r-futile.options-1.0.1-r41hc72bb7e_1002.tar.bz2#d8ce292272b4a660fe3ac7ee93c13c9f https://conda.anaconda.org/conda-forge/osx-64/r-glue-1.6.2-r41h0f1d5c4_0.tar.bz2#0cd33286679ce646aecaf1371f48556e -https://conda.anaconda.org/conda-forge/noarch/r-gtable-0.3.0-r41hc72bb7e_3.tar.bz2#c2265bc91dceb28f25d533e9d93212fa +https://conda.anaconda.org/conda-forge/noarch/r-gtable-0.3.1-r41hc72bb7e_0.tar.bz2#db42f11efeba36388f28393a629c8fde https://conda.anaconda.org/conda-forge/osx-64/r-jsonlite-1.8.0-r41h0f1d5c4_0.tar.bz2#c9ab2e0fcf48172bcac6a2b7258743b6 https://conda.anaconda.org/conda-forge/noarch/r-labeling-0.4.2-r41hc72bb7e_1.tar.bz2#7536c98f2faf4bf5faf4334fb79f1328 https://conda.anaconda.org/conda-forge/osx-64/r-lattice-0.20_45-r41h28b5c78_0.tar.bz2#2d3f0843eadc140eeaa1240d18cb7a00 https://conda.anaconda.org/conda-forge/osx-64/r-magrittr-2.0.3-r41h0f1d5c4_0.tar.bz2#1f7e4c1ceb1d87918215be62158bf4a3 -https://conda.anaconda.org/conda-forge/osx-64/r-mass-7.3_58-r41h67d6963_0.tar.bz2#f2059aeaa6e718ba32fd3059f8eb635f +https://conda.anaconda.org/conda-forge/osx-64/r-mass-7.3_58.1-r41hef1f586_0.tar.bz2#b6e487bafc95644d18eea55e60540520 https://conda.anaconda.org/conda-forge/osx-64/r-matrixstats-0.62.0-r41h0f1d5c4_0.tar.bz2#6aa64de6126f9d2438d76a709dc15673 https://conda.anaconda.org/conda-forge/osx-64/r-mime-0.12-r41h28b5c78_0.tar.bz2#bfa140172a53ceb2df480fa6cce89891 https://conda.anaconda.org/conda-forge/noarch/r-pkgconfig-2.0.3-r41hc72bb7e_1.tar.bz2#2c4f78477753ad83700a1f9d605a3002 @@ -222,65 +220,66 @@ https://conda.anaconda.org/conda-forge/noarch/r-praise-1.0.0-r41hc72bb7e_1005.ta https://conda.anaconda.org/conda-forge/osx-64/r-ps-1.7.1-r41h67d6963_0.tar.bz2#73130fd50411693eef6516435eda8ec5 https://conda.anaconda.org/conda-forge/noarch/r-r6-2.5.1-r41hc72bb7e_0.tar.bz2#a560798c2d5ab0dbb30feddc6b8e63fb https://conda.anaconda.org/conda-forge/noarch/r-rcolorbrewer-1.1_3-r41h785f33e_0.tar.bz2#579b6792713b6c8538c1aa8e4ce636c0 -https://conda.anaconda.org/conda-forge/osx-64/r-rcpp-1.0.9-r41h8619c4b_0.tar.bz2#baed54b731118f5628b6adc6f42a3fb7 -https://conda.anaconda.org/conda-forge/osx-64/r-rlang-1.0.4-r41h8619c4b_0.tar.bz2#81fe72b99a2c09d6e7549237d8d1bdd2 +https://conda.anaconda.org/conda-forge/osx-64/r-rcpp-1.0.9-r41h49197e3_1.tar.bz2#9936618f869b53f12f5bfb348970affc +https://conda.anaconda.org/conda-forge/osx-64/r-rlang-1.0.6-r41h49197e3_0.tar.bz2#c020b3ab54f6bb069ed5dbbd1239e09e https://conda.anaconda.org/conda-forge/noarch/r-snow-0.4_4-r41hc72bb7e_0.tar.bz2#6e7a0c895d09c4e035262a850d69a994 https://conda.anaconda.org/conda-forge/osx-64/r-sys-3.4-r41h28b5c78_0.tar.bz2#3ede777d9ced86093d710f30ea1dd311 https://conda.anaconda.org/conda-forge/osx-64/r-utf8-1.2.2-r41h28b5c78_0.tar.bz2#981a4e229b4ca5b612d308817e1ea551 -https://conda.anaconda.org/conda-forge/noarch/r-viridislite-0.4.0-r41hc72bb7e_0.tar.bz2#00ad214f0ade6d1a8e7fedb5eb06add6 +https://conda.anaconda.org/conda-forge/noarch/r-viridislite-0.4.1-r41hc72bb7e_0.tar.bz2#9e845780dbd53f680cc9d7a2602dca53 https://conda.anaconda.org/conda-forge/noarch/r-withr-2.5.0-r41hc72bb7e_0.tar.bz2#53558bed67c14dc998209e5a567b6cfd https://conda.anaconda.org/conda-forge/osx-64/r-xml-3.99_0.9-r41h0f1d5c4_0.tar.bz2#cf24f46c1cd397c6ab1b2b89f3445bcf https://conda.anaconda.org/conda-forge/noarch/r-xtable-1.8_4-r41hc72bb7e_3.tar.bz2#f44d2ad79a8d084f757cefd24156bdfc -https://conda.anaconda.org/conda-forge/osx-64/schema-salad-8.3.20220525163636-py39h701faf5_0.tar.bz2#0e40f1a4e20b51ab569376269c5091bc -https://conda.anaconda.org/bioconda/osx-64/bioconductor-biobase-2.54.0-r41haba8685_1.tar.bz2#8a137b48256ed44fa99b57bd287aae11 +https://conda.anaconda.org/conda-forge/osx-64/schema-salad-8.3.20220916115321-py39ha30fb19_0.tar.bz2#d2321c1a21ca174638bdacf4932541e0 +https://conda.anaconda.org/bioconda/osx-64/bioconductor-biobase-2.54.0-r41h3be46a4_2.tar.bz2#0b1433334620553fb2f8409abd94ce9a https://conda.anaconda.org/bioconda/noarch/bioconductor-matrixgenerics-1.6.0-r41hdfd78af_0.tar.bz2#69613f3c312fb36021947f3359e89435 -https://conda.anaconda.org/bioconda/osx-64/bioconductor-s4vectors-0.32.3-r41haba8685_0.tar.bz2#91eff583927c8fe0de8393c0eaa762d4 -https://conda.anaconda.org/conda-forge/osx-64/gtk2-2.24.33-h675d97a_1.tar.bz2#5042a5243c79120f5970ddf050e60bb4 +https://conda.anaconda.org/bioconda/osx-64/bioconductor-s4vectors-0.32.4-r41h3be46a4_0.tar.bz2#81eed04abd12d2b17c79980509c57ced +https://conda.anaconda.org/bioconda/osx-64/htseq-2.0.2-py39h6041a6e_0.tar.bz2#df827aa362726e693bbcbc127373bb8b +https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.5.2-py39h6e9494a_1.tar.bz2#20c7960b5df870c9c52884c7eb5ea1fc +https://conda.anaconda.org/main/osx-64/pandas-1.4.3-py39he9d5cce_0.tar.bz2#fcd3e91845db1728f2c64b0c2e26c7d3 +https://conda.anaconda.org/conda-forge/osx-64/pydot-1.4.2-py39h6e9494a_2.tar.bz2#a28ed8d626bfe443d6f15322c486a04c +https://conda.anaconda.org/main/noarch/pydotplus-2.0.2-py_3.tar.bz2#cfb123d1e6b3375ae2acab4c290390ae https://conda.anaconda.org/conda-forge/osx-64/r-askpass-1.1-r41h28b5c78_2.tar.bz2#36b5930700151476033444f3d0c77984 https://conda.anaconda.org/conda-forge/osx-64/r-bit64-4.0.5-r41h28b5c78_0.tar.bz2#18addcb9663e3a4ddfff2f29cb5f1bfa https://conda.anaconda.org/conda-forge/osx-64/r-cachem-1.0.6-r41h28b5c78_0.tar.bz2#ec24168081578c8692bef86871d35ef8 -https://conda.anaconda.org/conda-forge/osx-64/r-cli-3.3.0-r41h8619c4b_0.tar.bz2#4b18573c99fdadddf706f7a6ff989ef4 +https://conda.anaconda.org/conda-forge/osx-64/r-cli-3.4.1-r41h49197e3_0.tar.bz2#dc01e9a9b5dc3a66bb74a6c9df81660c https://conda.anaconda.org/conda-forge/osx-64/r-diffobj-0.3.5-r41h28b5c78_0.tar.bz2#5f97027fe102e4843dd1c65d4d132d50 https://conda.anaconda.org/conda-forge/osx-64/r-ellipsis-0.3.2-r41h28b5c78_0.tar.bz2#aab0ff84a4dfb093ffa8ee1ba2649a7f https://conda.anaconda.org/conda-forge/noarch/r-lambda.r-1.2.4-r41hc72bb7e_1.tar.bz2#0cdb436f2dd68815329b28ba9ffce962 -https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.1-r41hc72bb7e_0.tar.bz2#c7f563a8fc6d5d9c52da2e390fa5f856 +https://conda.anaconda.org/conda-forge/noarch/r-lifecycle-1.0.2-r41hc72bb7e_0.tar.bz2#b22d5a6a675d3a2ce268b5f7a913e41e https://conda.anaconda.org/conda-forge/osx-64/r-locfit-1.5_9.6-r41h67d6963_0.tar.bz2#3c142b6b96c43064be71363994f8e325 https://conda.anaconda.org/conda-forge/osx-64/r-matrix-1.4_1-r41ha2825d1_0.tar.bz2#db80ef748ae2438cf55677a72b50f6e6 https://conda.anaconda.org/conda-forge/noarch/r-munsell-0.5.0-r41hc72bb7e_1004.tar.bz2#f5bd0c85c189a1797f29f2731c13a4dd -https://conda.anaconda.org/conda-forge/osx-64/r-nlme-3.1_158-r41he3b5f32_0.tar.bz2#4270fb9c7a6259fb0ce7ee1477cfaaff +https://conda.anaconda.org/conda-forge/osx-64/r-nlme-3.1_159-r41h225c1e1_0.tar.bz2#ca80b6aab439bcf18c92c45e12250fe1 https://conda.anaconda.org/conda-forge/osx-64/r-processx-3.7.0-r41h67d6963_0.tar.bz2#8f5159cf4e512318ca0eec79f6511829 -https://conda.anaconda.org/conda-forge/osx-64/r-rcpparmadillo-0.11.2.0.0-r41h3fca91c_0.tar.bz2#e8a68cb12bcf96bac41bc37e1d7ad9c9 -https://conda.anaconda.org/conda-forge/osx-64/r-rcurl-1.98_1.7-r41h67d6963_0.tar.bz2#25453496d913dc4c193e837dc14024e2 +https://conda.anaconda.org/conda-forge/osx-64/r-rcpparmadillo-0.11.2.3.1-r41hf5e6a41_0.tar.bz2#635c29e17bd4f5bf714da069e6d75fef +https://conda.anaconda.org/conda-forge/osx-64/r-rcurl-1.98_1.8-r41h67d6963_0.tar.bz2#680cc130ef5159619e355e953acc6552 https://conda.anaconda.org/conda-forge/noarch/r-rprojroot-2.0.3-r41hc72bb7e_0.tar.bz2#4c97577b6f80853234f34b548d8140ec -https://conda.anaconda.org/bioconda/osx-64/bioconductor-iranges-2.28.0-r41haba8685_1.tar.bz2#a89de4b313bb7e4d43f1dc0150ea2b81 -https://conda.anaconda.org/conda-forge/osx-64/graphviz-2.50.0-h77de9ca_1.tar.bz2#af78b515d7c524c24994e1c641d7c8bc -https://conda.anaconda.org/conda-forge/noarch/r-callr-3.7.1-r41hc72bb7e_0.tar.bz2#03d548449f2330be28745ea8be93bf9e -https://conda.anaconda.org/conda-forge/noarch/r-desc-1.4.1-r41hc72bb7e_0.tar.bz2#259389838e07898dbc82ae38fba42541 +https://conda.anaconda.org/bioconda/osx-64/bioconductor-iranges-2.28.0-r41h3be46a4_2.tar.bz2#a43cfbe6f6bbe15d9de0fb19990b95f8 +https://conda.anaconda.org/conda-forge/noarch/prov-1.5.1-py_1.tar.bz2#66e578dc9f4bfe65728ad547a81cd6dd +https://conda.anaconda.org/conda-forge/noarch/r-callr-3.7.2-r41hc72bb7e_0.tar.bz2#0493d6ed1666106a66d218c0eb44d475 +https://conda.anaconda.org/conda-forge/noarch/r-desc-1.4.2-r41hc72bb7e_0.tar.bz2#b63a8a0a0fa2fd16a324d50b4df98238 https://conda.anaconda.org/conda-forge/noarch/r-futile.logger-1.4.3-r41hc72bb7e_1003.tar.bz2#c29b2d0547e44751529e51f667d39b19 https://conda.anaconda.org/conda-forge/noarch/r-memoise-2.0.1-r41hc72bb7e_0.tar.bz2#ba52e00d6d8b5bd8edd1c164b74cca9d https://conda.anaconda.org/conda-forge/osx-64/r-mgcv-1.8_40-r41h60b693f_0.tar.bz2#8078f5835b30ae9c092145c8ebb8313e -https://conda.anaconda.org/conda-forge/osx-64/r-openssl-2.0.2-r41h2a55822_0.tar.bz2#c5fd66f0ff73ac19d81de6c62aa41ea4 -https://conda.anaconda.org/conda-forge/noarch/r-scales-1.2.0-r41hc72bb7e_0.tar.bz2#0fb58a9c9b925c7c5f491d2c7e2b78da -https://conda.anaconda.org/conda-forge/osx-64/r-survival-3.3_1-r41h0f1d5c4_0.tar.bz2#4459dadd6fc675b9ac913f1a019c4812 +https://conda.anaconda.org/conda-forge/osx-64/r-openssl-2.0.3-r41h13d56fc_0.tar.bz2#c8e0777e35810e0bec9a192423c8f571 +https://conda.anaconda.org/conda-forge/noarch/r-scales-1.2.1-r41hc72bb7e_0.tar.bz2#9ba5c1de3a9758871584ba40c378db14 +https://conda.anaconda.org/conda-forge/osx-64/r-survival-3.4_0-r41hef1f586_0.tar.bz2#6099c4777457e770b64f0d32e5588adf https://conda.anaconda.org/conda-forge/osx-64/r-vctrs-0.4.1-r41hc4bb905_0.tar.bz2#1693b30f880c5cd8a3cd36eedd2a3cfe -https://conda.anaconda.org/bioconda/osx-64/bioconductor-biocparallel-1.28.3-r41h7cba510_0.tar.bz2#8a1d491b537cd4e886e677ca3463471a -https://conda.anaconda.org/bioconda/osx-64/bioconductor-delayedarray-0.20.0-r41haba8685_1.tar.bz2#18b6dc7e658d6df39f3ea7b350812a8f +https://conda.anaconda.org/bioconda/osx-64/bioconductor-biocparallel-1.28.3-r41hb890f52_1.tar.bz2#d55e3b2bc51f1f46c9c28ebb634e2752 +https://conda.anaconda.org/bioconda/osx-64/bioconductor-delayedarray-0.20.0-r41h3be46a4_2.tar.bz2#f09225597919434201c12bdded91ab51 https://conda.anaconda.org/bioconda/noarch/bioconductor-genomeinfodb-1.30.0-r41hdfd78af_0.tar.bz2#c55150adbc50b1d923d48d9bb126cf0f -https://conda.anaconda.org/bioconda/osx-64/bioconductor-xvector-0.34.0-r41haba8685_1.tar.bz2#aa8411a873df7f52f75872c75284427f -https://conda.anaconda.org/conda-forge/osx-64/pydot-1.4.2-py39h6e9494a_2.tar.bz2#a28ed8d626bfe443d6f15322c486a04c -https://conda.anaconda.org/main/noarch/pydotplus-2.0.2-py_3.tar.bz2#cfb123d1e6b3375ae2acab4c290390ae +https://conda.anaconda.org/bioconda/osx-64/bioconductor-xvector-0.34.0-r41h3be46a4_2.tar.bz2#b8acc70c152158784a99a87de469cc93 +https://conda.anaconda.org/conda-forge/noarch/cwltool-3.1.20220628170238-pyhc268e32_0.tar.bz2#b8780347d56585db8c9323522cbe9118 https://conda.anaconda.org/conda-forge/noarch/r-blob-1.2.3-r41hc72bb7e_0.tar.bz2#6ab0e11ba8562a5bb418e63e30013448 -https://conda.anaconda.org/conda-forge/noarch/r-httr-1.4.3-r41hc72bb7e_0.tar.bz2#7c69a246584f13167d43bf377d321a1f -https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.8.0-r41hc72bb7e_0.tar.bz2#e7c96c840e76a1bbe8b7e9f0ae2e1f2f +https://conda.anaconda.org/conda-forge/noarch/r-httr-1.4.4-r41hc72bb7e_0.tar.bz2#0f25e3d569e80378216435d8f324c1f5 +https://conda.anaconda.org/conda-forge/noarch/r-pillar-1.8.1-r41hc72bb7e_0.tar.bz2#88ffc0fd48cf9bb5501bf54a8c885124 https://conda.anaconda.org/conda-forge/noarch/r-pkgload-1.3.0-r41hc72bb7e_0.tar.bz2#ad902132cde9da724b8c200b0e15e015 -https://conda.anaconda.org/bioconda/osx-64/bioconductor-biostrings-2.62.0-r41haba8685_1.tar.bz2#7caa444e64004bc5e02db0e278ad2fa4 -https://conda.anaconda.org/bioconda/osx-64/bioconductor-genomicranges-1.46.1-r41haba8685_0.tar.bz2#fa948cc6268f275a7415d9204f3d478b -https://conda.anaconda.org/conda-forge/noarch/prov-1.5.1-py_1.tar.bz2#66e578dc9f4bfe65728ad547a81cd6dd +https://conda.anaconda.org/bioconda/osx-64/bioconductor-biostrings-2.62.0-r41h3be46a4_2.tar.bz2#b00fdf7b04e078e8359bf4ff6cb2ae3a +https://conda.anaconda.org/bioconda/osx-64/bioconductor-genomicranges-1.46.1-r41h3be46a4_1.tar.bz2#16b1870ffe795187fb63b3e0e8437cbd https://conda.anaconda.org/conda-forge/osx-64/r-rsqlite-2.2.8-r41h9951f98_0.tar.bz2#59ba4d78009ad6a785ce894c63f23f2a https://conda.anaconda.org/conda-forge/osx-64/r-tibble-3.1.8-r41h67d6963_0.tar.bz2#d1e5896327f1f90c7e3528da19f9ebc6 https://conda.anaconda.org/bioconda/noarch/bioconductor-keggrest-1.34.0-r41hdfd78af_0.tar.bz2#54056f47634552568aa26b1b09a2e16a https://conda.anaconda.org/bioconda/noarch/bioconductor-summarizedexperiment-1.24.0-r41hdfd78af_0.tar.bz2#f21eb229b8ee58680cb0179ef6d51f7e -https://conda.anaconda.org/conda-forge/noarch/cwltool-3.1.20220628170238-pyhc268e32_0.tar.bz2#b8780347d56585db8c9323522cbe9118 https://conda.anaconda.org/conda-forge/noarch/r-rematch2-2.1.2-r41hc72bb7e_1.tar.bz2#5c26fbf849007fc5022745ec14e38822 https://conda.anaconda.org/bioconda/noarch/bioconductor-annotationdbi-1.56.1-r41hdfd78af_0.tar.bz2#182f445903618187b8f657644da1a335 https://conda.anaconda.org/conda-forge/noarch/r-waldo-0.4.0-r41hc72bb7e_0.tar.bz2#974972fb4e38703ed8e1722f45600e8b @@ -290,4 +289,4 @@ https://conda.anaconda.org/bioconda/osx-64/bioconductor-genefilter-1.76.0-r41h11 https://conda.anaconda.org/bioconda/noarch/bioconductor-geneplotter-1.72.0-r41hdfd78af_0.tar.bz2#32c0e22a0095ad9b592a6bf7e16a835a https://conda.anaconda.org/conda-forge/osx-64/r-isoband-0.2.5-r41h9951f98_0.tar.bz2#7f3658a3f8a2b98249bc34df1b865f03 https://conda.anaconda.org/conda-forge/noarch/r-ggplot2-3.3.6-r41hc72bb7e_0.tar.bz2#e305660b2a4c78aea808bc5c2aa910a3 -https://conda.anaconda.org/bioconda/osx-64/bioconductor-deseq2-1.34.0-r41h7cba510_1.tar.bz2#75a3a591545cbbd603be67ec35c43af8 +https://conda.anaconda.org/bioconda/osx-64/bioconductor-deseq2-1.34.0-r41hb890f52_2.tar.bz2#480ff3b340d91e3a4fff6c65a0d22715 diff --git a/conda/environment.yml b/conda/environment.yml index 3afc17d8..99f0d099 100644 --- a/conda/environment.yml +++ b/conda/environment.yml @@ -6,6 +6,7 @@ dependencies: - python>=3.9 - bioconductor-deseq2==1.34.0 - bowtie==1.3.1 + - cython==0.29.24 - fastp==0.23.2 - htseq==2.0.2 - nodejs==16.13.1 From 126060699e32b8a80d80fae67025bc04518af8c4 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sat, 1 Oct 2022 16:23:07 -0700 Subject: [PATCH 25/29] setup.sh: Conda will print extra newlines before "done" in the "Executing transaction... done" message under certain conditions. The installation script checks for this "success string" to confirm installation/update since Conda doesn't always return a non-zero exit code on failure. The presence of newlines means the pattern fails to match the success string. To remedy this, the setup script now removes newlines from before checking for the success string. setup.py: Very minor cleanup to group Cython Extension record creation under a function --- setup.py | 43 ++++++++++++++++++++++--------------------- setup.sh | 4 ++-- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/setup.py b/setup.py index d23fdb43..1c9a2da0 100644 --- a/setup.py +++ b/setup.py @@ -30,26 +30,27 @@ def in_conda_env(self): return all([os.getenv(conda_var) for conda_var in ["CONDA_PREFIX", "CONDA_DEFAULT_ENV"]]) -pyx_files = [ - 'tiny/rna/counter/stepvector/_stepvector.pyx', - 'tests/cython_tests/stepvector/test_cython.pyx' -] -cxx_extension_args = { - 'extra_compile_args': ['-stdlib=libc++', '-std=c++11', '-O3'], - 'extra_link_args': [], - 'language': 'c++' -} -if sys.platform == "darwin": - cxx_extension_args['extra_compile_args'] += ['-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include'] - cxx_extension_args['extra_link_args'] += ['-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'] -cython_extensions = [ - setuptools.Extension( - pyx_filename.replace('/', '.').rstrip('.pyx'), - sources=[pyx_filename], - **cxx_extension_args - ) - for pyx_filename in pyx_files -] +def get_cython_extension_defs(): + pyx_files = [ + 'tiny/rna/counter/stepvector/_stepvector.pyx', + 'tests/cython_tests/stepvector/test_cython.pyx' + ] + + cxx_extension_args = { + 'extra_compile_args': ['-std=c++11', '-O3'], + 'extra_link_args': [], + 'language': 'c++' + } + + if sys.platform == "darwin": + cxx_extension_args['extra_compile_args'] += ['-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include'] + cxx_extension_args['extra_link_args'] += ['-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'] + + return [setuptools.Extension( + pyx_filename.replace('/', '.').rstrip('.pyx'), + sources=[pyx_filename], + **cxx_extension_args) + for pyx_filename in pyx_files] setuptools.setup( name=NAME, @@ -71,7 +72,7 @@ def in_conda_env(self): ] }, ext_modules=cythonize( - cython_extensions, + get_cython_extension_defs(), compiler_directives={'language_level': '3'}, gdb_debug=False ), diff --git a/setup.sh b/setup.sh index 56ead387..40e24b9b 100755 --- a/setup.sh +++ b/setup.sh @@ -58,7 +58,7 @@ function setup_environment() { # Setup tinyRNA environment using our generated lock file status "Setting up $env_name environment (this may take a while)..." conda create --file $platform_lock_file --name $env_name 2>&1 | tee "env_install.log" - if ! grep -q "Executing transaction: ...working... done" env_install.log; then + if ! tr -d \\n < env_install.log | grep -q "Executing transaction: ...working... done"; then fail "$env_name environment setup failed" echo "Console output has been saved to env_install.log." exit 1 @@ -135,7 +135,7 @@ if conda env list | grep -q "$env_name"; then echo status "Updating $env_name environment..." conda update --file $platform_lock_file --name "$env_name" 2>&1 | tee "env_update.log" - if ! grep -q "Executing transaction: ...working... done" env_update.log; then + if ! tr -d \\n < env_install.log | grep -q "Executing transaction: ...working... done"; then fail "Failed to update the environment" echo "Check the env_update.log file for more information." exit 1 From f745ef2dbc797334e6d160b38831ddc014fb043e Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sat, 1 Oct 2022 17:58:09 -0700 Subject: [PATCH 26/29] Removing _chromvector.pyx. After further testing I found that it was not necessary to patch it in order to use the StepVector, as I had once thought. Updated unit_tests_stepvector.py accordingly and also added the Cython native test, since there isn't a clean way I'm aware of for doing this with the unittest module. --- tests/unit_tests_stepvector.py | 32 ++- tiny/rna/counter/stepvector/_chromvector.pyx | 259 ------------------- 2 files changed, 20 insertions(+), 271 deletions(-) delete mode 100644 tiny/rna/counter/stepvector/_chromvector.pyx diff --git a/tests/unit_tests_stepvector.py b/tests/unit_tests_stepvector.py index 05f03106..c72facb5 100644 --- a/tests/unit_tests_stepvector.py +++ b/tests/unit_tests_stepvector.py @@ -6,7 +6,7 @@ class StepVectorTests(unittest.TestCase): - """Does our Cython StepVector accept and return steps as expected?""" + """Does our Cython StepVector integrate with HTSeq's GenomicArray, and accept/return steps as expected?""" def test_genomicarray_with_cython_stepvec(self): # Patch the StepVector reference in the HTSeq module and use a GenomicArray @@ -47,15 +47,16 @@ def test_genomicarray_with_cython_stepvec(self): actual = list(gas['chr']['.'].array[iv.start:iv.end].get_steps()) self.assertEqual(actual, expected) - def test_compare_to_htseq_steps(self): - """Test currently doesn't work (and it won't be easy to get it to work) + """Does the Cython StepVector contain the expected steps/coordinates when called from a Cython .pyx file?""" - This is because HTSeq's StepVector with typecode 'O' handles .add_value() using the - apply() function and a local function which copies the set OUT of the StepVector, - adds to it, then puts it back. This is exactly the kind of thing that our - specialized StepVector was intended to avoid. - """ + def test_native_cython_expected_steps(self): + from tests.cython_tests.stepvector.test_cython import main as cython_native_test + cython_native_test() + """Does the Cython StepVector's steps/coordinates match those in HTSeq's StepVector when called from a .py file?""" + + def test_compare_to_htseq_steps(self): + # HTSeq setup from HTSeq.StepVector import StepVector as hStepVector def htseq_add(x): y = x.copy() @@ -65,6 +66,7 @@ def htseq_add(x): sv_len = 200 ours = StepVector.create(length=sv_len) theirs = hStepVector.create(length=sv_len, typecode='O') + theirs[:] = set() iv1 = slice(0, 99) iv2 = slice(99, 101) @@ -78,10 +80,16 @@ def htseq_add(x): f4 = {'featE', 'featF', 'featG'} fs = [f1, f2, f3, f4] - for iv, feat in zip(ivs, fs): - value = feat - theirs[iv].apply(htseq_add) - ours[iv] += feat + for iv, feat_set in zip(ivs, fs): + ours[iv] += feat_set + + # Emulating the behavior of ChromVector + # Can't add a set to a set; need to add item by item + for value in feat_set: + theirs[iv].apply(htseq_add) + + for our_step, their_step in zip(ours.get_steps(), theirs.get_steps()): + self.assertEqual(our_step, their_step) if __name__ == '__main__': diff --git a/tiny/rna/counter/stepvector/_chromvector.pyx b/tiny/rna/counter/stepvector/_chromvector.pyx deleted file mode 100644 index e0429fe5..00000000 --- a/tiny/rna/counter/stepvector/_chromvector.pyx +++ /dev/null @@ -1,259 +0,0 @@ -from ._stepvector import StepVector -from HTSeq._HTSeq import GenomicInterval - -cdef class ChromVector(object): - """Counting vector covering a chromosome. - - This class supports only the tinyRNA Cython implementation of the - HTSeq StepVector - """ - - cdef public object array - cdef public GenomicInterval iv - cdef public int offset - cdef public bint is_vector_of_sets - cdef public str _storage - cdef public str typecode - cdef public str memmap_dir - - @classmethod - def create(cls, GenomicInterval iv, str typecode, str storage, str memmap_dir=""): - """Create ChromVector from GenomicInterval - - Args: - iv (GenomicInterval): A GenomicInterval describing the chromosome - vector. - typecode ('d', 'i', 'l', 'b', or 'O'): What kind of data will be - stored inside this chromosome vector. 'd' for double, 'i' for int, - 'l' for long int, 'b' for boolean, 'O' for arbitrary objects - (e.g. sets). - storage ('step', 'ndarray', or 'memmap'): What kind of storage to - use. 'ndarray' is appropriate for short chromosomes and stores - each position in the genome into memory. 'memmap' stores all - positions, but maps the memory onto disk for larger chromosomes. - 'step' is a sparse representation similar to CSR matrices whereby - only the boundaries between genomic stretches with differing - data content are stored - see HTSeq.StepVector. - memmap_dir (str): If using 'memmap' storage, what folder to store - the memory maps. These can get quite big. - - Returns: - An instance of ChromVector with the requested options. - - """ - ncv = cls() - ncv.iv = iv - ncv.array = StepVector.create() - - ncv._storage = storage - ncv.typecode = typecode - # NOTE: As long as autochromosomes in GenomicArray are infinite length - # this has pretty limited use, but that might change - ncv.offset = iv.start - ncv.is_vector_of_sets = False - ncv.memmap_dir = memmap_dir - return ncv - - @classmethod - def _create_view(cls, ChromVector vec, GenomicInterval iv): - if iv.length == 0: - raise IndexError("Cannot subset to zero-length interval.") - v = cls() - v.iv = iv - v.array = vec.array - v.offset = vec.offset - v.is_vector_of_sets = vec.is_vector_of_sets - v._storage = vec._storage - return v - - def extend_to_include(self, iv): - if iv.strand != self.iv.strand: - raise ValueError( - 'The new interval must match the current strandedness', - ) - - # Step 1: extend the interval - length = self.iv.length - startdiff = max(self.iv.start - iv.start, 0) - self.iv.extend_to_include(iv) - self.offset = self.iv.start - - # Step 2: extend the array if needed, and shift-copy the old values - if self._storage == 'ndarray': - if self.typecode != 'O': - array = numpy.zeros(shape=(self.iv.length,), dtype=self.typecode) - else: - array = numpy.empty(shape=(self.iv.length,), dtype=self.typecode) - array[:] = None - array[startdiff: startdiff + length] = self.array[:] - elif self._storage == 'memmap': - array = numpy.memmap( - shape=(self.iv.length,), dtype=self.typecode, - filename=os.path.join( - self.memmap_dir, - self.iv.chrom + self.iv.strand + str(self.iv.start) + '_' \ - + str(self.iv.length) + ".nmm"), - mode='w+', - ) - array[startdiff: startdiff + length] = self.array[:] - else: - # The StepVector is created in ChromVector.create without explicit - # boundaries, so it's already bound by 0, +inf. So we do not need - # to extend it here, but rather just set the slice to the right - # value - array = self.array - self.array = array - - def __getitem__(self, index): - """Index or slice the chromosome. - - The index can be a few things: - - an integer: get the value of the vector at that chromosome coordinate - - a 1-step slice e.g "4:7": get a view of the chromosome region - between those coordinates. The array data are not copied. - - a GenomicInterval: similar to slices, with the additional choice of - strandedness. If this argument is stranded but the chromosome itself - is not stranded, a nonstranded view of the chromosome region is - returned. - - """ - cdef slice index_slice - cdef long int index_int - cdef long int start, stop - cdef GenomicInterval iv - - if isinstance(index, int): - index_int = index - if index_int < self.iv.start or index_int >= self.iv.end: - raise IndexError - return self.array[index_int - self.offset] - - elif isinstance(index, slice): - index_slice = index - if index_slice.start is None: - start = self.iv.start - else: - start = index_slice.start - if start < self.iv.start: - raise IndexError("start too small") - - if index_slice.stop is None: - stop = self.iv.end - else: - stop = index_slice.stop - if stop > self.iv.end: - raise IndexError("stop too large") - - iv = GenomicInterval(self.iv.chrom, start, stop, self.iv.strand) - - if not self.iv.contains(iv): - raise IndexError - return ChromVector._create_view(self, iv) - - elif isinstance(index, GenomicInterval): - if not self.iv.contains(index): - raise IndexError - - if self.iv.strand is strand_nostrand and \ - index.strand is not strand_nostrand: - iv = index.copy() # Is this correct now? - iv.strand = strand_nostrand - else: - iv = index - - return ChromVector._create_view(self, iv) - - else: - raise TypeError("Illegal index type") - - def __setitem__(self, index, value): - cdef slice index_slice - cdef long int start, stop - - if isinstance(value, ChromVector): - if self.array is value.array and value.iv.start == index.start and \ - value.iv.end == index.stop and (index.step is None or index.step == 1): - return - else: - raise NotImplementedError( - "Required assignment signature not yet implemented.") - - if isinstance(index, int): - self.array[index - self.iv.start] = value - - elif isinstance(index, slice): - index_slice = index - if index_slice.start is not None: - start = index_slice.start - if start < self.iv.start: - raise IndexError("start too small") - else: - start = self.iv.start - if index_slice.stop is not None: - stop = index_slice.stop - if stop > self.iv.end: - raise IndexError("stop too large") - else: - stop = self.iv.end - if start > stop: - raise IndexError("Start of interval is after its end.") - if start == stop: - raise IndexError("Cannot assign to zero-length interval.") - self.array[start - self.offset: stop - - self.iv.start: index.step] = value - - elif isinstance(index, GenomicInterval): - if index.chrom != self.iv.chrom: - raise KeyError("Chromosome name mismatch.") - if self.iv.strand is not strand_nostrand and \ - self.iv.strand is not self.index.strand: - raise KeyError("Strand mismatch.") - self.array[index.iv.start - self.iv.start, - index.iv.end - self.iv.start] = value - else: - raise TypeError("Illegal index type") - - def __iadd__(self, value): - if not self.is_vector_of_sets: - self.array[self.iv.start - self.offset: self.iv.end - - self.offset].__iadd__(value) - else: - def addval(x): - y = x.copy() - y.add(value) - return y - - self.apply(addval) - return self - - def __iter__(self): - return self.values() - - def values(self): - return iter(self.array[self.iv.start - self.offset: self.iv.end - self.offset]) - - def steps(self): - return _HTSeq_internal.ChromVector_steps(self) - - def apply(self, fun): - for iv, value in self.steps(): - self.array[iv.start - self.offset: iv.end - - self.offset] = fun(value) - - def __repr__(self): - return "<%s object, %s, %s>" % (self.__class__.__name__, str(self.iv), self._storage) - - def __reduce__(self): - assert self.__class__ is ChromVector - return(_ChromVector_unpickle, - (self.array, self.iv, self.offset, self.is_vector_of_sets, self._storage)) - - -def _ChromVector_unpickle(array, iv, offset, is_vector_of_sets, _storage): - cv = ChromVector() - cv.array = array - cv.iv = iv - cv.offset = offset - cv.is_vector_of_sets = is_vector_of_sets - cv._storage = _storage - return cv \ No newline at end of file From a6fd064af2625234b8552abb1e321c466f853c66 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sat, 1 Oct 2022 18:29:53 -0700 Subject: [PATCH 27/29] Updates added regarding the interchangeability of ID and gene_id. Small grammar corrections in the table in README.md, and a correction for the "unassigned" class explanation in tiny-plot.md --- README.md | 12 ++++++------ doc/tiny-count.md | 3 ++- doc/tiny-plot.md | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 57006663..91b7d4df 100644 --- a/README.md +++ b/README.md @@ -90,12 +90,12 @@ tiny get-template ### Requirements for User-Provided Input Files -| Input Type | File Extension | Requirements | -|----------------------------------------------------------------------------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Reference annotations
[(example)](START_HERE/reference_data/ram1.gff3) | GFF3 / GFF2 / GTF | Column 9 attributes (defined as "tag=value" or "tag "):
  • Each feature's `ID` tag is required
  • Feature classes can be defined with the `Class` tag. If undefined, the default value \__UNKNOWN_\_ will be used.
  • Discontinuous features must be defined with the `Parent` tag whose value is the logical parent's `ID`, or by sharing the same `ID`.
  • Attribute values containing commas must represent lists
  • All features must be stranded
  • See the example link (left) for col. 9 formatting
| -| Sequencing data
[(example)](START_HERE/fastq_files) | FASTQ(.gz) | Files must be demultiplexed. | -| Reference genome
[(example)](START_HERE/reference_data/ram1.fa) | FASTA | Chromosome identifiers (e.g. Chr1):
  • Must match your reference annotation file chromosome identifiers
  • Are case sensitive
| -| Bowtie indexes (optional) 1 | ebwt | Must be small indexes (.ebwtl indexes are not supported) | +| Input Type | File Extension | Requirements | +|----------------------------------------------------------------------------|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Reference annotations
[(example)](START_HERE/reference_data/ram1.gff3) | GFF3 / GFF2 / GTF | Column 9 attributes (defined as "tag=value" or "tag "):
  • Each feature must have an `ID` or `gene_id` tag.
  • Feature classes can be defined with the `Class` tag. If undefined, the default value \__UNKNOWN_\_ will be used.
  • Discontinuous features must be defined with the `Parent` tag whose value is the logical parent's `ID`, or by sharing the same `ID`.
  • Attribute values containing commas must represent lists.
  • All features must be stranded.
  • See the example link (left) for col. 9 formatting.
| +| Sequencing data
[(example)](START_HERE/fastq_files) | FASTQ(.gz) | Files must be demultiplexed. | +| Reference genome
[(example)](START_HERE/reference_data/ram1.fa) | FASTA | Chromosome identifiers (e.g. Chr1):
  • Must match your reference annotation file chromosome identifiers
  • Are case sensitive
| +| Bowtie indexes (optional) 1 | ebwt | Must be small indexes (.ebwtl indexes are not supported) |
1 Bowtie indexes can be created for you. See the [configuration file documentation](doc/Configuration.md#building-bowtie-indexes). diff --git a/doc/tiny-count.md b/doc/tiny-count.md index de66cce8..1439c635 100644 --- a/doc/tiny-count.md +++ b/doc/tiny-count.md @@ -118,6 +118,7 @@ You may encounter the following cases when you have more than one unique GFF fil - If a feature is defined in one GFF file, then again but under a different **Alias by...**, then both aliases are retained and treated as a list. All aliases will be present in the `Feature Name` column of the Feature Counts output table. They will be comma separated. Discontinuous features and feature filtering support: -- Discontinuous features are supported (as defined by the `Parent` attribute key, or by a shared `ID` attribute value). Rule and alias matches of descendents are merged with the root parent's. +- Discontinuous features are supported (as defined by the `Parent` attribute key, or by a shared `ID`/`gene_id` attribute value). Rule and alias matches of descendents are merged with the root parent's. +- If a feature contains both `ID` and `gene_id` attributes, only the value of `ID` is used as the feature's ID. - Features can be filtered during GFF parsing by their `source` and/or `type` columns, and these preferences can be specified in the Run Config file. These are inclusive filters. Only features matching the values specified will be retained for selection and listed in the output counts table. An empty list allows all values. See the [parameters documentation](Parameters.md#filters) for information about specifying these filters. - If a filtered feature breaks a feature lineage (that is, features chained via the `Parent` attribute), then the highest non-filtered ancestor is designated the root parent. The lineage is maintained transparently but the filtered feature does not contribute to the domains of selection. \ No newline at end of file diff --git a/doc/tiny-plot.md b/doc/tiny-plot.md index a8e890d8..8b60d37a 100644 --- a/doc/tiny-plot.md +++ b/doc/tiny-plot.md @@ -70,8 +70,8 @@ Features can have multiple classes associated with them, so it is useful to see class_chart with 8 classes

-#### Class N -Class **N** represents the percentage of mapped reads that were unassigned. Sources of unassigned reads include: +#### Class \_UNASSIGNED_ +This category represents the percentage of mapped reads that were unassigned. Sources of unassigned reads include: - A lack of features passing selection at alignment loci - Alignments which do not overlap with any features From 81aed023a7a42e69ae28aef9587d855924c779ed Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sun, 2 Oct 2022 18:24:21 -0700 Subject: [PATCH 28/29] Reliability improvements for the installation and use of build dependencies on macOS. setup.py: Now treats the Cython StepVector and tests as optional extensions so that installation doesn't error out when there are unforeseen system incompatibilities. Installation also determines the path to the macOS SDK in a way that will be compatible with conda-build pipelines (if/when we have a bioconda recipe for tinyRNA). Also: better condition checks for determining when we need to search for the SDK, and better comments/docstrings. setup.sh: CommandLineTools is now checked for and installed if not present because it is required for the C++ compiler on macOS. This marks another curious exception to the Conda isolation model. As far as I can tell, build dependencies on Linux are already covered by Conda subdependencies. --- setup.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++------- setup.sh | 16 ++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 1c9a2da0..8875fcf3 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,10 @@ class PrereqAndExec(install): + """These checks are performed prior to installation to ensure that this setup routine + is only executed within a Conda environment. Users should perform installation via + setup.sh, not setup.py.""" + def run(self): if not self.in_conda_env(): sys.exit("CRITICAL ERROR: you appear to be installing %s outside of a conda environment.\n" % (NAME,) + @@ -31,9 +35,14 @@ def in_conda_env(self): def get_cython_extension_defs(): + """Returns a list of Extension objects corresponding to the contents of pyx_files. + Extensions indicated as optional in pyx_files will NOT cause the installation to + error out if there are build issues, and therefore must be used as optional imports.""" + pyx_files = [ - 'tiny/rna/counter/stepvector/_stepvector.pyx', - 'tests/cython_tests/stepvector/test_cython.pyx' + # (file path, optional) + ('tiny/rna/counter/stepvector/_stepvector.pyx', True), + ('tests/cython_tests/stepvector/test_cython.pyx', True) ] cxx_extension_args = { @@ -42,15 +51,45 @@ def get_cython_extension_defs(): 'language': 'c++' } - if sys.platform == "darwin": - cxx_extension_args['extra_compile_args'] += ['-isystem', '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include'] - cxx_extension_args['extra_link_args'] += ['-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'] + if sys.platform == "darwin" and os.getenv('SDKROOT') is None: + # If building with the environment variables set by Conda, this isn't necessary + # However build shortcuts in some IDEs will strip these vars + sdk_root = get_macos_sdk_path() + cxx_extension_args['extra_compile_args'] += ['-isystem', os.path.join(sdk_root, '/usr/include')] + cxx_extension_args['extra_link_args'] += ['-L' + os.path.join(sdk_root, '/usr/lib')] return [setuptools.Extension( - pyx_filename.replace('/', '.').rstrip('.pyx'), + pyx_filename.replace('./', '').replace('/', '.').rstrip('.pyx'), sources=[pyx_filename], + optional=optional, **cxx_extension_args) - for pyx_filename in pyx_files] + for pyx_filename, optional in pyx_files] + + +def get_macos_sdk_path(): + """Determines the SDK path for compiler dependencies in a manner that will be + compatible with conda-build pipelines. The following code was copied from + https://github.com/python-pillow/Pillow/blob/main/setup.py """ + + try: + import subprocess + sdk_path = subprocess.check_output(["xcrun", "--show-sdk-path"]).strip().decode('latin1') + except Exception: + sdk_path = None + + if ( + not sdk_path + or sdk_path == "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" + ): + commandlinetools_sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" + if os.path.exists(commandlinetools_sdk_path): + sdk_path = commandlinetools_sdk_path + else: + raise RuntimeError("The macOS SDK path could not be found. This is required for Cython. " + "Please run xcode-select --install in your terminal.") + + return sdk_path + setuptools.setup( name=NAME, diff --git a/setup.sh b/setup.sh index 40e24b9b..8834649c 100755 --- a/setup.sh +++ b/setup.sh @@ -67,6 +67,21 @@ function setup_environment() { fi } +function setup_macOS_command_line_tools() { + # Install Xcode command line tools if necessary + if ! xcode-select --print-path > /dev/null 2>&1; then + status "Installing Xcode command line tools. Follow prompts in new window..." + if xcode-select --install; then + success "Command line tools setup complete" + else + fail "Command line tools installation failed" + exit 1 + fi + else + success "Xcode command line tools are already installed" + fi +} + ################################################################################ # Main Routine ################################################################################ @@ -82,6 +97,7 @@ if [[ "$OSTYPE" == "darwin"* ]]; then shell=$(basename "$(dscl . -read ~/ UserShell | cut -f 2 -d " ")") miniconda_installer="Miniconda3-py${python_version/./}_${miniconda_version}-MacOSX-x86_64.sh" platform_lock_file="./conda/conda-osx-64.lock" + setup_macOS_command_line_tools elif [[ "$OSTYPE" == "linux-gnu" ]]; then success "Linux detected" shell="$(basename "$SHELL")" From 68840196dfad62376da685436ca19ff557a19131 Mon Sep 17 00:00:00 2001 From: Alex Tate <0xalextate@gmail.com> Date: Sun, 2 Oct 2022 18:34:19 -0700 Subject: [PATCH 29/29] ReferenceTables no longer treats Cython StepVector import errors as hard errors, and instead falls back to using HTSeq's StepVector. This is because setup.py has been changed to skip installation of extensions that produce build errors. Minor correctness change to the --step-vector command line option (is now --stepvector). Also cleaned up get_macos_sdk_path() in setup.py --- doc/Parameters.md | 6 +++--- setup.py | 9 ++++----- tiny/rna/counter/counter.py | 2 +- tiny/rna/counter/hts_parsing.py | 14 ++++++++------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/doc/Parameters.md b/doc/Parameters.md index d870c454..b8ca2d63 100644 --- a/doc/Parameters.md +++ b/doc/Parameters.md @@ -93,9 +93,9 @@ The SAM files produced by the tinyRNA pipeline are collapsed by default; alignme You can optionally filter features in your GFF files by specifying sources and/or types that are desired. Source and type refer to GFF columns 2 and 3 respectively. If source _and_ type filters are specified, each feature must match one of the sources _and_ one of the types in order to be included in the counting process. For both filters, an empty list is the same as "allow all." ### StepVector - | Run Config Key | Commandline Argument | +| Run Config Key | Commandline Argument | |--------------------|----------------------| -| counter_stepvector | `--step-vector` | +| counter_stepvector | `--stepvector` | A custom Cython implementation of HTSeq's StepVector is used for finding features that overlap each alignment interval. While the core C++ component of the StepVector is the same, we have found that our Cython implementation can result in runtimes up to 50% faster than HTSeq's implementation. This parameter allows you to use HTSeq's StepVector if you wish (for example, if the Cython StepVector is incompatible with your system) @@ -153,7 +153,7 @@ Optional arguments: -dc, --decollapse Create a decollapsed copy of all SAM files listed in your Samples Sheet. This option is ignored for non- collapsed inputs. - -sv {Cython,HTSeq}, --step-vector {Cython,HTSeq} + -sv {Cython,HTSeq}, --stepvector {Cython,HTSeq} Select which StepVector implementation is used to find features overlapping an interval. -md, --multi-id Don't treat features with multiple ID values as an diff --git a/setup.py b/setup.py index 8875fcf3..0f83abb4 100644 --- a/setup.py +++ b/setup.py @@ -77,11 +77,10 @@ def get_macos_sdk_path(): except Exception: sdk_path = None - if ( - not sdk_path - or sdk_path == "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" - ): - commandlinetools_sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" + xcode_sdk_path = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" + commandlinetools_sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" + + if not sdk_path or sdk_path == xcode_sdk_path: if os.path.exists(commandlinetools_sdk_path): sdk_path = commandlinetools_sdk_path else: diff --git a/tiny/rna/counter/counter.py b/tiny/rna/counter/counter.py index 37329760..c81336d9 100644 --- a/tiny/rna/counter/counter.py +++ b/tiny/rna/counter/counter.py @@ -51,7 +51,7 @@ def get_args(): optional_args.add_argument('-dc', '--decollapse', action='store_true', help='Create a decollapsed copy of all SAM files listed in your ' 'Samples Sheet. This option is ignored for non-collapsed inputs.') - optional_args.add_argument('-sv', '--step-vector', choices=['Cython', 'HTSeq'], default='Cython', + optional_args.add_argument('-sv', '--stepvector', choices=['Cython', 'HTSeq'], default='Cython', help='Select which StepVector implementation is used to find ' 'features overlapping an interval.') optional_args.add_argument('-md', '--multi-id', action='store_true', diff --git a/tiny/rna/counter/hts_parsing.py b/tiny/rna/counter/hts_parsing.py index 533983bf..cccbdce4 100644 --- a/tiny/rna/counter/hts_parsing.py +++ b/tiny/rna/counter/hts_parsing.py @@ -446,17 +446,18 @@ def __init__(self, gff_files: Dict[str, list], feature_selector, **prefs): self._set_filters(**prefs) self.gff_files = gff_files # ----------------------------------------------------------- Primary Key: - if prefs['step_vector'] == 'Cython': + if prefs['stepvector'] == 'Cython': try: from tiny.rna.counter.stepvector import StepVector setattr(HTSeq.StepVector, 'StepVector', StepVector) self.feats = HTSeq.GenomicArray("auto", stranded=False) # Root Match ID except ModuleNotFoundError: - print("The Cython StepVector has not yet been built.\n" - "Run: python setup.py build_ext --inplace", + prefs['stepvector'] = 'HTSeq' + print("Failed to import Cython StepVector\n" + "Falling back to HTSeq's StepVector", file=sys.stderr) - sys.exit(1) - else: + + if prefs['stepvector'] == 'HTSeq': self.feats = HTSeq.GenomicArrayOfSets("auto", stranded=False) # Root Match ID self.parents, self.filtered = {}, set() # Original Feature ID @@ -701,7 +702,8 @@ def chrom_vector_setdefault(self, chrom): self.feats.add_chrom(chrom) def get_feature_id(self, row): - id_collection = row.attr.get('ID', row.attr.get('gene_id', None)) + id_collection = row.attr.get('ID', default= + row.attr.get('gene_id', default=None)) if id_collection is None: raise ValueError(f"Feature {row.name} does not contain an ID attribute.")