<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:

def merge_units(self, unit_ids):
        '''This function merges two roots from the curation tree according to the given unit_ids. It creates a new unit_id and root
        that has the merged roots as children.

        Parameters
        ----------
        unit_ids: list
            The unit ids to be merged
        '''
        root_ids = []
        for i in range(len(self._roots)):
            root_id = self._roots[i].unit_id
            root_ids.append(root_id)

        indices_to_be_deleted = []
        if(set(unit_ids).issubset(set(root_ids)) and len(unit_ids) > 1):
            #Find all unique feature names and create all feature lists
            all_feature_names = []
            for unit_id in unit_ids:
                feature_names = self.get_unit_spike_feature_names(unit_id)
                all_feature_names.append(feature_names)

            shared_feature_names = set(all_feature_names[0])
            for feature_names in all_feature_names[1:]:
                shared_feature_names.intersection_update(feature_names)
            shared_feature_names = list(shared_feature_names)
            shared_features = []
            for i in range(len(shared_feature_names)):
                shared_features.append([])

            new_root_id = max(self._all_ids)+1
            self._all_ids.append(new_root_id)
            new_root = Unit(new_root_id)
            all_spike_trains = []
            for unit_id in unit_ids:
                root_index = root_ids.index(unit_id)
                new_root.add_child(self._roots[root_index])
                all_spike_trains.append(self._roots[root_index].get_spike_train())
                for i, feature_name in enumerate(shared_feature_names):
                    features = self.get_unit_spike_features(unit_id, feature_name)
                    shared_features[i].append(features)
                del self._unit_features[unit_id]
                self._roots[root_index].set_spike_train(np.asarray([])) #clear spiketrain
                indices_to_be_deleted.append(root_index)

            all_spike_trains = np.concatenate(all_spike_trains)
            sort_indices = np.argsort(all_spike_trains)
            new_root.set_spike_train(np.asarray(all_spike_trains)[sort_indices])
            del all_spike_trains
            self._roots = [self._roots[i] for i,_ in enumerate(root_ids) if i not in indices_to_be_deleted]
            self._roots.append(new_root)
            for i, feature_name in enumerate(shared_feature_names):
                self.set_unit_spike_features(new_root_id, feature_name, np.concatenate(shared_features[i])[sort_indices])
        else:
            raise ValueError(str(unit_ids) + " has one or more invalid unit ids")