Units and complex numbers

brian2.units do not work with python’s complex numbers. As currently brian2 integrators do not support complex numbers this may not seem like a problem at all. However, if one continues to work with brian2 units also in analytical and symbolic parts of ones code, e.g., comparing spike spectra from Brian simmulations to some theory, one ends up with complex expressions. In these cases it would be great if one can just use the same units system.

One solution to this might be to undertand if brian2’s units were derived and are compatible to some other python unit system, and then see if some of them are complex number compatible.

Cheers and stay healthy!

Hi. Never thought about this much, I wonder whether in practice it would be that useful in practice, given that complex signal processing functions etc. would probably not correctly deal with the units internally. Do you have any concrete example we could discuss?
On a basic level, using the unit system with complex numbers should already work:

>>> x = (np.arange(3) + 1j)*mV + (4 + 2j)*mV
>>> x
array([4.+3.j, 5.+3.j, 6.+3.j]) * mvolt
>>> np.abs(x)
array([5.        , 5.83095189, 6.70820393]) * mvolt
>>> np.mean(x)
5.+3.j * mvolt

Wow, this means basic complex number functionality it is already implemented. I am sorry to not have noticed that. When I do

import brian2
brian2.Hz * 1j

in atom/hydrogen. It results in

TypeError                                 Traceback (most recent call last)
~/.local/lib/python3.8/site-packages/IPython/core/formatters.py in __call__(self, obj)
    343             method = get_real_method(obj, self.print_method)
    344             if method is not None:
--> 345                 return method()
    346             return None
    347         else:

~/.local/lib/python3.8/site-packages/brian2/units/fundamentalunits.py in _repr_latex_(self)
   1606     def _repr_latex_(self):
-> 1607         return '`

0.+1.j * hertz
``` + latex(self) + '`

   1609     def __str__(self):

~/.local/lib/python3.8/site-packages/sympy/printing/printer.py in __call__(self, *args, **kwargs)
    372     def __call__(self, *args, **kwargs):
--> 373         return self.__wrapped__(*args, **kwargs)
    375     @property

~/.local/lib/python3.8/site-packages/sympy/printing/latex.py in latex(expr, **settings)
   2914     """
-> 2915     return LatexPrinter(settings).doprint(expr)

~/.local/lib/python3.8/site-packages/sympy/printing/latex.py in doprint(self, expr)
    253     def doprint(self, expr):
--> 254         tex = Printer.doprint(self, expr)
    256         if self._settings['mode'] == 'plain':

~/.local/lib/python3.8/site-packages/sympy/printing/printer.py in doprint(self, expr)
    289     def doprint(self, expr):
    290         """Returns printer's representation for expr (as a string)"""
--> 291         return self._str(self._print(expr))
    293     def _print(self, expr, **kwargs):

~/.local/lib/python3.8/site-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    306             if (self.printmethod and hasattr(expr, self.printmethod)
    307                     and not isinstance(expr, BasicMeta)):
--> 308                 return getattr(expr, self.printmethod)(self, **kwargs)
    310             # See if the class of expr is known, or if one of its super

~/.local/lib/python3.8/site-packages/brian2/units/fundamentalunits.py in _latex(self, expr)
   1599         unitless = np.array(self / best_unit, copy=False)
   1600         if unitless.ndim == 0:
-> 1601             sympy_quantity = np.float(unitless)
   1602         else:
   1603             sympy_quantity = Matrix(unitless)

TypeError: can't convert complex to float`


However, the same expression in normal ipython console works as you have suggested. This must be some issues concerning my installation. So sorry again for presuming complex numbers would not work in general. I’ll try to figgure out what the issues is and report back.

Saying we implemented it is probably an exaggeration :slight_smile: The quantity system basically attaches a number to a unit and knows that e.g. if you add two quantities, the numbers add, but the units don’t change (and have to be the same) – so this means that we don’t really care about whether the numbers are floats or complex, Python/numpy takes care of these calculations.
There are certainly places where our assumption about the numbers being floats “leaks through”, but those shouldn’t be that hard to fix.

The error you are seeing is one of these places: we sometimes use the shorthand np.float(q) to remove the units from a quantity and get the “pure numpy” array. This obviously fails for a complex number, but instead of np.float we could also use np.asarray (which we do in a number of other places), which should fix your specific error.

I don’t think it’s an installation issue, your atom/hydrogen setup simply seems to support fancy LaTeX representations, and the generation of this representation fails – print(brian2.Hz * 1j) probably works just fine.