arbor-sim / arbor

The Arbor multi-compartment neural network simulation library.
https://arbor-sim.org
BSD 3-Clause "New" or "Revised" License
108 stars 60 forks source link

Improve interface of property wrappers like `membrane_capacitance`. #2254

Open Helveg opened 10 months ago

Helveg commented 10 months ago

There's some stubs like arbor.membrane_capacitance that aren't very Python friendly, to retrieve the value it seems I have to parse their string representations:

>>> x = arbor.membrane_capacitance(1 * units.F / units.m2)
>>> x + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'arbor._arbor.membrane_capacitance' and 'int'
>>> x * 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'arbor._arbor.membrane_capacitance' and 'int'
>>> int(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'arbor._arbor.membrane_capacitance'
>>> float(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: float() argument must be a string or a real number, not 'arbor._arbor.membrane_capacitance'
>>> dir(x)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> x < 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'arbor._arbor.membrane_capacitance' and 'int'
>>> x >= 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>=' not supported between instances of 'arbor._arbor.membrane_capacitance' and 'int'
>>> float(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: float() argument must be a string or a real number, not 'arbor._arbor.membrane_capacitance'
>>> float(str(x))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: 'Cm=1'
>>> float(str(x)[3:])
1.0

There's also arbor.axial_resistivity and probably others. These are returned from decor.paintings() and make it pretty opaque to inspection.

thorstenhater commented 3 months ago

Hi @Helveg,

I'd agree with the API as written here:

x = 42 * U.m2
# Should give an error; numbers are not quantities
y = x + 1 
# Error, wrong unit
y = x + 1 * U.Ohm
# That works, though
y = x + 1 * U.cm2

If you want the magnitude of a quantity, use

x = 42 * U.m2
# as currently set
x_m = x.value
# safer: force your expected unit
x_m = x.value_as(U.cm2)
Helveg commented 3 months ago

x.value and x.value_as solve this, but they're not listed in dir(x) (and so likely also not easily found in other places?), which should list all regularly available members of an object. I'd guess there's an issue with the quantity class's bindings if it has no visible members.

thorstenhater commented 3 months ago

I just checked, at least for v0.10.0 it seems like expected:

In [8]: q = 4 * U.ns

In [9]: dir(q)
Out[9]:
['__add__',
 '__class__',
 '__delattr__',
[...]
 '__subclasshook__',
 '__truediv__',
 'units',
 'value',
 'value_as']
Helveg commented 3 months ago

Ok!

Helveg commented 3 months ago

Wait, did you check the return value of an arbor function like arbor.membrane_capacitance(1 * units.F / units.m2) the way I did in the original report?

thorstenhater commented 3 months ago

Aha, no I didn't, led astray by the title mentioning 'stubs'. Yes, that interface leaves something to be desired, you can't even extract the value.

Helveg commented 3 months ago

yea my massive text blurb of my interpreter adventures isn't the greatest bug report either ;) sorry :)