def solve_binary_pick_k(self, show_spent_time: bool = True) -> list:
     """
     借助Binary Search的思想,每次從post_list中N篇文章中找出相對排序最前面1個,重複k次
     時間複雜度 O(k log N)
     空間複雜度 O(N)
     Returns:
         [list]: Top-K articles
     """
     if show_spent_time:
         start = time.time()
     top_k_posts_list = []
     n_picks = 0
     post_set = IndexedSet(self.post_list)
     # 一個重複k次的迴圈,時間複雜度 O(k)
     while n_picks < self.top_k:
         # binary pick,採Divide and Conquer,時間複雜度 O(log N)
         curr_best_post = binary_pick(
             self.ranker,
             post_set, left=0,
             right=len(post_set) - 1)
         top_k_posts_list.append(curr_best_post)  # 加入元素到list 時間複雜度 O(1)
         # 從IndexableSet移除元素,時間複雜度O(1) (HashTable)
         post_set.remove(curr_best_post)
         n_picks += 1
     if show_spent_time:
         spent = (time.time() - start) * 1000
         print(self)
         print(f'spent time {spent:.2f} ms')
     return top_k_posts_list
Example #2
0
class GNodeSocket(QGraphicsWidget):
    edge_started = pyqtSignal(GEdge)
    edge_released = pyqtSignal(GEdge)
    connection_changed = pyqtSignal(object, object)  # Socket, Edge or None
    position_changed = pyqtSignal(QPointF)

    INPUT = SocketType.INPUT
    OUTPUT = SocketType.OUTPUT

    def __init__(self, parent_node: 'GShaderNode', socket: NodeSocket):
        super().__init__(parent=parent_node)
        self._socket = socket
        self._socket.set_container(self)
        self._parent_g_node = parent_node
        self._connected_g_edges = IndexedSet()

        # Define socket properties
        self._circle_connected_brush = QColor(
            255, 130, 0,
            255) if self._socket.type() == NodeSocket.INPUT else QColor(
                130, 255, 0, 255)
        self._circle_disconnected_brush = QColor(
            102, 50, 0,
            255) if self._socket.type() == NodeSocket.INPUT else QColor(
                50, 102, 0, 255)
        self._circle_hover_brush = QColor(
            170, 130, 0,
            255) if self._socket.type() == NodeSocket.INPUT else QColor(
                130, 170, 0, 255)
        self._border_connected_brush = QPen(QColor(255, 255, 255, 255))
        self._border_disconnected_brush = QPen(Qt.black)
        self._circle_brush = self._circle_disconnected_brush
        self._border_brush = self._border_disconnected_brush
        self._bbox = QRectF(0, 0, 10, 10)
        self._moving_edge = False
        self._current_edge = None
        self._layout = QGraphicsLinearLayout(Qt.Horizontal)

        self._init_socket()

    def _init_socket(self):
        self.setLayout(self._layout)
        self.setFlag(QGraphicsItem.ItemIsMovable, False)
        self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True)
        self.setAcceptHoverEvents(True)

    def type(self) -> SocketType:
        return self._socket.type()

    def value(self):
        return self._socket.value()

    def set_value(self, value: typing.Any):
        self._socket.set_value(value)

    def save_value(self):
        """Saves the value of this socket internally. This value can be reassigned by calling 'restore_value()'."""

        self._socket.save_value()

    def restore_value(self):
        """Restores the value of this socket to the last saved value."""

        self._socket.restore_value()

    def parent_node(self) -> 'GShaderNode':
        return self._parent_g_node

    def get_size(self):
        """Returns a tuple with the width,height of this socket."""
        return self._bbox.right(), self._bbox.bottom()

    def get_backend_socket(self) -> NodeSocket:
        return self._socket

    def get_connected_nodes(self) -> typing.List['GShaderNode']:
        return [n.get_container() for n in self._socket.get_connected_nodes()]

    def get_connected_sockets(self) -> typing.List['GNodeSocket']:
        return [
            s.get_container() for s in self._socket.get_connected_sockets()
        ]

    def set_index(self, index: int):
        self._socket.set_index(index)

    def get_index(self) -> int:
        return self._socket.get_index()

    def is_connected(self) -> bool:
        return self._socket.is_connected()

    def boundingRect(self):
        return self._bbox

    def paint(self, painter: QPainter, option, widget=None):
        painter.setPen(self._border_brush)
        painter.setBrush(self._circle_brush)
        painter.drawEllipse(self._bbox)

    def get_scene_pos(self) -> QPointF:
        """Gets the center position of this socket in scene coordinates."""
        pos = self.scenePos()
        pos.setX(pos.x() + self._bbox.right() / 2)
        pos.setY(pos.y() + self._bbox.bottom() / 2)
        return pos

    def connect_to(self, socket: 'GNodeSocket') -> GEdge:
        """
        Connects this GNodeSocket to another GNodeSocket.
        :param other_socket: Other GNodeSocket to connect this socket to.
        :return: the GEdge that was created between the sockets, or the old GEdge if there already exists a connection.
        """
        edge = self._socket.connect_to(socket.get_backend_socket())

        # Only emit change event for input sockets, as nothing really changed for the output socket (at least not for the node as a whole)
        if self.type() == SocketType.INPUT:
            self.connection_changed.emit(self, edge)
        elif socket.type() == SocketType.INPUT:
            socket.connection_changed.emit(socket, edge)

        return GEdge.from_edge(edge)

    def get_connected_edges(self) -> IndexedSet:
        return self._connected_g_edges

    def add_connecting_edge(self, edge: GEdge):
        self._connected_g_edges.add(edge)

    def remove_connected_edge(self, gedge: GEdge):
        assert gedge in self._connected_g_edges

        self._connected_g_edges.remove(gedge)

    def label(self) -> str:
        return self._socket.label()

    def __eq__(self, other):
        if isinstance(other, GNodeSocket):
            return self._socket.__eq__(other.get_backend_socket())

        return False

    def __hash__(self):
        return self._socket.__hash__()

    def __str__(self):
        return self._socket.__str__()

    # -------- Event Handling ---------
    # ----------------------------------
    def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
        # self._circle_brush = self._circle_hover_brush
        self._border_brush = self._border_connected_brush
        self.update()
        event.accept()

    def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
        # self._circle_brush = self._circle_connected_brush if self.connected else self._circle_disconnected_brush
        if not self.is_connected():
            self._border_brush = self._border_disconnected_brush
            self.update()

        event.accept()

    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        # Needs to be reimplemented to be able to catch mouseMoveEvent, but it does not need to do anything
        event.accept()

    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):
        if event.buttons() == Qt.LeftButton:
            if not self._moving_edge:
                if self.type() == self.INPUT:
                    edge = GEdge(destination=self)
                else:
                    edge = GEdge(source=self)
                self._current_edge = edge
                self._moving_edge = True
                self.edge_started.emit(edge)
            else:
                self._current_edge.set_tip_pos(event.scenePos())

            event.accept()
        else:
            event.ignore()

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent):
        if self._current_edge:  # Prevent release without dragging
            self.edge_released.emit(self._current_edge)
            self._current_edge = None
            self._moving_edge = False

    def itemChange(self, change, value):
        if change == self.ItemScenePositionHasChanged:

            for edge in self._connected_g_edges:
                edge.update_edge()

        return super().itemChange(change, value)
