-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for partial config files #981
Conversation
I'm sure I've probably forgotten to do a chore or add something so please let me know what they are 😅 But the tests pass (so far) so I think that's a good sign. Always scary editing the early code in TopoStats |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice way of handling the increasingly complex configuration file and I like the way any specified configuration updates the default and then other command line options take precedence over those.1
I've suggested an alternative perhaps more descriptive name and tried to expand on the docstrings to give users a better understanding of what options will be used where.
It would be worthwhile updating docs/configuration.md
with an explanation of how this works and perhaps examples (on the off chance someone reads the documentation!).
I think we'll have to live with topostats create-config
including everything as it would add a lot of complex code to specify which options should be written to file.
Footnotes
-
As it happens I spent most of Tuesday going through
topostats/entry_point.py
and adding all the configuration options as command line arguments to each entry point as part of Remodel entry points and Command Line Interface to use "Swiss Army knife" approach #517. ↩
|
||
# Override the config with command line arguments passed in, eg --output_dir ./output/ | ||
if args is not None: | ||
config = update_config(config, args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be worthwhile replacing update_config()
in favour of the typed merge_mappings()
? They are doing the same thing, although update_config()
doesn't use the MutableMapping
type and also converts path strings to Path()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm so sorry I didn't realise I had re-written your function. I assumed update_config did something different!
Don't want to tread on toes, so happy to remove merge_mappings
in favour of update_config
if you like!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't worry its only code and I'm not possessive about it.
I don't mind which remains bit glad that we converged on the same solution!
Go with whichever you think is best, currently I think that is merge_mappings()
, stronger type-hints and clearer docstring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried replacing it, but they do slightly different things, in that update_config
takes argparse.Namespace
objects, while merge_mappings
takes only MutableMappings
, attempting to replace causes errors with iteration. I tried to fix this by using vars(args)
on the input but it still fails, so if it's okay with you, I'd suggest keeping both for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, keep both but write up an issue to go through and reconcile the two in the future. On the surface they look very similar so it should be possible to write one function.
…ns-rse Co-authored-by: Neil Shephard <[email protected]>
Co-authored-by: Neil Shephard <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding the docs/configuration.md
@SylviaWhittle
I think this is ready to go.
I've noted the possibility of reconciling update_config()
and merge_mappings()
into a single function in #984.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding the docs/configuration.md
@SylviaWhittle
I think this is ready to go.
I've noted the possibility of reconciling update_config()
and merge_mappings()
into a single function in #984.
Rebase onto main after #985 |
Failing test:
on MacOS 3.9 & 3.11 (so probably 3.10 too) Annoyingly I cannot recreate this using the testing function in VSCode but I can using pytest in the terminal directly. Seems to be due to Skan version I'll work out how to do a cherry pick commit now :) |
The release of [skan-0.12.0]() introduced the below error where the type is wrong when `nbgraph()` uses `numba`. For now we pin `skan==0.11.1` ``` tests/tracing/test_disordered_tracing.py:1265: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ topostats/tracing/disordered_tracing.py:660: in disordered_trace_grain "branch_types": get_skan_image( topostats/tracing/disordered_tracing.py:697: in get_skan_image skan_skeleton = skan.Skeleton(skeleton_image, spacing=1e-9, value_is_height=True) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/skan/csr.py:532: in __init__ self.nbgraph = csr_to_nbgraph(graph, self.pixel_values) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/skan/csr.py:205: in csr_to_nbgraph return NBGraph( /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/experimental/jitclass/base.py:124: in __call__ return cls._ctor(*bind.args[1:], **bind.kwargs) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:442: in _compile_for_args raise e /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:375: in _compile_for_args return_val = self.compile(tuple(argtypes)) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:905: in compile cres = self._compiler.compile(args, return_type) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:80: in compile status, retval = self._compile_cached(args, return_type) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:94: in _compile_cached retval = self._compile_core(args, return_type) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:107: in _compile_core cres = compiler.compile_extra(self.targetdescr.typing_context, /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:744: in compile_extra return pipeline.compile_extra(func) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:438: in compile_extra return self._compile_bytecode() /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:506: in _compile_bytecode return self._compile_core() /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:481: in _compile_core raise e /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:472: in _compile_core pm.run(self.state) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:364: in run raise e /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:356: in run self._runPass(idx, pass_inst, state) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_lock.py:35: in _acquire_compile_lock return func(*args, **kwargs) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:311: in _runPass mutated |= check(pss.run_pass, internal_state) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:273: in check mangled = func(compiler_state) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typed_passes.py:112: in run_pass typemap, return_type, calltypes, errs = type_inference_stage( /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typed_passes.py:93: in type_inference_stage errs = infer.propagate(raise_errors=raise_errors) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:1083: in propagate errors = self.constraints.propagate(self) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:182: in propagate raise e /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:160: in propagate constraint(typeinfer) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:583: in __call__ self.resolve(typeinfer, typevars, fnty) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:606: in resolve sig = typeinfer.resolve_call(fnty, pos_args, kw_args) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:1577: in resolve_call return self.context.resolve_function_type(fnty, pos_args, kw_args) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typing/context.py:196: in resolve_function_type res = self._resolve_user_function_type(func, args, kws) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typing/context.py:248: in _resolve_user_function_type return func.get_call_type(self, args, kws) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/types/misc.py:441: in get_call_type return self.ctor_template(context).apply(args, kws) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typing/templates.py:350: in apply sig = generic(args, kws) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/experimental/jitclass/base.py:269: in generic sig = disp_type.get_call_type(self.context, boundargs, kws) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/types/functions.py:541: in get_call_type self.dispatcher.get_call_template(args, kws) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:318: in get_call_template self.compile(tuple(args)) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:905: in compile cres = self._compiler.compile(args, return_type) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:80: in compile status, retval = self._compile_cached(args, return_type) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:94: in _compile_cached retval = self._compile_core(args, return_type) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:107: in _compile_core cres = compiler.compile_extra(self.targetdescr.typing_context, /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:744: in compile_extra return pipeline.compile_extra(func) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:438: in compile_extra return self._compile_bytecode() /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:506: in _compile_bytecode return self._compile_core() /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:481: in _compile_core raise e /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:472: in _compile_core pm.run(self.state) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:364: in run raise e /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:356: in run self._runPass(idx, pass_inst, state) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_lock.py:35: in _acquire_compile_lock return func(*args, **kwargs) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:311: in _runPass mutated |= check(pss.run_pass, internal_state) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:273: in check mangled = func(compiler_state) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typed_passes.py:468: in run_pass lower.lower() /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:187: in lower self.lower_normal_function(self.fndesc) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:226: in lower_normal_function entry_block_tail = self.lower_function_body() /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:256: in lower_function_body self.lower_block(block) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:270: in lower_block self.lower_inst(inst) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:564: in lower_inst return impl(self.builder, (target, value)) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/base.py:627: in wrapped return impl(self, builder, sig, args, attr) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/imputils.py:166: in res return real_impl(context, builder, sig, args, attr) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/experimental/jitclass/base.py:505: in set_attr_impl setattr(data, _mangle_attr(attr), val) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/cgutils.py:164: in __setattr__ self[self._datamodel.get_field_position(field)] = value /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/cgutils.py:178: in __setitem__ value = self._cast_member_from_value(index, value) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/cgutils.py:251: in _cast_member_from_value return model.as_data(self._builder, val) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/datamodel/models.py:586: in as_data struct = builder.insert_value(struct, el, [i]) /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/llvmlite/ir/builder.py:983: in insert_value instr = instructions.InsertValue(self.block, agg, value, idx, name=name) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <[AttributeError("'InsertValue' object has no attribute '_name'") raised in repr()] InsertValue object at 0x7ccfbd4f6b90>, parent = <ir.Block 'B0' of type 'label'> agg = <ir.InsertValue '.258' of type '{i8*, i8*, i64, i64, double*, [1 x i64], [1 x i64]}', opname 'insertvalue', operands [...value=<llvmlite.ir.values._Undefined object at 0x7cd0142cda50>>, <ir.Argument 'arg.node_props.6.0' of type i64>]>]>]>]> elem = <ir.ExtractValue 'extracted.data.22' of type 'float*', opname 'extractvalue', operands [<ir.InsertValue 'inserted.stri...' value=<llvmlite.ir.values._Undefined object at 0x7cd0142cda50>>, <ir.Argument 'arg.node_props.6.0' of type i64>]>]>]> indices = [4], name = '' def __init__(self, parent, agg, elem, indices, name=''): typ = agg.type try: for i in indices: typ = typ.elements[i] except (AttributeError, IndexError): raise TypeError("Can't index at %r in %s" % (list(indices), agg.type)) if elem.type != typ: > raise TypeError("Can only insert %s at %r in %s: got %s" % (typ, list(indices), agg.type, elem.type)) E TypeError: Can only insert double* at [4] in {i8*, i8*, i64, i64, double*, [1 x i64], [1 x i64]}: got float* /home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/llvmlite/ir/instructions.py:702: TypeError __________________________________________________________________________________________ tests/test_run_topostats.py ___________________________________________________________________________________________ ```
Thank you very much @MaxGamill-Sheffield |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOVE this, and love that it:
- can remove practically all files
- produces the config file output which contains all the added-by-default params on run
Buuut, how does this get to the users as they'll still be blindsided by a thicc config file upon use of the topostats create-config
command.
Should we think about adding a topostats create-config --simple
to make a reduced version that's not as scary, perhaps with the following keys / values (+ some commonly used by the experimentalists?)
base_dir: ./tests/resources # Directory in which to search for data files
output_dir: ./output # Directory to output results to
cores: 3 # Number of CPU cores to utilise for processing multiple files simultaneously.
loading:
channel: Height # Channel to pull data from in the data files.
filter:
run: true # Options : true, false
grains:
run: true # Options : true, false
grainstats:
run: true # Options : true, false
disordered_tracing:
run: true # Options : true, false
nodestats:
run: true # Options : true, false
ordered_tracing:
run: true
splining:
run: true # Options : true, false
plotting:
run: true # Options : true, false
summary_stats:
run: true # Whether to make summary plots for output data
Additionally, one issue I have seen is that any fields designated as null
but are not null
in the default config are overwritten. i.e. modifying the grains.absolute_area_threshold
to:
above: [null, null] # above surface [Low, High] in nm^2 (also takes null)
below: [null, null] # below surface [Low, High] in nm^2 (also takes null)
runs and replaces the above
values with [300, 3000]
in the produced config file.
So at what level does it replace the values? Is it for each iterative key-value or the whole block if a little bit of it is missing?
Windows 3.9 test fails but looks fishy and nothing:
but I can't test this on my machine |
Looks brill to me (&max)
… On 7 Nov 2024, at 15:48, Sylvia Whittle ***@***.***> wrote:
How about this for a simple config?
image.png (view on web) <https://github.com/user-attachments/assets/8d3e60b1-a992-4571-beb5-30ab743b8763>
This is probably something that will be argued about before deciding on a single simple config, so we can always edit it later once there is consensus.
—
Reply to this email directly, view it on GitHub <#981 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ADJSFMOTAQCDVAMTERNQPMDZ7ODT5AVCNFSM6AAAAABQPSXZMSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINRSGU3TMNJYGQ>.
You are receiving this because you are subscribed to this thread.
|
Looks like the tests pass. @MaxGamill-Sheffield I was unable to replicate your issue with the |
As discussed in the recent TopoStats catchup, the config file is about to explode with parameters. This will result in a rather cumbersome config file for users which will be intimidating.
After fielding a few options to the experimentalists, it was decided that the least bad way forward might be to add support for partial config files, where any missing fields are added in from the
default_config.yaml
.This PR is attempting to do just that. Though I'm sure I've forgotten something.
Oh also this PR is entirely type safe (I think?, at least according to mypy!) It brings some type safety to the beginning of
run_topostats.py
and marks my first foray into generic typing!