def execute(self, time_interval):
        """
        Execute the factor over the given time interval. Note that this is normally done by the workflow,
        but can also be done on the factor directly

        :param time_interval: The time interval
        :return: self (for chaining)
        """
        logging.info('{} running from {} to {}'.format(
            self.tool.__class__.__name__, time_interval.start, time_interval.end))

        input_plate_values = self.input_plate.values if self.input_plate else [None]
        output_plate_values = self.sink.plate_values
        meta_data_ids = [p.meta_data_id for p in self.sink.plates]

        def belongs(plate_value):
            return all(ii in plate_value for ii in input_plate_value)

        def meta_data_matches(plate_value):
            return filter(lambda x: x[0] in meta_data_ids, plate_value)

        for input_plate_value in input_plate_values:
            if input_plate_value:
                # Select only the valid output plate values based on the input plate value
                # filtered = filter(belongs, output_plate_values)
                filtered = filter(belongs, output_plate_values)
            else:
                filtered = output_plate_values

            sinks = [self.sink.streams[s] for s in filtered]

            sub_plate_values_only = map(meta_data_matches, filtered)

            if not self.source:
                source = None
            elif input_plate_value in self.source.streams:
                source = self.source.streams[input_plate_value]
            else:
                logging.warn("{} with value {} not valid for source {}".format(
                    self.input_plate, input_plate_value, self.source))
                continue

            if self.input_plate:
                if len(self.output_plates) == 1:
                    if self.output_plates[0].parent.plate_id != self.input_plate.plate_id:
                        raise IncompatiblePlatesError("Parent plate of output plate does not match input plate")
            else:
                if len(self.output_plates) != 1:
                    raise ValueError("Should be a single output plate if there is no input plate")

            if len(self.output_plates) > 1:
                raise NotImplementedError

            splitting_stream = self.get_splitting_stream(input_plate_value)

            self.tool.execute(
                source=source,
                sinks=sinks,
                interval=time_interval,
                splitting_stream=splitting_stream,
                meta_data_id=self.output_plates[0].meta_data_id,
                output_plate_values=sub_plate_values_only)
            self.update_computed_intervals(sinks, time_interval)

        return self