Example #3
0
class NodeSocket(GraphElement):
    INPUT: SocketType = SocketType.INPUT
    OUTPUT: SocketType = SocketType.OUTPUT

    def __init__(self, parent_node: 'Node', socket_type: SocketType, dtype: DataType = None, label: str = "", container=None):
        super().__init__(container)
        assert isinstance(socket_type, SocketType), "socket_type must be of enum type SocketType!"
        self._parent_g_node = parent_node
        self._type = socket_type
        self._dtype = dtype
        self._label = label
        self._id = uuid.uuid4()
        self._index = -1
        self._value = None
        self._saved_value = None
        self._connected = False
        self._connected_sockets = IndexedSet()
        self._connected_edges = IndexedSet()

    def connect_to(self, other_socket: 'NodeSocket') -> Edge:
        """
        Connects this NodeSocket to another NodeSocket.
        :param other_socket: Other NodeSocket to connect this socket to.
        :return: the Edge that was created between the sockets, or the old Edge if there already exists a connection.
        """
        assert self.id() != other_socket.id(), "Can not connect socket to itself!"
        assert self.type() != other_socket.type(), "Can not connect sockets of the same type!"

        # Check if this socket is already connected to 'socket'
        if other_socket in self._connected_sockets:
            edge = self.find_connecting_edge(other_socket)
            return edge

        # Create a directed edge
        if self.type() == NodeSocket.OUTPUT:
            edge = Edge(source=self, destination=other_socket)
        else:
            edge = Edge(source=other_socket, destination=self)

        self._connected_edges.add(edge)
        other_socket._connected_edges.add(edge)
        self._connected_sockets.add(other_socket)
        other_socket._connected_sockets.add(self)
        self._connected = True
        other_socket._connected = True

        return edge

    def disconnect_all(self):
        """Disconnects this NodeSocket from all other connected sockets."""
        for other in self._connected_sockets:
            self.disconnect_from(other)

    def disconnect_from(self, other_socket: 'NodeSocket'):
        """
        Disconnects this NodeSocket from another NodeSocket.
        :param other_socket: Another NodeSocket to disconnect from.
        """
        assert other_socket in self._connected_sockets, "Can not disconnect from a socket that is not connected!"

        edge = self.find_connecting_edge(other_socket)

        if edge is not None:
            self._connected_sockets.remove(other_socket)
            other_socket._connected_sockets.remove(self)

            self._connected_edges.remove(edge)
            other_socket._connected_edges.remove(edge)
            del edge

            self._connected = False
            other_socket._connected = False
        else:
            raise RuntimeError("Sockets are indicated as connected but could not find their connecting Edge. The Node Graph is corrupt!")

    def get_parent_node(self) -> 'Node':
        return self._parent_g_node

    def get_connected_nodes(self) -> typing.List['Node']:
        """
        Returns each node that is connected to this NodeSocket.
        :return: A list of Nodes connected to this NodeSocket.
        """

        nodes = []
        for socket in self._connected_sockets:
            nodes.append(socket.get_parent_node())

        return nodes

    def get_connected_sockets(self) -> list:
        """
        Returns each NodeSocket that this NodeSocket is connected to.
        :return: A list of NodeSockets connected to this NodeSocket.
        """

        return list(self._connected_sockets)

    def set_index(self, index):
        """Set the index of this socket. This value is used to figure out what input/output this socket corresponds to."""
        self._index = index

    def get_index(self) -> int:
        """Gets the index of this socket."""
        return self._index

    def set_value(self, value: typing.Any):
        self._value = value

    def save_value(self):
        """Saves the value of this socket internally. This value can be reassigned by calling 'restore_value()'."""

        self._saved_value = self._value.detach().clone()

    def restore_value(self):
        """Restores the value of this socket to the last saved value."""

        self._value = self._saved_value.detach().clone()

    def value(self) -> typing.Any:
        return self._value

    def label(self) -> str:
        return self._label

    def is_connected(self) -> bool:
        """Returns a boolean indicating whether this socket is connected to any other sockets with an Edge."""
        return self._connected

    def type(self) -> SocketType:
        """Returns the type of this NodeSocket indicating whether it accepts input or returns output."""
        return self._type

    def dtype(self) -> DataType:
        return self._dtype

    def find_connecting_edge(self, other_socket: 'NodeSocket') -> typing.Union[None, Edge]:
        for edge in self._connected_edges:
            if edge.connects(self, other_socket):
                return edge

        return None