From e6c6b3b67713f15ad56deb98744729d94a45ef3f Mon Sep 17 00:00:00 2001 From: Henri Pesonen Date: Tue, 4 Aug 2020 09:32:24 +0200 Subject: [PATCH 1/9] Fix readthedocs build (#331) * Add a second line to a code-block * Remove code-blocks from quickstart.rst * Change code to code-block * Test whether code or code-block works * Test ipython vs ipython3 block-type * Pin ipython-version in requirements * Print plugin_lexers * Added explicit requirement on Sphinx version * Modify conf.py * Add IPython.sphinxext.ipython_console_highlighting to extentions in conf.py * Update requirements.txt * Add .readthedocs.yml to force python3.5 build on rtd * Add requirements-file-def in .readthedocs.yml * Modify requirements.txt * Remove editor-specific file * Add vscode-files to .gitignore * Modify CHANGELOG --- .gitignore | 3 +++ .readthedocs.yml | 24 ++++++++++++++++++++++++ CHANGELOG.rst | 3 +++ requirements.txt | 2 +- 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .readthedocs.yml diff --git a/.gitignore b/.gitignore index 8bba4641..619ec5c4 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,6 @@ ENV/ *.swp notebooks/mydask.png + +# vscode-settings +.vscode \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..16a3256a --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,24 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Build documentation with MkDocs +#mkdocs: +# configuration: mkdocs.yml + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.5 + install: + - requirements: requirements.txt \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a612ff0d..f91e3008 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,9 @@ Changelog ========= +- Fix readthedocs-build by adding .readthedocs.yml and restricting the build to + python3.5, for now + 0.7.6 (2020-08-29) ------------------ - Fix incompatibility with scipy>1.5 in bo.utils.stochastic_optimization diff --git a/requirements.txt b/requirements.txt index 113bf97c..bedbb0bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,4 @@ GPy>=1.0.9 networkX>=1.11,<2.0 ipyparallel>=6 toolz>=0.8 -scikit-learn>=0.18.1 +scikit-learn>=0.18.1 \ No newline at end of file From accd0461745bff3edf7d6375bed12c1552c734c8 Mon Sep 17 00:00:00 2001 From: Henri Pesonen Date: Thu, 6 Aug 2020 19:52:42 +0200 Subject: [PATCH 2/9] Fix documentation (#332) * Fix example docstring in adjust_posterior * Fix docstrings in elfi.methods.bo.acquisition * Suppress waitbar in postprocessing example which causes travis to fail --- CHANGELOG.rst | 1 + elfi/methods/bo/acquisition.py | 65 ++++++++++++++++++--------------- elfi/methods/post_processing.py | 10 ++--- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f91e3008..0ced606c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,7 @@ Changelog ========= +- Improve docstrings in elfi.methods.bo.acquisition - Fix readthedocs-build by adding .readthedocs.yml and restricting the build to python3.5, for now diff --git a/elfi/methods/bo/acquisition.py b/elfi/methods/bo/acquisition.py index 10d55c90..d11de94f 100644 --- a/elfi/methods/bo/acquisition.py +++ b/elfi/methods/bo/acquisition.py @@ -261,23 +261,23 @@ class MaxVar(AcquisitionBase): The next evaluation point is acquired in the maximiser of the variance of the unnormalised approximate posterior. - \theta_{t+1} = arg max Var(p(\theta) * p_a(\theta)), + .. math:: \theta_{t+1} = \arg \max \text{Var}(p(\theta) \cdot p_a(\theta)), - where the unnormalised likelihood p_a is defined - using the CDF of normal distribution, \Phi, as follows: + where the unnormalised likelihood :math:`p_a` is defined + using the CDF of normal distribution, :math:`\Phi`, as follows: - p_a(\theta) = - (\Phi((\epsilon - \mu_{1:t}(\theta)) / \sqrt(v_{1:t}(\theta) + \sigma2_n))), + .. math:: p_a(\theta) = \Phi((\epsilon - \mu_{1:t}(\theta)) / + \sqrt{v_{1:t}(\theta) + \sigma^2_n}), - where \epsilon is the ABC threshold, \mu_{1:t} and v_{1:t} are - determined by the Gaussian process, \sigma2_n is the noise. + where \epsilon is the ABC threshold, :math:`\mu_{1:t}` and :math:`v_{1:t}` are + determined by the Gaussian process, :math:`\sigma^2_n` is the noise. References ---------- - [1] Järvenpää et al. (2017). arXiv:1704.00520 - [2] Gutmann M U, Corander J (2016). Bayesian Optimization for - Likelihood-Free Inference of Simulator-Based Statistical Models. - JMLR 17(125):1−47, 2016. http://jmlr.org/papers/v17/15-017.html + Järvenpää et al. (2019). Efficient Acquisition Rules for Model-Based + Approximate Bayesian Computation. Bayesian Analysis 14(2):595-622, 2019 + https://projecteuclid.org/euclid.ba/1537258134 + """ @@ -425,22 +425,24 @@ class RandMaxVar(MaxVar): The next evaluation point is sampled from the density corresponding to the variance of the unnormalised approximate posterior (The MaxVar acquisition function). - \theta_{t+1} ~ q(\theta), + .. math:: \theta_{t+1} \thicksim q(\theta), - where q(\theta) \propto Var(p(\theta) * p_a(\theta)) and - the unnormalised likelihood p_a is defined - using the CDF of normal distribution, \Phi, as follows: + where :math:`q(\theta) \propto \text{Var}(p(\theta) \cdot p_a(\theta))` and + the unnormalised likelihood :math:`p_a` is defined + using the CDF of normal distribution, :math:`\Phi`, as follows: - p_a(\theta) = - (\Phi((\epsilon - \mu_{1:t}(\theta)) / \sqrt(\v_{1:t}(\theta) + \sigma2_n))), + .. math:: p_a(\theta) = \Phi((\epsilon - \mu_{1:t}(\theta)) / + \sqrt{v_{1:t}(\theta) + \sigma^2_n} ), - where \epsilon is the ABC threshold, \mu_{1:t} and \v_{1:t} are - determined by the Gaussian process, \sigma2_n is the noise. + where :math:`\epsilon` is the ABC threshold, :math:`\mu_{1:t}` and :math:`v_{1:t}` are + determined by the Gaussian process, :math:`\sigma^2_n` is the noise. References ---------- - [1] arXiv:1704.00520 (Järvenpää et al., 2017) + Järvenpää et al. (2019). Efficient Acquisition Rules for Model-Based + Approximate Bayesian Computation. Bayesian Analysis 14(2):595-622, 2019 + https://projecteuclid.org/euclid.ba/1537258134 """ @@ -565,23 +567,26 @@ class ExpIntVar(MaxVar): Essentially, we define a loss function that measures the overall uncertainty in the unnormalised ABC posterior over the parameter space. The value of the loss function depends on the next simulation and thus - the next evaluation location \theta^* is chosen to minimise the expected loss. + the next evaluation location :math:`\theta^*` is chosen to minimise the expected loss. - \theta_{t+1} = arg min_{\theta^* \in \Theta} L_{1:t}(\theta^*), where + .. math:: \theta_{t+1} = arg min_{\theta^* \in \Theta} L_{1:t}(\theta^*), - \Theta is the parameter space, and L is the expected loss function approximated as follows: + where :math:`\Theta` is the parameter space, and :math:`L` is the expected loss + function approximated as follows: - L_{1:t}(\theta^*) \approx 2 * \sum_{i=1}^s (\omega^i * p^2(\theta^i) - * w_{1:t+1})(theta^i, \theta^*), where + .. math:: L_{1:t}(\theta^*) \approx 2 * \sum_{i=1}^s (\omega^i \cdot p^2(\theta^i) + \cdot w_{1:t+1}(\theta^i, \theta^*), - \omega^i is an importance weight, - p^2(\theta^i) is the prior squared, and - w_{1:t+1})(theta^i, \theta^*) is the expected variance of the unnormalised ABC posterior - at \theta^i after running the simulation model with parameter \theta^* + where :math:`\omega^i` is an importance weight, + :math:`p^2(\theta^i)` is the prior squared, and + :math:`w_{1:t+1}(\theta^i, \theta^*)` is the expected variance of the unnormalised ABC + posterior at \theta^i after running the simulation model with parameter :math:`\theta^*` References ---------- - [1] arXiv:1704.00520 (Järvenpää et al., 2017) + Järvenpää et al. (2019). Efficient Acquisition Rules for Model-Based + Approximate Bayesian Computation. Bayesian Analysis 14(2):595-622, 2019 + https://projecteuclid.org/euclid.ba/1537258134 """ diff --git a/elfi/methods/post_processing.py b/elfi/methods/post_processing.py index 847a2e65..6d410f29 100644 --- a/elfi/methods/post_processing.py +++ b/elfi/methods/post_processing.py @@ -239,11 +239,11 @@ def adjust_posterior(sample, model, summary_names, parameter_names=None, adjustm Examples -------- - import elfi - from elfi.examples import gauss - m = gauss.get_model() - res = elfi.Rejection(m['d'], output_names=['ss_mean', 'ss_var']).sample(1000) - adj = adjust_posterior(res, m, ['ss_mean', 'ss_var'], ['mu'], LinearAdjustment()) + >>> import elfi + >>> from elfi.examples import gauss + >>> m = gauss.get_model() + >>> res = elfi.Rejection(m['d'], output_names=['ss_mean', 'ss_var']).sample(1000, bar=False) + >>> adj = adjust_posterior(res, m, ['ss_mean', 'ss_var'], ['mu'], LinearAdjustment()) """ adjustment = _get_adjustment(adjustment) From 6fae9719609b73fc0ea89db62ccea1ef5d1b16f5 Mon Sep 17 00:00:00 2001 From: Henri Pesonen Date: Mon, 21 Sep 2020 13:12:53 +0200 Subject: [PATCH 3/9] Add networkx2 support (#333) * Change networkx support from 1.X to 2.X, initial commit * Modify ELFI-model to follow structure of networkx2.X * Fix docstring-example for the tests * Fix docstring-example, again * Fix graphviz visualization for networkx2.X * Fix post_processing.py docstring * Fix post_processing.py docstring --- CHANGELOG.rst | 1 + elfi/client.py | 10 ++++++++-- elfi/compiler.py | 25 ++++++++++++------------- elfi/executor.py | 15 ++++++++------- elfi/loader.py | 16 ++++++++-------- elfi/methods/post_processing.py | 3 ++- elfi/methods/utils.py | 6 ++++-- elfi/model/elfi_model.py | 16 ++++++++-------- elfi/model/graphical_model.py | 13 ++++++------- elfi/visualization/visualization.py | 6 +++--- requirements.txt | 2 +- tests/functional/test_compilation.py | 9 ++++----- tests/unit/test_tools.py | 2 +- 13 files changed, 66 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0ced606c..c22a9982 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,7 @@ Changelog ========= +- Change networkx support from 1.X to 2.X - Improve docstrings in elfi.methods.bo.acquisition - Fix readthedocs-build by adding .readthedocs.yml and restricting the build to python3.5, for now diff --git a/elfi/client.py b/elfi/client.py index 071cc274..a431cf56 100644 --- a/elfi/client.py +++ b/elfi/client.py @@ -159,7 +159,8 @@ def submit(self, batch=None): loaded_net = self.client.load_data(self.compiled_net, self.context, batch_index) # Override for k, v in batch.items(): - loaded_net.node[k] = {'output': v} + loaded_net.nodes[k].update({'output': v}) + del loaded_net.nodes[k]['operation'] task_id = self.client.submit(loaded_net) self._pending_batches[batch_index] = task_id @@ -299,7 +300,12 @@ def compile(cls, source_net, outputs=None): outputs = source_net.nodes() if not outputs: logger.warning("Compiling for no outputs!") - outputs = outputs if isinstance(outputs, list) else [outputs] + if isinstance(outputs, list): + outputs = set(outputs) + elif isinstance(outputs, type(source_net.nodes())): + outputs = outputs + else: + outputs = [outputs] compiled_net = nx.DiGraph( outputs=outputs, name=source_net.graph['name'], observed=source_net.graph['observed']) diff --git a/elfi/compiler.py b/elfi/compiler.py index 78d80820..84f4253e 100644 --- a/elfi/compiler.py +++ b/elfi/compiler.py @@ -54,8 +54,8 @@ def compile(cls, source_net, compiled_net): compiled_net.add_edges_from(source_net.edges(data=True)) # Compile the nodes to computation nodes - for name, data in compiled_net.nodes_iter(data=True): - state = source_net.node[name] + for name, data in compiled_net.nodes(data=True): + state = source_net.nodes[name]['attr_dict'] if '_output' in state and '_operation' in state: raise ValueError("Cannot compile: both _output and _operation present " "for node '{}'".format(name)) @@ -92,7 +92,7 @@ def compile(cls, source_net, compiled_net): uses_observed = [] for node in nx.topological_sort(source_net): - state = source_net.node[node] + state = source_net.nodes[node]['attr_dict'] if state.get('_observable'): observable.append(node) cls.make_observed_copy(node, compiled_net) @@ -113,14 +113,14 @@ def compile(cls, source_net, compiled_net): else: link_parent = parent - compiled_net.add_edge(link_parent, obs_node, source_net[parent][node].copy()) + compiled_net.add_edge(link_parent, obs_node, **source_net[parent][node].copy()) # Check that there are no stochastic nodes in the ancestors for node in uses_observed: # Use the observed version to query observed ancestors in the compiled_net obs_node = observed_name(node) for ancestor_node in nx.ancestors(compiled_net, obs_node): - if '_stochastic' in source_net.node.get(ancestor_node, {}): + if '_stochastic' in source_net.nodes.get(ancestor_node, {}): raise ValueError("Observed nodes must be deterministic. Observed " "data depends on a non-deterministic node {}." .format(ancestor_node)) @@ -148,11 +148,10 @@ def make_observed_copy(cls, node, compiled_net, operation=None): raise ValueError("Observed node {} already exists!".format(obs_node)) if operation is None: - compiled_dict = compiled_net.node[node].copy() + compiled_dict = compiled_net.nodes[node].copy() else: compiled_dict = dict(operation=operation) - - compiled_net.add_node(obs_node, compiled_dict) + compiled_net.add_node(obs_node, **compiled_dict) return obs_node @@ -176,8 +175,8 @@ def compile(cls, source_net, compiled_net): instruction_node_map = dict(_uses_batch_size='_batch_size', _uses_meta='_meta') for instruction, _node in instruction_node_map.items(): - for node, d in source_net.nodes_iter(data=True): - if d.get(instruction): + for node, d in source_net.nodes(data=True): + if d['attr_dict'].get(instruction): if not compiled_net.has_node(_node): compiled_net.add_node(_node) compiled_net.add_edge(_node, node, param=_node[1:]) @@ -203,8 +202,8 @@ def compile(cls, source_net, compiled_net): logger.debug("{} compiling...".format(cls.__name__)) _random_node = '_random_state' - for node, d in source_net.nodes_iter(data=True): - if '_stochastic' in d: + for node, d in source_net.nodes(data=True): + if '_stochastic' in d['attr_dict']: if not compiled_net.has_node(_random_node): compiled_net.add_node(_random_node) compiled_net.add_edge(_random_node, node, param='random_state') @@ -230,7 +229,7 @@ def compile(cls, source_net, compiled_net): outputs = compiled_net.graph['outputs'] output_ancestors = nbunch_ancestors(compiled_net, outputs) - for node in compiled_net.nodes(): + for node in list(compiled_net.nodes()): if node not in output_ancestors: compiled_net.remove_node(node) return compiled_net diff --git a/elfi/executor.py b/elfi/executor.py index f8aca08f..4f20fb6f 100644 --- a/elfi/executor.py +++ b/elfi/executor.py @@ -57,7 +57,7 @@ def execute(cls, G): order = cls.get_execution_order(G) for node in order: - attr = G.node[node] + attr = G.nodes[node] logger.debug("Executing {}".format(node)) if attr.keys() >= {'operation', 'output'}: @@ -67,7 +67,8 @@ def execute(cls, G): if 'operation' in attr: op = attr['operation'] try: - G.node[node] = cls._run(op, node, G) + G.nodes[node].update(cls._run(op, node, G)) + del G.nodes[node]['operation'] except Exception as exc: raise exc.__class__("In executing node '{}': {}." .format(node, exc)).with_traceback(exc.__traceback__) @@ -77,7 +78,7 @@ def execute(cls, G): '{}'.format(node)) # Make a result dict based on the requested outputs - result = {k: G.node[k]['output'] for k in G.graph['outputs']} + result = {k: G.nodes[k]['output'] for k in G.graph['outputs']} return result @classmethod @@ -104,7 +105,7 @@ def get_execution_order(cls, G): output_nodes = G.graph['outputs'] # Filter those output nodes who have an operation to run - needed = tuple(sorted(node for node in output_nodes if 'operation' in G.node[node])) + needed = tuple(sorted(node for node in output_nodes if 'operation' in G.nodes[node])) if needed not in cache: # Resolve the nodes that need to be executed in the graph @@ -115,9 +116,9 @@ def get_execution_order(cls, G): sort_order = cache['sort_order'] # Resolve the dependencies of needed - dep_graph = nx.DiGraph(G.edges()) + dep_graph = nx.DiGraph(G.edges) for node in sort_order: - attr = G.node[node] + attr = G.nodes[node] if attr.keys() >= {'operation', 'output'}: raise ValueError('Generative graph has both op and output present') @@ -143,7 +144,7 @@ def _run(fn, node, G): for parent_name in G.predecessors(node): param = G[parent_name][node]['param'] - output = G.node[parent_name]['output'] + output = G.nodes[parent_name]['output'] if isinstance(param, int): args.append((param, output)) else: diff --git a/elfi/loader.py b/elfi/loader.py index eae8688e..9b64eb80 100644 --- a/elfi/loader.py +++ b/elfi/loader.py @@ -52,7 +52,8 @@ def load(cls, context, compiled_net, batch_index): obs_name = observed_name(name) if not compiled_net.has_node(obs_name): continue - compiled_net.node[obs_name] = dict(output=obs) + compiled_net.nodes[obs_name].update(dict(output=obs)) + del compiled_net.nodes[obs_name]['operation'] del compiled_net.graph['observed'] return compiled_net @@ -86,9 +87,8 @@ def load(cls, context, compiled_net, batch_index): details = dict(_batch_size=context.batch_size, _meta=meta_dict) for node, v in details.items(): - if compiled_net.has_node(node): - compiled_net.node[node]['output'] = v - + if node in compiled_net: + compiled_net.nodes[node]['output'] = v return compiled_net @@ -119,12 +119,12 @@ def load(cls, context, compiled_net, batch_index): if not compiled_net.has_node(node): continue elif node in batch: - compiled_net.node[node]['output'] = batch[node] - compiled_net.node[node].pop('operation', None) + compiled_net.nodes[node]['output'] = batch[node] + compiled_net.nodes[node].pop('operation', None) elif node not in compiled_net.graph['outputs']: # We are missing this item from the batch so add the output to the # requested outputs so that it can be stored when the results arrive - compiled_net.graph['outputs'].append(node) + compiled_net.graph['outputs'].add(node) return compiled_net @@ -173,6 +173,6 @@ def load(cls, context, compiled_net, batch_index): # Assign the random state or its acquirer function to the corresponding node node_name = '_random_state' if compiled_net.has_node(node_name): - compiled_net.node[node_name][key] = random_state + compiled_net.nodes[node_name][key] = random_state return compiled_net diff --git a/elfi/methods/post_processing.py b/elfi/methods/post_processing.py index 6d410f29..b1efb741 100644 --- a/elfi/methods/post_processing.py +++ b/elfi/methods/post_processing.py @@ -242,7 +242,8 @@ def adjust_posterior(sample, model, summary_names, parameter_names=None, adjustm >>> import elfi >>> from elfi.examples import gauss >>> m = gauss.get_model() - >>> res = elfi.Rejection(m['d'], output_names=['ss_mean', 'ss_var']).sample(1000, bar=False) + >>> res = elfi.Rejection(m['d'], output_names=['ss_mean', 'ss_var'],\ + batch_size=10).sample(500, bar=False) >>> adj = adjust_posterior(res, m, ['ss_mean', 'ss_var'], ['mu'], LinearAdjustment()) """ diff --git a/elfi/methods/utils.py b/elfi/methods/utils.py index fb0ddcc4..b9374534 100644 --- a/elfi/methods/utils.py +++ b/elfi/methods/utils.py @@ -339,7 +339,8 @@ def rvs(self, size=None, random_state=None): # Change to the correct random_state instance # TODO: allow passing random_state to ComputationContext seed - loaded_net.node['_random_state'] = {'output': random_state} + loaded_net.nodes['_random_state'].update({'output': random_state}) + del loaded_net.nodes['_random_state']['operation'] batch = self.client.compute(loaded_net) rvs = np.column_stack([batch[p] for p in self.parameter_names]) @@ -377,7 +378,8 @@ def _evaluate_pdf(self, x, log=False): # Override for k, v in batch.items(): - loaded_net.node[k] = {'output': v} + loaded_net.nodes[k].update({'output': v}) + del loaded_net.nodes[k]['operation'] val = self.client.compute(loaded_net)[node] if ndim == 0 or (ndim == 1 and self.dim > 1): diff --git a/elfi/model/elfi_model.py b/elfi/model/elfi_model.py index 3bbb071b..26edba42 100644 --- a/elfi/model/elfi_model.py +++ b/elfi/model/elfi_model.py @@ -304,7 +304,7 @@ def get_reference(self, name): name : str """ - cls = self.get_node(name)['_class'] + cls = self.get_node(name)['attr_dict']['_class'] return cls.reference(name, self) def get_state(self, name): @@ -315,7 +315,7 @@ def get_state(self, name): name : str """ - return self.source_net.node[name] + return self.source_net.nodes[name] def update_node(self, name, updating_name): """Update `node` with `updating_node` in the model. @@ -357,7 +357,7 @@ def remove_node(self, name): @property def parameter_names(self): """Return a list of model parameter names in an alphabetical order.""" - return sorted([n for n in self.nodes if '_parameter' in self.get_state(n)]) + return sorted([n for n in self.nodes if '_parameter' in self.get_state(n)['attr_dict']]) @parameter_names.setter def parameter_names(self, parameter_names): @@ -374,7 +374,7 @@ def parameter_names(self, parameter_names): """ parameter_names = set(parameter_names) for n in self.nodes: - state = self.get_state(n) + state = self.get_state(n)['attr_dict'] if n in parameter_names: parameter_names.remove(n) state['_parameter'] = True @@ -453,11 +453,11 @@ def state(self): @property def uses_meta(self): - return self.state.get('_uses_meta', False) + return self.state['attr_dict'].get('_uses_meta', False) @uses_meta.setter def uses_meta(self, val): - self.state['_uses_meta'] = True + self.state['attr_dict']['_uses_meta'] = val class NodeReference(InstructionsMapper): @@ -827,7 +827,7 @@ def compile_operation(state): @property def distribution(self): """Return the distribution object.""" - distribution = self['distribution'] + distribution = self.state['attr_dict']['distribution'] if isinstance(distribution, str): distribution = scipy_from_str(distribution) return distribution @@ -886,7 +886,7 @@ def __init__(self, distribution, *params, size=None, **kwargs): """ super(Prior, self).__init__(distribution, *params, size=size, **kwargs) - self['_parameter'] = True + self['attr_dict']['_parameter'] = True class Simulator(StochasticMixin, ObservableMixin, NodeReference): diff --git a/elfi/model/graphical_model.py b/elfi/model/graphical_model.py index a96eb659..32451316 100644 --- a/elfi/model/graphical_model.py +++ b/elfi/model/graphical_model.py @@ -38,8 +38,7 @@ def remove_node(self, name): # Remove sole private parents for p in parent_names: - if p[0] == '_' and len(self.source_net.successors(p)) == 0 \ - and len(self.source_net.predecessors(p)) == 0: + if p[0] == '_' and self.source_net.degree(p) == 0: self.remove_node(p) def get_node(self, name): @@ -50,11 +49,11 @@ def get_node(self, name): out : dict """ - return self.source_net.node[name] + return self.source_net.nodes[name] def set_node(self, name, state): """Set the state of the node.""" - self.source_net.node[name] = state + self.source_net.nodes[name] = state def has_node(self, name): """Whether the graph has a node `name`.""" @@ -101,14 +100,14 @@ def update_node(self, node, updating_node): updating_node : str """ - out_edges = self.source_net.out_edges(node, data=True) + out_edges = list(self.source_net.edges(node, data=True)) self.remove_node(node) - self.source_net.add_node(node, self.source_net.node[updating_node]) + self.source_net.add_node(node, attr_dict=self.source_net.nodes[updating_node]['attr_dict']) self.source_net.add_edges_from(out_edges) # Transfer incoming edges for u, v, data in self.source_net.in_edges(updating_node, data=True): - self.source_net.add_edge(u, node, data) + self.source_net.add_edge(u, node, **data) self.remove_node(updating_node) diff --git a/elfi/visualization/visualization.py b/elfi/visualization/visualization.py index bc2d5cd4..bccfd649 100644 --- a/elfi/visualization/visualization.py +++ b/elfi/visualization/visualization.py @@ -48,8 +48,8 @@ def nx_draw(G, internal=False, param_names=False, filename=None, format=None): hidden = set() - for n, state in G.nodes_iter(data=True): - if not internal and n[0] == '_' and state.get('_class') == Constant: + for n, state in G.nodes(data=True): + if not internal and n[0] == '_' and state['attr_dict'].get('_class') == Constant: hidden.add(n) continue _format = {'shape': 'circle', 'fillcolor': 'gray80', 'style': 'solid'} @@ -58,7 +58,7 @@ def nx_draw(G, internal=False, param_names=False, filename=None, format=None): dot.node(n, **_format) # add edges to graph - for u, v, label in G.edges_iter(data='param', default=''): + for u, v, label in G.edges(data='param', default=''): if not internal and u in hidden: continue diff --git a/requirements.txt b/requirements.txt index bedbb0bf..b55caf1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy>=1.12.1 scipy>=0.19 matplotlib>=1.1 GPy>=1.0.9 -networkX>=1.11,<2.0 +networkX>=2.0 ipyparallel>=6 toolz>=0.8 scikit-learn>=0.18.1 \ No newline at end of file diff --git a/tests/functional/test_compilation.py b/tests/functional/test_compilation.py index 1e1e10b3..73537ed5 100644 --- a/tests/functional/test_compilation.py +++ b/tests/functional/test_compilation.py @@ -11,7 +11,7 @@ def test_meta_param(ma2): # Test that it is passed try: # Add to state - sim['_uses_meta'] = True + sim.uses_meta = True sim.generate() assert False, "Should raise an error" except TypeError: @@ -27,12 +27,11 @@ def bi(meta): return meta['batch_index'] # Test the correct batch_index value - m = elfi.ElfiModel() - op = elfi.Operation(bi, model=m, name='op') - op['_uses_meta'] = True + op = elfi.Operation(bi, model=ma2, name='op') + op.uses_meta = True client = elfi.get_client() c = elfi.ComputationContext() - compiled_net = client.compile(m.source_net, m.nodes) + compiled_net = client.compile(ma2.source_net, ma2.nodes) loaded_net = client.load_data(compiled_net, c, batch_index=3) res = client.compute(loaded_net) diff --git a/tests/unit/test_tools.py b/tests/unit/test_tools.py index e386867a..aafe2fd4 100644 --- a/tests/unit/test_tools.py +++ b/tests/unit/test_tools.py @@ -70,7 +70,7 @@ def test_vectorized_and_external_combined(): with pytest.raises(Exception): sim.generate(3) - sim['_uses_meta'] = True + sim.uses_meta = True g = sim.generate(3) # Test uniqueness of seeds From 7174eae9da02b36e46da937b50bd23955f88cefa Mon Sep 17 00:00:00 2001 From: Henri Pesonen Date: Wed, 23 Sep 2020 11:10:57 +0200 Subject: [PATCH 4/9] Modify progressbar (#334) * Initial commit * Modify progress bar functionality to accommodate roundwise ABC-SMC progress reporting * Fix lint --- CHANGELOG.rst | 1 + elfi/methods/parameter_inference.py | 15 +++--- elfi/visualization/visualization.py | 81 ++++++++++++++++++++++++----- 3 files changed, 76 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c22a9982..71e8275f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,7 @@ Changelog ========= +- Modify progress bar to better indicate ABC-SMC inference status - Change networkx support from 1.X to 2.X - Improve docstrings in elfi.methods.bo.acquisition - Fix readthedocs-build by adding .readthedocs.yml and restricting the build to diff --git a/elfi/methods/parameter_inference.py b/elfi/methods/parameter_inference.py index ad67678a..7e1d3d7e 100644 --- a/elfi/methods/parameter_inference.py +++ b/elfi/methods/parameter_inference.py @@ -22,7 +22,7 @@ batch_to_arr2d, ceil_to_batch_size, weighted_var) from elfi.model.elfi_model import ComputationContext, ElfiModel, NodeReference from elfi.utils import is_array -from elfi.visualization.visualization import progress_bar +from elfi.visualization.visualization import ProgressBar logger = logging.getLogger(__name__) @@ -119,6 +119,8 @@ def __init__(self, # inference after an iteration. self.state = dict(n_sim=0, n_batches=0) self.objective = dict() + self.progress_bar = ProgressBar(prefix='Progress', suffix='Complete', + decimals=1, length=50, fill='=') @property def pool(self): @@ -256,18 +258,14 @@ def infer(self, *args, vis=None, bar=True, **kwargs): self.set_objective(*args, **kwargs) - if bar: - progress_bar(0, self._objective_n_batches, prefix='Progress:', - suffix='Complete', length=50) - while not self.finished: self.iterate() if vis: self.plot_state(interactive=True, **vis_opt) if bar: - progress_bar(self.state['n_batches'], self._objective_n_batches, - prefix='Progress:', suffix='Complete', length=50) + self.progress_bar.update_progressbar(self.state['n_batches'], + self._objective_n_batches) self.batches.cancel_pending() if vis: @@ -723,6 +721,9 @@ def prepare_new_batch(self, batch_index): def _init_new_round(self): round = self.state['round'] + reinit_msg = 'ABC-SMC Round {0} / {1}'.format(round + 1, self.objective['round'] + 1) + self.progress_bar.reinit_progressbar(scaling=(self.state['n_batches']), + reinit_msg=reinit_msg) dashes = '-' * 16 logger.info('%s Starting round %d %s' % (dashes, round, dashes)) diff --git a/elfi/visualization/visualization.py b/elfi/visualization/visualization.py index bccfd649..2109b59d 100644 --- a/elfi/visualization/visualization.py +++ b/elfi/visualization/visualization.py @@ -250,15 +250,11 @@ def plot_traces(result, selector=None, axes=None, **kwargs): return axes -def progress_bar(iteration, total, prefix='', suffix='', decimals=1, length=100, fill='█'): - """Progress bar for showing the inference process. +class ProgressBar: + """Progress bar monitoring the inference process. - Parameters + Attributes ---------- - iteration : int, required - Current iteration - total : int, required - Total iterations prefix : str, optional Prefix string suffix : str, optional @@ -269,15 +265,72 @@ def progress_bar(iteration, total, prefix='', suffix='', decimals=1, length=100, Character length of bar fill : str, optional Bar fill character + scaling : int, optional + Integer used to scale current iteration and total iterations of the progress bar """ - if total > 0: - percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) - filled_length = int(length * iteration // total) - bar = fill * filled_length + '-' * (length - filled_length) - print('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end='\r') - if iteration == total: - print() + + def __init__(self, prefix='', suffix='', decimals=1, length=100, fill='='): + """Construct progressbar for monitoring. + + Parameters + ---------- + prefix : str, optional + Prefix string + suffix : str, optional + Suffix string + decimals : int, optional + Positive number of decimals in percent complete + length : int, optional + Character length of bar + fill : str, optional + Bar fill character + + """ + self.prefix = prefix + self.suffix = suffix + self.decimals = 1 + self.length = length + self.fill = fill + self.scaling = 0 + + def update_progressbar(self, iteration, total): + """Print updated progress bar in console. + + Parameters + ---------- + iteration : int + Integer indicating completed iterations + total : int + Integer indicating total number of iterations + + """ + if total - self.scaling > 0: + percent = ("{0:." + str(self.decimals) + "f}").\ + format(100 * ((iteration - self.scaling) / float(total - self.scaling))) + filled_length = int(self.length * (iteration - self.scaling) // (total - self.scaling)) + bar = self.fill * filled_length + '-' * (self.length - filled_length) + print('%s [%s] %s%% %s' % (self.prefix, bar, percent, self.suffix), end='\r') + if iteration == total: + print() + + def reinit_progressbar(self, scaling=0, reinit_msg=None): + """Reinitialize new round of progress bar. + + Parameters + ---------- + scaling : int, optional + Integer used to scale current and total iterations of the progress bar + reinit_msg : str, optional + Message printed before restarting an empty progess bar on a new line + + """ + self.scaling = scaling + if scaling == 0: + print(reinit_msg) + else: + self.update_progressbar(scaling + 1, scaling + 1) + print('\n' + reinit_msg) def plot_params_vs_node(node, n_samples=100, func=None, seed=None, axes=None, **kwargs): From 03c53dba558aff722db2928f954aa39c902eb2a0 Mon Sep 17 00:00:00 2001 From: Henri Pesonen Date: Thu, 8 Oct 2020 10:16:38 +0200 Subject: [PATCH 5/9] Update travis tests to py36 (#336) * Initial commit with updated CHANGELOG.rst * Add python 3.7 to travis setups and remove python 3.6 --- .travis.yml | 6 +++--- CHANGELOG.rst | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c531c385..e713a35e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,10 @@ matrix: include: - os: linux language: python - python: 3.5 + python: 3.6 - os: linux language: python - python: 3.6 + python: 3.7 - os: osx language: generic before_install: @@ -25,6 +25,6 @@ install: - pip install -e . script: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ipcluster start -n 2 --daemon ; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ipcluster start -n 2 --daemonize ; fi #- travis_wait 20 make test - make test diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 71e8275f..1e08c863 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,7 @@ Changelog ========= +- Add python 3.7 to travis tests and remove python 3.5 due to clash with dask - Modify progress bar to better indicate ABC-SMC inference status - Change networkx support from 1.X to 2.X - Improve docstrings in elfi.methods.bo.acquisition From 3276381c4e0460351c4900780eb6681c4403e1ad Mon Sep 17 00:00:00 2001 From: Jan Kokko Date: Thu, 8 Oct 2020 14:40:06 +0300 Subject: [PATCH 6/9] Add dask client (#335) * Add dask client * Update CHANGELOG.rst Co-authored-by: Henri Pesonen --- .gitignore | 5 +- CHANGELOG.rst | 1 + elfi/clients/dask.py | 111 +++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 3 +- tests/conftest.py | 3 +- 5 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 elfi/clients/dask.py diff --git a/.gitignore b/.gitignore index 619ec5c4..2f0a619d 100644 --- a/.gitignore +++ b/.gitignore @@ -107,4 +107,7 @@ ENV/ notebooks/mydask.png # vscode-settings -.vscode \ No newline at end of file +.vscode + +# dask +dask-worker-space diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1e08c863..dd50f5fd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,7 @@ Changelog ========= +- Add dask support to elfi client options - Add python 3.7 to travis tests and remove python 3.5 due to clash with dask - Modify progress bar to better indicate ABC-SMC inference status - Change networkx support from 1.X to 2.X diff --git a/elfi/clients/dask.py b/elfi/clients/dask.py new file mode 100644 index 00000000..2a84303c --- /dev/null +++ b/elfi/clients/dask.py @@ -0,0 +1,111 @@ +"""This module implements a multiprocessing client using dask.""" + +import itertools +import os + +from dask.distributed import Client as DaskClient + +import elfi.client + + +def set_as_default(): + """Set this as the default client.""" + elfi.client.set_client() + elfi.client.set_default_class(Client) + + +class Client(elfi.client.ClientBase): + """A multiprocessing client using dask.""" + + def __init__(self): + """Initialize a dask client.""" + self.dask_client = DaskClient() + self.tasks = {} + self._id_counter = itertools.count() + + def apply(self, kallable, *args, **kwargs): + """Add `kallable(*args, **kwargs)` to the queue of tasks. Returns immediately. + + Parameters + ---------- + kallable: callable + + Returns + ------- + task_id: int + + """ + task_id = self._id_counter.__next__() + async_result = self.dask_client.submit(kallable, *args, **kwargs) + self.tasks[task_id] = async_result + return task_id + + def apply_sync(self, kallable, *args, **kwargs): + """Call and returns the result of `kallable(*args, **kwargs)`. + + Parameters + ---------- + kallable: callable + + """ + return self.dask_client.run_on_scheduler(kallable, *args, **kwargs) + + def get_result(self, task_id): + """Return the result from task identified by `task_id` when it arrives. + + Parameters + ---------- + task_id: int + + Returns + ------- + dict + + """ + async_result = self.tasks.pop(task_id) + return async_result.result() + + def is_ready(self, task_id): + """Return whether task with identifier `task_id` is ready. + + Parameters + ---------- + task_id: int + + Returns + ------- + bool + + """ + return self.tasks[task_id].done() + + def remove_task(self, task_id): + """Remove task with identifier `task_id` from scheduler. + + Parameters + ---------- + task_id: int + + """ + async_result = self.tasks.pop(task_id) + if not async_result.done(): + async_result.cancel() + + def reset(self): + """Stop all worker processes immediately and clear pending tasks.""" + self.dask_client.shutdown() + self.tasks.clear() + + @property + def num_cores(self): + """Return the number of processes. + + Returns + ------- + int + + """ + return os.cpu_count() + + +set_as_default() diff --git a/requirements.txt b/requirements.txt index b55caf1f..601a32d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +dask[distributed]>=1.0.0,<3.0.0 numpy>=1.12.1 scipy>=0.19 matplotlib>=1.1 @@ -5,4 +6,4 @@ GPy>=1.0.9 networkX>=2.0 ipyparallel>=6 toolz>=0.8 -scikit-learn>=0.18.1 \ No newline at end of file +scikit-learn>=0.18.1 diff --git a/tests/conftest.py b/tests/conftest.py index 8482193c..d1b81ecb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,6 +7,7 @@ import scipy.stats as ss import elfi +import elfi.clients.dask as dask import elfi.clients.ipyparallel as eipp import elfi.clients.multiprocessing as mp import elfi.clients.native as native @@ -31,7 +32,7 @@ def pytest_addoption(parser): """Functional fixtures""" -@pytest.fixture(scope="session", params=[eipp, mp, native]) +@pytest.fixture(scope="session", params=[eipp, dask, mp, native]) def client(request): """Provides a fixture for all the different supported clients """ From 5f05076b0de365519b4a46eefd75af704e5bcfb5 Mon Sep 17 00:00:00 2001 From: Henri Pesonen Date: Thu, 8 Oct 2020 14:09:11 +0200 Subject: [PATCH 7/9] Update docs to default py36 (#337) * Initial commit with CHANGELOG.rst * Change python 3.6 as the default version in docs --- CHANGELOG.rst | 1 + CONTRIBUTING.rst | 2 +- README.md | 4 ++-- docs/installation.rst | 10 +++++----- docs/quickstart.rst | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index dd50f5fd..cdac58fe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,7 @@ Changelog ========= +- Update documentation to setting python 3.6 as default - Add dask support to elfi client options - Add python 3.7 to travis tests and remove python 3.5 due to clash with dask - Modify progress bar to better indicate ABC-SMC inference status diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index b48cc59e..faa969c2 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -78,7 +78,7 @@ Ready to contribute? Here's how to set up `ELFI` for local development. environment. You may need to replace "3.5" in the first line with the python version printed in the previous step:: - $ conda create -n elfi python=3.5 numpy + $ conda create -n elfi python=3.6 numpy $ source activate elfi $ cd elfi $ make dev diff --git a/README.md b/README.md index 15eac0bd..f0fe99bf 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ is preferable. Installation ------------ -ELFI requires Python 3.5 or greater. You can install ELFI by typing in your terminal: +ELFI requires Python 3.6 or greater. You can install ELFI by typing in your terminal: ``` pip install elfi @@ -70,7 +70,7 @@ with your default Python environment and can easily use different versions of Py in different projects. You can create a virtual environment for ELFI using anaconda with: ``` -conda create -n elfi python=3.5 numpy +conda create -n elfi python=3.6 numpy source activate elfi pip install elfi ``` diff --git a/docs/installation.rst b/docs/installation.rst index 27b888f0..c891e938 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -3,7 +3,7 @@ Installation ============ -ELFI requires Python 3.5 or greater (see below how to install). To install ELFI, simply +ELFI requires Python 3.6 or greater (see below how to install). To install ELFI, simply type in your terminal: .. code-block:: console @@ -18,16 +18,16 @@ process. .. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ -Installing Python 3.5 +Installing Python 3.6 --------------------- If you are new to Python, perhaps the simplest way to install it is with Anaconda_ that -manages different Python versions. After installing Anaconda, you can create a Python 3.5. +manages different Python versions. After installing Anaconda, you can create a Python 3.6. environment with ELFI: .. code-block:: console - conda create -n elfi python=3.5 numpy + conda create -n elfi python=3.6 numpy source activate elfi pip install elfi @@ -51,7 +51,7 @@ Resolving these may sometimes go wrong: * If you receive an error about missing ``numpy``, please install it first. * If you receive an error about `yaml.load`, install ``pyyaml``. * On OS X with Anaconda virtual environment say `conda install python.app` and then use `pythonw` instead of `python`. -* Note that ELFI requires Python 3.5 or greater +* Note that ELFI requires Python 3.6 or greater * In some environments ``pip`` refers to Python 2.x, and you have to use ``pip3`` to use the Python 3.x version * Make sure your Python installation meets the versions listed in requirements_. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 3a81fa4e..f643f26c 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -3,7 +3,7 @@ Quickstart First ensure you have `installed `__ -Python 3.5 (or greater) and ELFI. After installation you can start using +Python 3.6 (or greater) and ELFI. After installation you can start using ELFI: .. code:: ipython3 From 305d09b823b2763bcd7d113df56a45cb263d4a53 Mon Sep 17 00:00:00 2001 From: Henri Pesonen Date: Mon, 12 Oct 2020 13:45:10 +0200 Subject: [PATCH 8/9] Update docs (#338) * Initial commit * Change python 3.5 references to 3.6 --- CHANGELOG.rst | 1 + CONTRIBUTING.rst | 4 ++-- setup.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cdac58fe..22dbbacc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,7 @@ Changelog ========= +- Update info to reflect setting python 3.6 as the default version - Update documentation to setting python 3.6 as default - Add dask support to elfi client options - Add python 3.7 to travis tests and remove python 3.5 due to clash with dask diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index faa969c2..61735334 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -75,7 +75,7 @@ Ready to contribute? Here's how to set up `ELFI` for local development. $ python -V 4. Install your local copy and the development requirements into a conda - environment. You may need to replace "3.5" in the first line with the python + environment. You may need to replace "3.6" in the first line with the python version printed in the previous step:: $ conda create -n elfi python=3.6 numpy @@ -127,7 +127,7 @@ Before you submit a pull request, check that it meets these guidelines: 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. -3. The pull request should work for Python 3.5 and later. Check +3. The pull request should work for Python 3.6 and later. Check https://travis-ci.org/elfi-dev/elfi/pull_requests and make sure that the tests pass for all supported Python versions. diff --git a/setup.py b/setup.py index 5c983047..22825b64 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ long_description=(open('docs/description.rst').read()), license='BSD', classifiers=[ - 'Programming Language :: Python :: 3.5', 'Topic :: Scientific/Engineering', + 'Programming Language :: Python :: 3.6', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Artificial Intelligence', 'Topic :: Scientific/Engineering :: Bio-Informatics', 'Topic :: Scientific/Engineering :: Mathematics', 'Operating System :: OS Independent', From 9ae457fe4fddd93d5777d02380a934ebde774b0c Mon Sep 17 00:00:00 2001 From: Henri Pesonen Date: Mon, 12 Oct 2020 15:07:31 +0200 Subject: [PATCH 9/9] Bump version (#339) * Initial commit with CHANGELOG.rst * Modify relevant files for new release * Modify post-processing dosctring * Fix postprocessing multiple-line docstring example --- CHANGELOG.rst | 3 +++ README.md | 2 +- elfi/__init__.py | 2 +- elfi/clients/dask.py | 4 ++-- elfi/methods/post_processing.py | 4 ++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 22dbbacc..0bc4ea54 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,9 @@ Changelog ========= + +0.7.7 (2020-10-12) +------------------ - Update info to reflect setting python 3.6 as the default version - Update documentation to setting python 3.6 as default - Add dask support to elfi client options diff --git a/README.md b/README.md index f0fe99bf..1f5c3848 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -**Version 0.7.6 released!** See the [CHANGELOG](CHANGELOG.rst) and [notebooks](https://github.com/elfi-dev/notebooks). +**Version 0.7.7 released!** See the [CHANGELOG](CHANGELOG.rst) and [notebooks](https://github.com/elfi-dev/notebooks). **NOTE:** For the time being NetworkX 2 is incompatible with ELFI. diff --git a/elfi/__init__.py b/elfi/__init__.py index 6a5c7a15..7f854c2d 100644 --- a/elfi/__init__.py +++ b/elfi/__init__.py @@ -26,4 +26,4 @@ __email__ = 'elfi-support@hiit.fi' # make sure __version_ is on the last non-empty line (read by setup.py) -__version__ = '0.7.6' +__version__ = '0.7.7' diff --git a/elfi/clients/dask.py b/elfi/clients/dask.py index 2a84303c..134d6a13 100644 --- a/elfi/clients/dask.py +++ b/elfi/clients/dask.py @@ -56,11 +56,11 @@ def get_result(self, task_id): Parameters ---------- task_id: int - + Returns ------- dict - + """ async_result = self.tasks.pop(task_id) return async_result.result() diff --git a/elfi/methods/post_processing.py b/elfi/methods/post_processing.py index b1efb741..b4c9a39c 100644 --- a/elfi/methods/post_processing.py +++ b/elfi/methods/post_processing.py @@ -242,8 +242,8 @@ def adjust_posterior(sample, model, summary_names, parameter_names=None, adjustm >>> import elfi >>> from elfi.examples import gauss >>> m = gauss.get_model() - >>> res = elfi.Rejection(m['d'], output_names=['ss_mean', 'ss_var'],\ - batch_size=10).sample(500, bar=False) + >>> res = elfi.Rejection(m['d'], output_names=['ss_mean', 'ss_var'], + ... batch_size=10).sample(500, bar=False) >>> adj = adjust_posterior(res, m, ['ss_mean', 'ss_var'], ['mu'], LinearAdjustment()) """