arbor icon indicating copy to clipboard operation
arbor copied to clipboard

Arbor Instant 🐛 🪦 Mode

Open Helveg opened this issue 2 years ago • 8 comments

Describe the feature you need Arbor Instant Bug Squash Mode: Turn on a flag, and run a dryrun of the model building phase, where errors like this are immediately raised when the instruction is given, so that a useful stack trace is obtained, and one can actually debug their model building code:

Traceback (most recent call last):
  File "/users/bp000347/arbenv/bin/bsb", line 33, in <module>
    sys.exit(load_entry_point('bsb', 'console_scripts', 'bsb')())
  File "/users/bp000347/arbenv/bsb/bsb/cli.py", line 26, in scaffold_cli
    start_cli()
  File "/users/bp000347/arbenv/bsb/bsb/cli.py", line 255, in start_cli
    scaffoldInstance.run_simulation(cl_args.simulation, quit=True)
  File "/users/bp000347/arbenv/bsb/bsb/core.py", line 386, in run_simulation
    simulation, simulator = self.prepare_simulation(simulation_name)
  File "/users/bp000347/arbenv/bsb/bsb/core.py", line 419, in prepare_simulation
    simulator = simulation.prepare()
  File "/users/bp000347/arbenv/bsb/bsb/simulators/arbor/adapter.py", line 456, in prepare
    simulation = arbor.simulation(recipe, self.domain, context)
RuntimeError: Model building error on cell 26: connection endpoint label "comp_0": label does not exist.

Explain what it is supposed to enable I don't like having to emulate a debugger :) I need to know where in my code this instruction was given, and how I got there.

Additional context 🐝

Helveg avatar Aug 01 '22 13:08 Helveg

I think this is impossible given our current model building: For example, label resolution is performed during simulation construction.

thorstenhater avatar Aug 03 '22 15:08 thorstenhater

Well the feature request is an alternative to actual model building, which'll check whether model building would fail, and raises the exception at the arbor.* method that gave the instruction.

Actually also a good candidate for a separate package.

Helveg avatar Aug 04 '22 18:08 Helveg

I have felt that frustration before, but I fear I see no way out of it.

That said, and to justify my 'ton of work' comment: simulation construction is when morphology, decor, connectivity, and (morphology) labels come together. I'd argue that it's almost the earliest point we can throw these errors. Let's go over the components one by one

  1. Morphology labels: we are allowing
    d = label_dict({'foo': '(restrict (terminal) (region "bar"))'})
    # Can we throw here? Maybe, but:
    d['bar'] = '(all)'
    
    Similar, we could construct expressions that are erroneous give a concrete morphology. What would you propose do here?
  2. Labels and decor: It's easy to see how paint/place can access labels that do not exist. At this point, we have to merge morphology, labels, and decor.
  3. Finally, connectivity. Needs a decor where the connection endpoints have been (correctly?) placed, thus morphology and labels. Worse, synaptic connections need remote labels. Now we are effectively merging all of the construction, making it synchronous across all nodes, and evaluate everything eagerly. Moreover, scripts that are valid in 'production' mode are now invalid in 'check' mode.

So, sadly I see no way of implementing your request without changing Arbor's way of doing things by 180º.

thorstenhater avatar Aug 04 '22 20:08 thorstenhater

In check mode you tag all objects with the traces that placed the instructions on the object. Even for (3) this works, the conn endpoints are checked across nodes at some point.

You can raise, capture and store Exceptions when things like arbor.label_dict (1), arbor.decor.paint (2), or arbor.connection (3) are called, to then reraise them when their instruction turns out to be involved in an error.

I'll see how far I get using wrapped objects, maybe I can provide a debug module for something like import arbor.debug as arbor ;p My solution would have to parse error messages though, and be coupled to them.

solution(now) < ton_of_work

Helveg avatar Aug 04 '22 22:08 Helveg

If doable, that would be pretty cool, go for it. It'll require tagging this

decor.place(..., ..., 'my_tåg')

connection(..., 'my_tag') # attach trace here silently.

in Python with a stacktrace (which you can get quite easily, I think) and then passing that through to C++ as a type erased thingy (technical term). This thingy™ would have to be attached to the thrown C++ exception and converted back into something useful in Python land before re-raising. Note that the exception is thrown in simulation construction by neccessity, as outlined above. Or do I misunderstand your proposal?

thorstenhater avatar Aug 05 '22 06:08 thorstenhater

Yea, I'm thinking:

class PaintException(Exception):
  pass

def trace(exc, *args, **kwargs):
  try:
    raise exc(args, kwargs)
  except Exception as e:
    return e

class TracedDecor(arbor.decor):
  def place(self, location, mech, labels):
    self.__dict__.setdefault(
      "_mech_not_found",
      defaultdict(list)
    )[mech.name].append(
      trace(PaintException, mech)
    )
    super().place(location, mech, labels)

def simulate(*args):
  try:
    arbor.simulate(*args)
  except Exception as e:
    parse_exception(e)

And in parse exception figure out which traced object we need to get, what property to look up for it and which offending trace to show. If we pass into CPP we could prevent having to parse the exception.

Helveg avatar Aug 05 '22 06:08 Helveg

Ok, that seems quite brittle, eg if we change the exception wording everything will break?

thorstenhater avatar Aug 05 '22 07:08 thorstenhater

My solution would have to parse error messages though, and be coupled to them.

Yes :[ That's the best I can do from Python, I'll make a PoC for 1 exception type, then we can think about a solution with CPP. Perhaps it can be enough that the arguments that are formatted into the error message are also present in a structured way as arguments on the exception so that I can use e.args[1:] instead of having to parse anything, otherwise, you let me know what can be done from CPP.

Helveg avatar Aug 05 '22 08:08 Helveg