<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def allocate_stream(self, stream_type, stream_id=None, previous=None, attach=False):
 """Allocate a new stream of the given type. The stream is allocated with an incremental ID starting at StreamAllocator.StartingID. The returned data stream can always be used to to attach a NodeInput to this stream, however the attach_stream() function should always be called first since this stream's output may need to be split and a logically equivalent stream used instead to satisfy a device specific constraint on the maximum number of outputs attached to a given stream. You can call allocate_stream on the same stream multiple times without issue. Subsequent calls to allocate_stream are noops. Args: stream_type (int):
 A stream type specified in the DataStream class like DataStream.ConstantType stream_id (int):
 The ID we would like to use for this stream, if this is not specified, an ID is automatically allocated. previous (DataStream):
 If this stream was automatically derived from another stream, this parameter should be a link to the old stream. attach (bool):
 Call attach_stream immediately before returning. Convenience routine for streams that should immediately be attached to something. Returns: DataStream: The allocated data stream. """


        if stream_type not in DataStream.TypeToString:
            raise ArgumentError("Unknown stream type in allocate_stream", stream_type=stream_type)

        if stream_id is not None and stream_id >= StreamAllocator.StartingID:
            raise ArgumentError("Attempted to explicitly allocate a stream id in the internally managed id range", stream_id=stream_id, started_id=StreamAllocator.StartingID)

        # If the stream id is not explicitly given, we need to manage and track it
        # from our autoallocate range
        if stream_id is None:
            if stream_type not in self._next_id:
                self._next_id[stream_type] = StreamAllocator.StartingID

            stream_id = self._next_id[stream_type]
            self._next_id[stream_type] += 1

        # Keep track of how many downstream nodes are attached to this stream so
        # that we know when we need to split it into two.
        stream = DataStream(stream_type, stream_id)

        if stream not in self._allocated_streams:
            self._allocated_streams[stream] = (stream, 0, previous)

        if attach:
            stream = self.attach_stream(stream)

        return stream