Repository: nipy/nipype

Issue:
workflow with iterables and cloned nodes fail when expanding iterables
### Summary
When running a workflow which includes a cloned node and iterables the workflow will fail when expanding the iterables because the id of the cloned node will be the same as the original one.

### Actual behavior
Will result in an error:
Traceback (most recent call last):

  File "<ipython-input-55-177d6eaeef2c>", line 27, in <module>
    workflow.run()

  File "/data/eaxfjord/anaconda2/lib/python2.7/site-packages/nipype/pipeline/engine/workflows.py", line 592, in run
    execgraph = generate_expanded_graph(deepcopy(flatgraph))

  File "/data/eaxfjord/anaconda2/lib/python2.7/site-packages/nipype/pipeline/engine/utils.py", line 1042, in generate_expanded_graph
    iterable_prefix, inode.synchronize)

  File "/data/eaxfjord/anaconda2/lib/python2.7/site-packages/nipype/pipeline/engine/utils.py", line 733, in _merge_graphs
    raise Exception(("Execution graph does not have a unique set of node "

Exception: Execution graph does not have a unique set of node names. Please rerun the workflow

### Expected behavior
Will execute normally without the errors.

### How to replicate the behavior
The following workflow will produce the error.

### Script/Workflow details
```python
from nipype.interfaces import utility as niu
from nipype.pipeline import engine as pe

def addstr(string):
    string = ('%s+2' % string )
    return string

subject_list = ['sub-001', 'sub-002']

inputnode = pe.Node(niu.IdentityInterface(fields = ['subject']),
                        name = 'inputnode')
inputnode.iterables = [('subject', subject_list)]

node_1 = pe.Node(niu.Function(input_names='string',
                              output_names= 'string',
                              function = addstr),name='node_1')
node_2 = node_1.clone('node_2')

workflow = pe.Workflow(name='my_workflow')
workflow.connect([(inputnode, node_1, [('subject','string')]),
                  (node_1, node_2, [('string','string')])])
workflow.run()
```
### Platform details:
/data/eaxfjord/anaconda2/lib/python2.7/site-packages/h5py/__init__.py:34: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
  from ._conv import register_converters as _register_converters
{'nibabel_version': '2.2.1', 'sys_executable': '/data/eaxfjord/anaconda2/bin/python', 'networkx_version': '2.1', 'numpy_version': '1.14.0', 'sys_platform': 'linux2', 'sys_version': '2.7.13 |Anaconda custom (64-bit)| (default, Dec 20 2016, 23:09:15) \n[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]', 'commit_source': 'installation', 'commit_hash': '0a5948a0', 'pkg_path': '/data/eaxfjord/anaconda2/lib/python2.7/site-packages/nipype', 'nipype_version': '1.0.0', 'traits_version': '4.6.0', 'scipy_version': '1.0.0'}
1.0.0


### Execution environment
- My python environment outside container


Discussion:

Patch:
diff --git a/nipype/pipeline/engine/base.py b/nipype/pipeline/engine/base.py
index 51449632b..0883023f6 100644
--- a/nipype/pipeline/engine/base.py
+++ b/nipype/pipeline/engine/base.py
@@ -84,9 +84,12 @@ class EngineBase(object):
             A clone of node or workflow must have a new name
         """
         if name == self.name:
-            raise ValueError('Cloning requires a new name, "%s" is in use.' % name)
+            raise ValueError('Cloning requires a new name, "%s" is '
+                             'in use.' % name)
         clone = deepcopy(self)
         clone.name = name
+        if hasattr(clone, '_id'):
+            clone._id = name
         return clone
 
     def _check_outputs(self, parameter):


Test patch:
diff --git a/nipype/pipeline/engine/tests/test_base.py b/nipype/pipeline/engine/tests/test_base.py
index 54356fd6c..fd87aa687 100644
--- a/nipype/pipeline/engine/tests/test_base.py
+++ b/nipype/pipeline/engine/tests/test_base.py
@@ -6,6 +6,8 @@ from __future__ import print_function, unicode_literals
 import pytest
 from ..base import EngineBase
 from ....interfaces import base as nib
+from ....interfaces import utility as niu
+from ... import engine as pe
 
 
 class InputSpec(nib.TraitedSpec):
@@ -64,3 +66,24 @@ def test_clone():
 
     with pytest.raises(ValueError):
         base.clone('nodename')
+
+def test_clone_node_iterables(tmpdir):
+    tmpdir.chdir()
+
+    def addstr(string):
+        return ('%s + 2' % string)
+
+    subject_list = ['sub-001', 'sub-002']
+    inputnode = pe.Node(niu.IdentityInterface(fields=['subject']),
+                            name='inputnode')
+    inputnode.iterables = [('subject', subject_list)]
+
+    node_1 = pe.Node(niu.Function(input_names='string',
+                                  output_names='string',
+                                  function=addstr), name='node_1')
+    node_2 = node_1.clone('node_2')
+
+    workflow = pe.Workflow(name='iter_clone_wf')
+    workflow.connect([(inputnode, node_1, [('subject', 'string')]),
+                      (node_1, node_2, [('string', 'string')])])
+    workflow.run()