From d23b5edbf1c1669612c62e3c94085a9bc86dd078 Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 15 Sep 2025 21:30:31 +0530 Subject: [PATCH 01/24] player 4 logic --- players/player_4/player.py | 221 +++++++++++++++++++++++++------------ 1 file changed, 151 insertions(+), 70 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index c3a67a7..f154bdd 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -1,77 +1,158 @@ -import openai +from collections import Counter -from models.player import Item, Player, PlayerSnapshot - -openai_api_key_player_4: str = 'sk-REDACTED' +from models.player import GameContext, Item, Player, PlayerSnapshot class Player4(Player): - def __init__(self, snapshot: PlayerSnapshot, conversation_length: int) -> None: # noqa: F821 - super().__init__(snapshot, conversation_length) - - self.client = openai.OpenAI(api_key=openai_api_key_player_4) - - def propose_item(self, history: list[Item]) -> Item | None: - prompt = self.create_prompt( - self.preferences, self.memory_bank, self.conversation_length, history - ) - print('Sending prompt!') - response = self.client.responses.create( - model='gpt-4.1', - input=prompt, - max_output_tokens=16, - ) - print(f'Received response: {response.output_text}') - if response.output_text == 'None': + def __init__(self, snapshot: PlayerSnapshot, ctx: GameContext) -> None: # noqa: F821 + super().__init__(snapshot, ctx) + + @staticmethod + def _is_pause(x: Item | None) -> bool: + return x is None + + @staticmethod + def _subjects_in(items: list[Item]) -> Counter[int]: + c = Counter() + for it in items: + c.update(it.subjects) + return c + + @staticmethod + def _take_preceding_block(history: list[Item | None], k: int) -> list[Item]: + """Take up to k preceding *non-pause* items, stopping if a pause is hit.""" + out: list[Item] = [] + for x in reversed(history): + if x is None: + break + out.append(x) + if len(out) == k: + break + out.reverse() + return out + + @staticmethod + def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: + """ + When history ends with a pause, take up to k items *before that pause*, + not crossing an earlier pause. + """ + if not history or history[-1] is not None: + return [] + # walk left of the trailing pause + out: list[Item] = [] + count = 0 + for x in reversed(history[:-1]): + if x is None: + break + out.append(x) + count += 1 + if count == k: + break + out.reverse() + return out + + + def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: + """ + Lower is better. Uses: + 1) best (lowest) index among the item's subjects in self.preferences + 2) sum of indices (to prefer items whose subjects are overall higher-ranked) + 3) id string for deterministic ordering + Subjects not found in preferences rank after all known preferences. + """ + n = len(self.preferences) + idxs = [ + (self.preferences.index(s) if s in self.preferences else n) + for s in item.subjects + ] + best = min(idxs) if idxs else n + total = sum(idxs) if idxs else n * 2 + return (best, total, str(item.id)) + + def _score_candidate(self, item: Item, history: list[Item | None]) -> float: + score = 0.0 + + # Repetition check: same item already in history? + already_seen = any(h is not None and h.id == item.id for h in history) + if already_seen: + # repeated items lose one point and contribute zero coherence/importance + score -= 1.0 + return score # early return: zero importance & coherence after first instance + + # Importance (only if not repeated) + score += float(item.importance) + + # Penalty: subject appeared in each of the previous 3 items? + prev3 = self._take_preceding_block(history, 3) + if len(prev3) == 3: + sets = [set(it.subjects) for it in prev3] + common_prev3 = sets[0].intersection(sets[1]).intersection(sets[2]) + if any(s in common_prev3 for s in item.subjects): + score -= 1.0 + + # Pause bonus: if most recent is a pause, and item has a subject not seen + # in the 5 turns prior to the pause (not crossing an earlier pause) -> +1 + if history and history[-1] is None: + window5 = self._take_window_before_pause(history, 5) + seen = set() + for it in window5: + seen.update(it.subjects) + + # Count how many candidate subjects are unseen in the last 5 turns + unseen_count = sum(1 for s in item.subjects if s not in seen) + + if unseen_count >= 2: + score += 2.0 # two or more unseen subjects → +2 + elif unseen_count >= 1: + score += 1.0 # exactly one unseen subject → +1 + + # Coherence relative to up to 3 preceding (no following at the end) + # Window cannot extend across a pause. + else: + context_items = self._take_preceding_block(history, 3) + subj_counts = self._subjects_in(context_items) + + if item.subjects: + # If any subject of I is never mentioned in CI -> -1 + if any(subj_counts.get(s, 0) == 0 for s in item.subjects): + score -= 1.0 + # If all subjects in I are mentioned at least twice in CI -> +1 + elif all(subj_counts.get(s, 0) >= 2 for s in item.subjects): + score += 1.0 + + return score + + # ------------ selection + + def propose_item(self, history: list[Item | None]) -> Item | None: + """ + Pick the memory_bank item with the maximum score under the rules. + Tie-breaker: player preference order. + Returns None if no items available. + """ + if not self.memory_bank: return None - try: - index = int(response.output_text) - if 0 <= index < len(self.memory_bank): - return self.memory_bank[index] - else: - return None - except ValueError: + + # Score all candidates + scored: list[tuple[float, Item]] = [ + (self._score_candidate(it, history), it) for it in self.memory_bank + ] + + # Find best score + if not scored: return None + best_score = max(s for s, _ in scored) + + # All with best score + tied = [it for s, it in scored if s == best_score] + + if len(tied) == 1: + choice = tied[0] + else: + # Tie-break by preferences + choice = min(tied, key=self._preference_tiebreak_key) - def create_prompt( - self, - preferences: list[int], - memory_bank: list[Item], - conversation_length: int, - history: list[Item], - ) -> str: - prompt = f""" - There are some players, and every player has a random memory bank of newsworthy items that they could contribute to the conversation. Every item has an importance. The conversation has a fixed length L, {conversation_length}. - - On each turn, each player may propose to share an item. If multiple players attempt to contribute an item: If the player currently talking wants to contribute again, they will be chosen with probability 0.5. If the player currently talking is not chosen, then a player at random from among those who propose an item and have so far contributed the smallest number of items among that group will be selected. If nobody contributes anything, then the sequence is filled by a "pause". If there are three consecutive pauses, then the conversation ends prematurely. - - The shared goals of a conversation are as follows: - - Coherence - For every item I, the (up to) 3 preceding items and (up to) 3 following items are collected into a set CI of context items. If a subject of I is never mentioned in CI then one point is lost from the final score. If all subjects in I are mentioned at least twice in CI then one point is added to the final score. The window defining CI does not extend beyond the start of the conversation or any pauses. - Importance - The total importance of all items in the final item sequence is added to the shared score. (Everybody agrees about the importance of each item.) - Nonrepetition - An item that is repeated has zero importance and does not contribute to the coherence score after the first instance of the item. - Freshness - Immediately after a pause, an item with a subject that was not previously present in the 5 turns prior to the pause gets a point added to the final score. An item with two novel subjects of this sort gets two bonus points. Repeated items do not get freshness points. - Nonmonotonousness - An item with a subject that was also present in each of the previous three items leads to the loss of a point. Also, repeated items lose one point, since the audience has already heard that news. - Individual goals depend on each player's preferred topics of conversation. The simulator gives each player a random permutation of the S subjects that represents the ranked preference of that player. An item with a subject that is of rank k for that player achieves a bonus of 1-k/|S| where |S| is the total number of possible subjects. An item with two subjects gives the player a bonus corresponing to the average of the two individual bonuses. Players know their own ranking, but not the ranking of other players. - - At the end of the game, the total score for each player is divided by L, resulting in an overall conversation quality that is the final metric by which players will be compared. - - You are a player in this game. Your goal is to select an item from your memory bank, or return "None" to pass, in order to achieve the highest conversation quality. - - The conversation so far is as follows: - {len(history)} items have been proposed in total: {', '.join('[' + 'subjects: ' + str(item.subjects) + ', importance: ' + str(item.importance) + ']' for item in history)}. - - Your memory bank contains the following items: - {', '.join('[' + 'id: ' + str(item.id) + ', subjects: ' + str(item.subjects) + ', importance: ' + str(item.importance) + ']' for item in memory_bank)}. - - Your preferences for subjects are as follows: - {', '.join('subject: ' + str(subject) + ', rank: ' + str(rank) for rank, subject in enumerate(preferences))} - - Based on the conversation so far, your memory bank, and your subject preferences, which item will you propose to contribute next? If you do not want to contribute an item, respond with "None". Otherwise, respond with the index of the item you wish to contribute, in your memory bank. Do not return anything other than either "None" or an integer index. - """ - return prompt + # Track contribution if you care downstream + self.contributed_items.append(choice) + return choice From 7348a3829675ca2c9d02b57fc0d5188724663627 Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 15 Sep 2025 21:36:46 +0530 Subject: [PATCH 02/24] ruff fix --- players/player_4/player.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index f154bdd..e975e14 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -52,7 +52,6 @@ def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: out.reverse() return out - def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: """ Lower is better. Uses: @@ -62,14 +61,11 @@ def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: Subjects not found in preferences rank after all known preferences. """ n = len(self.preferences) - idxs = [ - (self.preferences.index(s) if s in self.preferences else n) - for s in item.subjects - ] + idxs = [(self.preferences.index(s) if s in self.preferences else n) for s in item.subjects] best = min(idxs) if idxs else n total = sum(idxs) if idxs else n * 2 return (best, total, str(item.id)) - + def _score_candidate(self, item: Item, history: list[Item | None]) -> float: score = 0.0 @@ -103,9 +99,9 @@ def _score_candidate(self, item: Item, history: list[Item | None]) -> float: unseen_count = sum(1 for s in item.subjects if s not in seen) if unseen_count >= 2: - score += 2.0 # two or more unseen subjects → +2 + score += 2.0 # two or more unseen subjects → +2 elif unseen_count >= 1: - score += 1.0 # exactly one unseen subject → +1 + score += 1.0 # exactly one unseen subject → +1 # Coherence relative to up to 3 preceding (no following at the end) # Window cannot extend across a pause. From 829b1806cd438a70b835b6a9e718285ef294fbd4 Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 15 Sep 2025 21:42:31 +0530 Subject: [PATCH 03/24] ruff fix 2 --- players/player_4/player.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index e975e14..0beb87d 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -41,12 +41,11 @@ def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: return [] # walk left of the trailing pause out: list[Item] = [] - count = 0 - for x in reversed(history[:-1]): + #count = 0 + for count, x in enumerate(reversed(history[:-1])): if x is None: break out.append(x) - count += 1 if count == k: break out.reverse() @@ -143,11 +142,7 @@ def propose_item(self, history: list[Item | None]) -> Item | None: # All with best score tied = [it for s, it in scored if s == best_score] - if len(tied) == 1: - choice = tied[0] - else: - # Tie-break by preferences - choice = min(tied, key=self._preference_tiebreak_key) + choice = tied[0] if len(tied) == 1 else min(tied, key=self._preference_tiebreak_key) # Track contribution if you care downstream self.contributed_items.append(choice) From 421cb3e6d80722cd552780332be548c8014a4dac Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 15 Sep 2025 21:43:28 +0530 Subject: [PATCH 04/24] ruff fix 3 --- players/player_4/player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index 0beb87d..7b8410e 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -41,7 +41,7 @@ def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: return [] # walk left of the trailing pause out: list[Item] = [] - #count = 0 + # count = 0 for count, x in enumerate(reversed(history[:-1])): if x is None: break From 95fabfbb542ecbd71c3d10f18dc010a761128221 Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 15 Sep 2025 22:47:13 +0530 Subject: [PATCH 05/24] some changes with thresholding --- players/player_4/player.py | 50 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index 7b8410e..dad4aa7 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -51,6 +51,47 @@ def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: out.reverse() return out + def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> float: + """ + Coherence over the previous up to 3 non-pause items: + - Hot-streak penalty: if any subject in `item` appears in *each* of the last 3 -> -1.0 + - Otherwise, reward based on total matched frequency across prev-3: + sum_match/len(item.subjects) + sum_match >= 4 -> +1.5 (e.g., 2+2) + sum_match == 3 -> +1.0 (e.g., 2+1) + sum_match == 2 -> +0.5 + sum_match == 1 -> +0.25 + else -> 0.0 + The window does not cross pauses. + """ + prev3 = self._take_preceding_block(history, 3) + if not prev3 or not item.subjects: + return 0.0 + + # Hot-streak penalty (subject present in all three previous items) + if len(prev3) == 3: + sets = [set(it.subjects) for it in prev3] + common_all3 = sets[0] & sets[1] & sets[2] + if any(s in common_all3 for s in item.subjects): + return -1.0 + """if sum_match >= 4: + elif sum_match == 3: + return 1.0 + elif sum_match == 2: + return 0.5 + elif sum_match == 1: + return 0.25 + else: + return 0.0""" + # Count subject frequencies across prev-3 + counts = Counter() + for it in prev3: + counts.update(it.subjects) + + # Total matched frequency across candidate subjects + sum_match = sum(counts.get(s, 0) for s in item.subjects) + return sum_match/len(item.subjects) + def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: """ Lower is better. Uses: @@ -108,7 +149,7 @@ def _score_candidate(self, item: Item, history: list[Item | None]) -> float: context_items = self._take_preceding_block(history, 3) subj_counts = self._subjects_in(context_items) - if item.subjects: + if item.subjects and context_items: # If any subject of I is never mentioned in CI -> -1 if any(subj_counts.get(s, 0) == 0 for s in item.subjects): score -= 1.0 @@ -116,6 +157,7 @@ def _score_candidate(self, item: Item, history: list[Item | None]) -> float: elif all(subj_counts.get(s, 0) >= 2 for s in item.subjects): score += 1.0 + score += self._coherence_prev3_score(item, history) return score # ------------ selection @@ -138,7 +180,11 @@ def propose_item(self, history: list[Item | None]) -> Item | None: if not scored: return None best_score = max(s for s, _ in scored) - + print(best_score) + print(history) + if len(history)!=0 and best_score < 1: + return None + # All with best score tied = [it for s, it in scored if s == best_score] From 482c44d5daffc2f414309fc5e8a7a08982c25172 Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 15 Sep 2025 22:47:35 +0530 Subject: [PATCH 06/24] some changes with thresholding --- players/player_4/player.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index dad4aa7..5fca368 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -90,7 +90,7 @@ def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> floa # Total matched frequency across candidate subjects sum_match = sum(counts.get(s, 0) for s in item.subjects) - return sum_match/len(item.subjects) + return sum_match / len(item.subjects) def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: """ @@ -182,9 +182,9 @@ def propose_item(self, history: list[Item | None]) -> Item | None: best_score = max(s for s, _ in scored) print(best_score) print(history) - if len(history)!=0 and best_score < 1: + if len(history) != 0 and best_score < 1: return None - + # All with best score tied = [it for s, it in scored if s == best_score] From 4e20556c7094a119153ae6ce70f313147be307fc Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 15 Sep 2025 22:53:57 +0530 Subject: [PATCH 07/24] remove some print statements --- players/player_4/player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index 5fca368..f73499e 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -180,8 +180,8 @@ def propose_item(self, history: list[Item | None]) -> Item | None: if not scored: return None best_score = max(s for s, _ in scored) - print(best_score) - print(history) + #print(best_score) + #print(history) if len(history) != 0 and best_score < 1: return None From 5e337a4ec4564d30d7a82ce46b6e79c3c6da704c Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 15 Sep 2025 23:05:56 +0530 Subject: [PATCH 08/24] remove some print statements 2 --- players/player_4/player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index f73499e..dccdbba 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -180,8 +180,8 @@ def propose_item(self, history: list[Item | None]) -> Item | None: if not scored: return None best_score = max(s for s, _ in scored) - #print(best_score) - #print(history) + # print(best_score) + # print(history) if len(history) != 0 and best_score < 1: return None From e5bd8adcb9f73dd16a2c172ef852c5962cacbd80 Mon Sep 17 00:00:00 2001 From: Akshat Bhandari <74658782+akshatbhandari15@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:18:27 +0530 Subject: [PATCH 09/24] player4 py resolving broke ruff --- players/player_4/player.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index 72da77c..dccdbba 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -149,7 +149,6 @@ def _score_candidate(self, item: Item, history: list[Item | None]) -> float: context_items = self._take_preceding_block(history, 3) subj_counts = self._subjects_in(context_items) - if item.subjects and context_items: # If any subject of I is never mentioned in CI -> -1 if any(subj_counts.get(s, 0) == 0 for s in item.subjects): @@ -181,20 +180,16 @@ def propose_item(self, history: list[Item | None]) -> Item | None: if not scored: return None best_score = max(s for s, _ in scored) - #print(best_score) - #print(history) + # print(best_score) + # print(history) if len(history) != 0 and best_score < 1: return None - best_score = max(s for s, _ in scored) # All with best score tied = [it for s, it in scored if s == best_score] choice = tied[0] if len(tied) == 1 else min(tied, key=self._preference_tiebreak_key) - # All with best score - tied = [it for s, it in scored if s == best_score] - choice = tied[0] if len(tied) == 1 else min(tied, key=self._preference_tiebreak_key) # Track contribution if you care downstream self.contributed_items.append(choice) return choice From 8539200e2961f4cdbda0a6abd008809c022865aa Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Wed, 17 Sep 2025 21:29:39 +0530 Subject: [PATCH 10/24] added preferences scores for ranking items to propose --- players/player_4/player.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index dccdbba..caa3752 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -6,6 +6,7 @@ class Player4(Player): def __init__(self, snapshot: PlayerSnapshot, ctx: GameContext) -> None: # noqa: F821 super().__init__(snapshot, ctx) + self.ctx = ctx @staticmethod def _is_pause(x: Item | None) -> bool: @@ -46,11 +47,29 @@ def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: if x is None: break out.append(x) - if count == k: + if count + 1 == k: break out.reverse() return out + def _preference_bonus(self, item: Item) -> float: + """ + Average of (1 - k/|S|) over the item's subjects, where k is 1-based rank + in self.preferences (a permutation of all subjects). Unknown subjects -> 0. + Only called when history ends with a pause. + """ + S = len(self.preferences) + if S == 0 or not item.subjects: + return 0.0 + + def subj_bonus(s: int) -> float: + # worst-case (unknown) -> k = S -> 1 - S/S = 0 + k = self.preferences.index(s) + 1 if s in self.preferences else S + return 1.0 - (k / S) + + bonuses = [subj_bonus(s) for s in item.subjects] + return sum(bonuses) / len(bonuses) + def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> float: """ Coherence over the previous up to 3 non-pause items: @@ -157,6 +176,7 @@ def _score_candidate(self, item: Item, history: list[Item | None]) -> float: elif all(subj_counts.get(s, 0) >= 2 for s in item.subjects): score += 1.0 + score += self._preference_bonus(item) score += self._coherence_prev3_score(item, history) return score @@ -182,7 +202,9 @@ def propose_item(self, history: list[Item | None]) -> Item | None: best_score = max(s for s, _ in scored) # print(best_score) # print(history) - if len(history) != 0 and best_score < 1: + max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence + threshold = max_possible * 0.3 + if len(history) != 0 and best_score < threshold: return None # All with best score From 36d8473108c36b9fcf5264cf7aadc77c5cb7e9ba Mon Sep 17 00:00:00 2001 From: Jace Date: Sat, 20 Sep 2025 22:10:59 -0400 Subject: [PATCH 11/24] wrong repo --- main.py | 2 +- players/player_4/player.py | 216 +--------------------- pyproject.toml | 15 ++ uv.lock | 367 +++++++++++++++++++++++++++++++++++++ 4 files changed, 389 insertions(+), 211 deletions(-) diff --git a/main.py b/main.py index 08e75bb..47fcdd4 100644 --- a/main.py +++ b/main.py @@ -9,7 +9,7 @@ from players.player_1.player import Player1 from players.player_2.player import Player2 from players.player_3.player import Player3 -from players.player_4.player import Player4 +from players.player_4.player_4_team_player import Player4 from players.player_5.player import Player5 from players.player_6.player import Player6 from players.player_7.player import Player7 diff --git a/players/player_4/player.py b/players/player_4/player.py index caa3752..0a9d8f9 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -1,217 +1,13 @@ -from collections import Counter +import random from models.player import GameContext, Item, Player, PlayerSnapshot -class Player4(Player): +class RandomPausePlayer(Player): def __init__(self, snapshot: PlayerSnapshot, ctx: GameContext) -> None: # noqa: F821 super().__init__(snapshot, ctx) - self.ctx = ctx - @staticmethod - def _is_pause(x: Item | None) -> bool: - return x is None - - @staticmethod - def _subjects_in(items: list[Item]) -> Counter[int]: - c = Counter() - for it in items: - c.update(it.subjects) - return c - - @staticmethod - def _take_preceding_block(history: list[Item | None], k: int) -> list[Item]: - """Take up to k preceding *non-pause* items, stopping if a pause is hit.""" - out: list[Item] = [] - for x in reversed(history): - if x is None: - break - out.append(x) - if len(out) == k: - break - out.reverse() - return out - - @staticmethod - def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: - """ - When history ends with a pause, take up to k items *before that pause*, - not crossing an earlier pause. - """ - if not history or history[-1] is not None: - return [] - # walk left of the trailing pause - out: list[Item] = [] - # count = 0 - for count, x in enumerate(reversed(history[:-1])): - if x is None: - break - out.append(x) - if count + 1 == k: - break - out.reverse() - return out - - def _preference_bonus(self, item: Item) -> float: - """ - Average of (1 - k/|S|) over the item's subjects, where k is 1-based rank - in self.preferences (a permutation of all subjects). Unknown subjects -> 0. - Only called when history ends with a pause. - """ - S = len(self.preferences) - if S == 0 or not item.subjects: - return 0.0 - - def subj_bonus(s: int) -> float: - # worst-case (unknown) -> k = S -> 1 - S/S = 0 - k = self.preferences.index(s) + 1 if s in self.preferences else S - return 1.0 - (k / S) - - bonuses = [subj_bonus(s) for s in item.subjects] - return sum(bonuses) / len(bonuses) - - def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> float: - """ - Coherence over the previous up to 3 non-pause items: - - Hot-streak penalty: if any subject in `item` appears in *each* of the last 3 -> -1.0 - - Otherwise, reward based on total matched frequency across prev-3: - sum_match/len(item.subjects) - sum_match >= 4 -> +1.5 (e.g., 2+2) - sum_match == 3 -> +1.0 (e.g., 2+1) - sum_match == 2 -> +0.5 - sum_match == 1 -> +0.25 - else -> 0.0 - The window does not cross pauses. - """ - prev3 = self._take_preceding_block(history, 3) - if not prev3 or not item.subjects: - return 0.0 - - # Hot-streak penalty (subject present in all three previous items) - if len(prev3) == 3: - sets = [set(it.subjects) for it in prev3] - common_all3 = sets[0] & sets[1] & sets[2] - if any(s in common_all3 for s in item.subjects): - return -1.0 - """if sum_match >= 4: - elif sum_match == 3: - return 1.0 - elif sum_match == 2: - return 0.5 - elif sum_match == 1: - return 0.25 - else: - return 0.0""" - # Count subject frequencies across prev-3 - counts = Counter() - for it in prev3: - counts.update(it.subjects) - - # Total matched frequency across candidate subjects - sum_match = sum(counts.get(s, 0) for s in item.subjects) - return sum_match / len(item.subjects) - - def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: - """ - Lower is better. Uses: - 1) best (lowest) index among the item's subjects in self.preferences - 2) sum of indices (to prefer items whose subjects are overall higher-ranked) - 3) id string for deterministic ordering - Subjects not found in preferences rank after all known preferences. - """ - n = len(self.preferences) - idxs = [(self.preferences.index(s) if s in self.preferences else n) for s in item.subjects] - best = min(idxs) if idxs else n - total = sum(idxs) if idxs else n * 2 - return (best, total, str(item.id)) - - def _score_candidate(self, item: Item, history: list[Item | None]) -> float: - score = 0.0 - - # Repetition check: same item already in history? - already_seen = any(h is not None and h.id == item.id for h in history) - if already_seen: - # repeated items lose one point and contribute zero coherence/importance - score -= 1.0 - return score # early return: zero importance & coherence after first instance - - # Importance (only if not repeated) - score += float(item.importance) - - # Penalty: subject appeared in each of the previous 3 items? - prev3 = self._take_preceding_block(history, 3) - if len(prev3) == 3: - sets = [set(it.subjects) for it in prev3] - common_prev3 = sets[0].intersection(sets[1]).intersection(sets[2]) - if any(s in common_prev3 for s in item.subjects): - score -= 1.0 - - # Pause bonus: if most recent is a pause, and item has a subject not seen - # in the 5 turns prior to the pause (not crossing an earlier pause) -> +1 - if history and history[-1] is None: - window5 = self._take_window_before_pause(history, 5) - seen = set() - for it in window5: - seen.update(it.subjects) - - # Count how many candidate subjects are unseen in the last 5 turns - unseen_count = sum(1 for s in item.subjects if s not in seen) - - if unseen_count >= 2: - score += 2.0 # two or more unseen subjects → +2 - elif unseen_count >= 1: - score += 1.0 # exactly one unseen subject → +1 - - # Coherence relative to up to 3 preceding (no following at the end) - # Window cannot extend across a pause. - else: - context_items = self._take_preceding_block(history, 3) - subj_counts = self._subjects_in(context_items) - - if item.subjects and context_items: - # If any subject of I is never mentioned in CI -> -1 - if any(subj_counts.get(s, 0) == 0 for s in item.subjects): - score -= 1.0 - # If all subjects in I are mentioned at least twice in CI -> +1 - elif all(subj_counts.get(s, 0) >= 2 for s in item.subjects): - score += 1.0 - - score += self._preference_bonus(item) - score += self._coherence_prev3_score(item, history) - return score - - # ------------ selection - - def propose_item(self, history: list[Item | None]) -> Item | None: - """ - Pick the memory_bank item with the maximum score under the rules. - Tie-breaker: player preference order. - Returns None if no items available. - """ - if not self.memory_bank: - return None - - # Score all candidates - scored: list[tuple[float, Item]] = [ - (self._score_candidate(it, history), it) for it in self.memory_bank - ] - - # Find best score - if not scored: - return None - best_score = max(s for s, _ in scored) - # print(best_score) - # print(history) - max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence - threshold = max_possible * 0.3 - if len(history) != 0 and best_score < threshold: - return None - - # All with best score - tied = [it for s, it in scored if s == best_score] - - choice = tied[0] if len(tied) == 1 else min(tied, key=self._preference_tiebreak_key) - - # Track contribution if you care downstream - self.contributed_items.append(choice) - return choice + def propose_item(self, history: list[Item]) -> Item | None: + if random.random() < 0.75: + return random.choice(self.memory_bank) + return None diff --git a/pyproject.toml b/pyproject.toml index 067506a..f05754f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,21 @@ dependencies = [ "pygame>=2.6.1", "openai", "ruff>=0.12.8", + "aiofiles", + "torch", + "numpy", +] + +[[tool.uv.index]] +name = "pytorch-cu128" +url = "https://download.pytorch.org/whl/cu128" +explicit = true +[tool.uv.sources] +torch = [ + { index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +torchvision = [ + { index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] [tool.ruff] diff --git a/uv.lock b/uv.lock index 2c3ce8f..2cbd5b0 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,19 @@ version = 1 revision = 3 requires-python = ">=3.13" +resolution-markers = [ + "sys_platform == 'linux' or sys_platform == 'win32'", + "sys_platform != 'linux' and sys_platform != 'win32'", +] + +[[package]] +name = "aiofiles" +version = "24.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247, upload-time = "2024-06-24T11:02:03.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896, upload-time = "2024-06-24T11:02:01.529Z" }, +] [[package]] name = "annotated-types" @@ -47,9 +60,13 @@ name = "conversation" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "aiofiles" }, + { name = "numpy" }, { name = "openai" }, { name = "pygame" }, { name = "ruff" }, + { name = "torch", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, + { name = "torch", version = "2.8.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] [package.dev-dependencies] @@ -59,9 +76,13 @@ dev = [ [package.metadata] requires-dist = [ + { name = "aiofiles" }, + { name = "numpy" }, { name = "openai" }, { name = "pygame", specifier = ">=2.6.1" }, { name = "ruff", specifier = ">=0.12.8" }, + { name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, + { name = "torch", marker = "sys_platform == 'linux' or sys_platform == 'win32'", index = "https://download.pytorch.org/whl/cu128" }, ] [package.metadata.requires-dev] @@ -76,6 +97,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, +] + +[[package]] +name = "fsspec" +version = "2025.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -122,6 +161,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + [[package]] name = "jiter" version = "0.10.0" @@ -158,6 +209,230 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, ] +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "networkx" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, + { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, + { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" }, + { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" }, + { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, + { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, + { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, + { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, + { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, + { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" }, + { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, + { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, + { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, + { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" }, + { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" }, + { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" }, + { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, + { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, + { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, + { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" }, + { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" }, + { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/5b/4e4fff7bad39adf89f735f2bc87248c81db71205b62bcc0d5ca5b606b3c3/nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039", size = 322364134, upload-time = "2025-06-03T21:58:04.013Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, +] + [[package]] name = "openai" version = "1.107.0" @@ -260,6 +535,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, ] +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -269,6 +553,77 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "torch" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "sys_platform != 'linux' and sys_platform != 'win32'", +] +dependencies = [ + { name = "filelock", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, + { name = "fsspec", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, + { name = "jinja2", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, + { name = "networkx", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, + { name = "setuptools", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, + { name = "sympy", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, + { name = "typing-extensions", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/69/8b7b13bba430f5e21d77708b616f767683629fc4f8037564a177d20f90ed/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767", size = 73915128, upload-time = "2025-08-06T14:54:34.769Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/650bb7f28f771af0cb791b02348db8b7f5f64f40f6829ee82aa6ce99aabe/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211", size = 73632395, upload-time = "2025-08-06T14:55:28.645Z" }, +] + +[[package]] +name = "torch" +version = "2.8.0+cu128" +source = { registry = "https://download.pytorch.org/whl/cu128" } +resolution-markers = [ + "sys_platform == 'linux' or sys_platform == 'win32'", +] +dependencies = [ + { name = "filelock", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "fsspec", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "jinja2", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "networkx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "sympy", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl" }, + { url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313-win_amd64.whl" }, + { url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl" }, + { url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313t-win_amd64.whl" }, +] + [[package]] name = "tqdm" version = "4.67.1" @@ -281,6 +636,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] +[[package]] +name = "triton" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/7b/0a685684ed5322d2af0bddefed7906674f67974aa88b0fae6e82e3b766f6/triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb", size = 155569223, upload-time = "2025-07-30T19:58:44.017Z" }, + { url = "https://files.pythonhosted.org/packages/20/63/8cb444ad5cdb25d999b7d647abac25af0ee37d292afc009940c05b82dda0/triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d", size = 155659780, upload-time = "2025-07-30T19:58:51.171Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" From 6f43167ab27850185e1b0fcfba9f30a883ec7801 Mon Sep 17 00:00:00 2001 From: Jace Date: Sat, 20 Sep 2025 22:33:04 -0400 Subject: [PATCH 12/24] wrong repo --- players/player_4/cuda_test.py | 6 + players/player_4/player_4_team_player.py | 217 +++++++++++++ players/player_4/process_data.py | 348 +++++++++++++++++++++ players/player_4/rules.txt | 24 ++ players/player_4/sim.py | 65 ++++ players/player_4/train.py | 374 +++++++++++++++++++++++ 6 files changed, 1034 insertions(+) create mode 100644 players/player_4/cuda_test.py create mode 100644 players/player_4/player_4_team_player.py create mode 100644 players/player_4/process_data.py create mode 100644 players/player_4/rules.txt create mode 100644 players/player_4/sim.py create mode 100644 players/player_4/train.py diff --git a/players/player_4/cuda_test.py b/players/player_4/cuda_test.py new file mode 100644 index 0000000..02ae371 --- /dev/null +++ b/players/player_4/cuda_test.py @@ -0,0 +1,6 @@ +import torch + +print(torch.cuda.is_available()) +print(torch.cuda.device_count()) +print(torch.cuda.current_device()) +print(torch.cuda.get_device_name(0)) \ No newline at end of file diff --git a/players/player_4/player_4_team_player.py b/players/player_4/player_4_team_player.py new file mode 100644 index 0000000..caa3752 --- /dev/null +++ b/players/player_4/player_4_team_player.py @@ -0,0 +1,217 @@ +from collections import Counter + +from models.player import GameContext, Item, Player, PlayerSnapshot + + +class Player4(Player): + def __init__(self, snapshot: PlayerSnapshot, ctx: GameContext) -> None: # noqa: F821 + super().__init__(snapshot, ctx) + self.ctx = ctx + + @staticmethod + def _is_pause(x: Item | None) -> bool: + return x is None + + @staticmethod + def _subjects_in(items: list[Item]) -> Counter[int]: + c = Counter() + for it in items: + c.update(it.subjects) + return c + + @staticmethod + def _take_preceding_block(history: list[Item | None], k: int) -> list[Item]: + """Take up to k preceding *non-pause* items, stopping if a pause is hit.""" + out: list[Item] = [] + for x in reversed(history): + if x is None: + break + out.append(x) + if len(out) == k: + break + out.reverse() + return out + + @staticmethod + def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: + """ + When history ends with a pause, take up to k items *before that pause*, + not crossing an earlier pause. + """ + if not history or history[-1] is not None: + return [] + # walk left of the trailing pause + out: list[Item] = [] + # count = 0 + for count, x in enumerate(reversed(history[:-1])): + if x is None: + break + out.append(x) + if count + 1 == k: + break + out.reverse() + return out + + def _preference_bonus(self, item: Item) -> float: + """ + Average of (1 - k/|S|) over the item's subjects, where k is 1-based rank + in self.preferences (a permutation of all subjects). Unknown subjects -> 0. + Only called when history ends with a pause. + """ + S = len(self.preferences) + if S == 0 or not item.subjects: + return 0.0 + + def subj_bonus(s: int) -> float: + # worst-case (unknown) -> k = S -> 1 - S/S = 0 + k = self.preferences.index(s) + 1 if s in self.preferences else S + return 1.0 - (k / S) + + bonuses = [subj_bonus(s) for s in item.subjects] + return sum(bonuses) / len(bonuses) + + def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> float: + """ + Coherence over the previous up to 3 non-pause items: + - Hot-streak penalty: if any subject in `item` appears in *each* of the last 3 -> -1.0 + - Otherwise, reward based on total matched frequency across prev-3: + sum_match/len(item.subjects) + sum_match >= 4 -> +1.5 (e.g., 2+2) + sum_match == 3 -> +1.0 (e.g., 2+1) + sum_match == 2 -> +0.5 + sum_match == 1 -> +0.25 + else -> 0.0 + The window does not cross pauses. + """ + prev3 = self._take_preceding_block(history, 3) + if not prev3 or not item.subjects: + return 0.0 + + # Hot-streak penalty (subject present in all three previous items) + if len(prev3) == 3: + sets = [set(it.subjects) for it in prev3] + common_all3 = sets[0] & sets[1] & sets[2] + if any(s in common_all3 for s in item.subjects): + return -1.0 + """if sum_match >= 4: + elif sum_match == 3: + return 1.0 + elif sum_match == 2: + return 0.5 + elif sum_match == 1: + return 0.25 + else: + return 0.0""" + # Count subject frequencies across prev-3 + counts = Counter() + for it in prev3: + counts.update(it.subjects) + + # Total matched frequency across candidate subjects + sum_match = sum(counts.get(s, 0) for s in item.subjects) + return sum_match / len(item.subjects) + + def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: + """ + Lower is better. Uses: + 1) best (lowest) index among the item's subjects in self.preferences + 2) sum of indices (to prefer items whose subjects are overall higher-ranked) + 3) id string for deterministic ordering + Subjects not found in preferences rank after all known preferences. + """ + n = len(self.preferences) + idxs = [(self.preferences.index(s) if s in self.preferences else n) for s in item.subjects] + best = min(idxs) if idxs else n + total = sum(idxs) if idxs else n * 2 + return (best, total, str(item.id)) + + def _score_candidate(self, item: Item, history: list[Item | None]) -> float: + score = 0.0 + + # Repetition check: same item already in history? + already_seen = any(h is not None and h.id == item.id for h in history) + if already_seen: + # repeated items lose one point and contribute zero coherence/importance + score -= 1.0 + return score # early return: zero importance & coherence after first instance + + # Importance (only if not repeated) + score += float(item.importance) + + # Penalty: subject appeared in each of the previous 3 items? + prev3 = self._take_preceding_block(history, 3) + if len(prev3) == 3: + sets = [set(it.subjects) for it in prev3] + common_prev3 = sets[0].intersection(sets[1]).intersection(sets[2]) + if any(s in common_prev3 for s in item.subjects): + score -= 1.0 + + # Pause bonus: if most recent is a pause, and item has a subject not seen + # in the 5 turns prior to the pause (not crossing an earlier pause) -> +1 + if history and history[-1] is None: + window5 = self._take_window_before_pause(history, 5) + seen = set() + for it in window5: + seen.update(it.subjects) + + # Count how many candidate subjects are unseen in the last 5 turns + unseen_count = sum(1 for s in item.subjects if s not in seen) + + if unseen_count >= 2: + score += 2.0 # two or more unseen subjects → +2 + elif unseen_count >= 1: + score += 1.0 # exactly one unseen subject → +1 + + # Coherence relative to up to 3 preceding (no following at the end) + # Window cannot extend across a pause. + else: + context_items = self._take_preceding_block(history, 3) + subj_counts = self._subjects_in(context_items) + + if item.subjects and context_items: + # If any subject of I is never mentioned in CI -> -1 + if any(subj_counts.get(s, 0) == 0 for s in item.subjects): + score -= 1.0 + # If all subjects in I are mentioned at least twice in CI -> +1 + elif all(subj_counts.get(s, 0) >= 2 for s in item.subjects): + score += 1.0 + + score += self._preference_bonus(item) + score += self._coherence_prev3_score(item, history) + return score + + # ------------ selection + + def propose_item(self, history: list[Item | None]) -> Item | None: + """ + Pick the memory_bank item with the maximum score under the rules. + Tie-breaker: player preference order. + Returns None if no items available. + """ + if not self.memory_bank: + return None + + # Score all candidates + scored: list[tuple[float, Item]] = [ + (self._score_candidate(it, history), it) for it in self.memory_bank + ] + + # Find best score + if not scored: + return None + best_score = max(s for s, _ in scored) + # print(best_score) + # print(history) + max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence + threshold = max_possible * 0.3 + if len(history) != 0 and best_score < threshold: + return None + + # All with best score + tied = [it for s, it in scored if s == best_score] + + choice = tied[0] if len(tied) == 1 else min(tied, key=self._preference_tiebreak_key) + + # Track contribution if you care downstream + self.contributed_items.append(choice) + return choice diff --git a/players/player_4/process_data.py b/players/player_4/process_data.py new file mode 100644 index 0000000..6553653 --- /dev/null +++ b/players/player_4/process_data.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +""" +process.py - Convert raw game JSON files to processed training data + +Usage: python process.py +- Reads from raw/batch_*.json +- Automatically identifies Player4 by looking at speaker_name +- Extracts training examples ONLY when Player4 acts (proposes item or passes) +- Game state includes ONLY Player4's memory bank and preferences (realistic) +- Saves to processed/batch_*.json +- Skips games where Player4 never speaks +""" + +import json +import os +from pathlib import Path +from typing import Dict, List, Any, Optional +from dataclasses import dataclass, asdict + +@dataclass +class ProcessedItem: + """Processed item representation""" + subjects: List[int] + importance: float + item_id: str + +@dataclass +class ProcessedGameState: + """Game state at a specific turn - Player4's view only""" + turn_number: int + conversation_history: List[ProcessedItem] + my_memory_bank: List[ProcessedItem] # Only Player4's memory bank + my_preferences: List[int] # Only Player4's preferences + conversation_length: int + pause_count: int + game_over: bool + +@dataclass +class ProcessedAction: + """Action taken by a player""" + player_id: str + action_type: str # "propose_item", "pass" + item_id: Optional[str] # None if pass + item_subjects: Optional[List[int]] # None if pass + item_importance: Optional[float] # None if pass + +@dataclass +class TrainingExample: + """Training example for Player4""" + game_id: str + turn_number: int + state_before: ProcessedGameState + our_action: ProcessedAction + immediate_reward: float + game_over: bool + +@dataclass +class ProcessedGame: + """Complete processed game - focused on Player4 training data""" + game_id: str + player4_id: str + training_examples: List[TrainingExample] # Training data for Player4 + final_scores: Dict[str, Dict[str, float]] + game_metadata: Dict[str, Any] + +class GameDataProcessor: + """Processes raw game JSON into structured format""" + + def __init__(self): + self.processed_games = [] + + def find_player4_id(self, raw_game: Dict) -> Optional[str]: + """ + First pass: Find Player4's ID by looking at speaker_name + Returns None if Player4 never speaks + """ + for turn_data in raw_game['turn_impact']: + speaker_name = turn_data.get('speaker_name', '') + if speaker_name == 'Player4': + return turn_data['speaker_id'] + return None + + def process_raw_game(self, raw_game: Dict) -> Optional[ProcessedGame]: + """Convert raw game JSON to ProcessedGame""" + + # First pass: Find Player4's ID + player4_id = self.find_player4_id(raw_game) + if player4_id is None: + print(" Skipping game - Player4 never speaks") + return None + + print(f" Found Player4 ID: {player4_id[:8]}...") + + # Extract player information + players = {p['id']: p for p in raw_game['scores']['player_scores']} + + # Check if Player4 is in the final scores + if player4_id not in players: + print(f" Skipping game - Player4 {player4_id} not found in player scores") + return None + + # Second pass: Process each turn, but only extract training data when Player4 acts + training_examples = [] + conversation_history = [] + + for turn_data in raw_game['turn_impact']: + # Player4 makes a decision every turn: either propose an item or pass + # We create training examples for every turn where Player4 had to decide + player4_action = None + + if player4_id in turn_data['proposals']: + # Player4 chose to propose an item + proposal = turn_data['proposals'][player4_id] + player4_action = ProcessedAction( + player_id=player4_id, + action_type="propose_item", + item_id=proposal['id'], + item_subjects=proposal['subjects'], + item_importance=proposal['importance'] + ) + else: + # Player4 chose to pass + # This happens whether others proposed or no one proposed + player4_action = ProcessedAction( + player_id=player4_id, + action_type="pass", + item_id=None, + item_subjects=None, + item_importance=None + ) + + # Create game state before this turn - Player4's view only + game_state_before = ProcessedGameState( + turn_number=turn_data['turn'], + conversation_history=[ + ProcessedItem( + subjects=item['subjects'], + importance=item['importance'], + item_id=item['id'] + ) for item in conversation_history + ], + my_memory_bank=[ + ProcessedItem( + subjects=item['subjects'], + importance=item['importance'], + item_id=item['id'] + ) for item in players[player4_id]['memory_bank'] + ], + my_preferences=players[player4_id]['preferences'], + conversation_length=raw_game['scores']['conversation_length'], + pause_count=raw_game['scores']['pauses'], + game_over=turn_data['is_over'] + ) + + # Update conversation history for next turn + if turn_data.get('item'): + conversation_history.append(turn_data['item']) + + # Calculate immediate reward for Player4's decision + immediate_reward = 0.0 + if turn_data['speaker_id'] == player4_id: + immediate_reward = turn_data['score_impact']['total'] + elif player4_action.action_type == "propose_item": + immediate_reward = self._calculate_individual_bonus( + turn_data['item']['subjects'], + players[player4_id]['preferences'] + ) + else: + other_player_spoken_item_subjects = turn_data.get('item', {}).get('subjects', []) + if other_player_spoken_item_subjects: + immediate_reward = self._calculate_individual_bonus( + other_player_spoken_item_subjects, + players[player4_id]['preferences'] + ) + else: + immediate_reward = 0.0 + + training_example = TrainingExample( + game_id=f"game_{len(self.processed_games)}", + turn_number=turn_data['turn'], + state_before=game_state_before, + our_action=player4_action, + immediate_reward=immediate_reward, + game_over=turn_data['is_over'] + ) + + training_examples.append(training_example) + + + # Create final processed game + processed_game = ProcessedGame( + game_id=f"game_{len(self.processed_games)}", + player4_id=player4_id, + training_examples=training_examples, + final_scores={ + pid: player_data['scores'] + for pid, player_data in players.items() + }, + game_metadata={ + 'player4_id': player4_id, + 'player4_training_examples': len(training_examples), + 'conversation_length': raw_game['scores']['conversation_length'], + 'total_pauses': raw_game['scores']['pauses'], + 'num_players': len(players), + 'shared_score_breakdown': raw_game['score_breakdown'] + } + ) + + return processed_game + + def _calculate_individual_bonus(self, subjects: List[int], preferences: List[int]) -> float: + """Calculate individual preference bonus for subjects""" + if not subjects: + return 0.0 + + bonus = 0.0 + for subject in subjects: + if subject in preferences: + rank = preferences.index(subject) + bonus += 1.0 - (rank / len(preferences)) + return bonus / len(subjects) + +def process_batch_file(input_path: Path, output_path: Path) -> bool: + """Process a single batch file""" + try: + print(f"Processing {input_path.name}...") + + # Load raw data + with open(input_path, 'r') as f: + raw_data = json.load(f) + + # Initialize processor + processor = GameDataProcessor() + + # Process each game in the batch + processed_games = [] + skipped_games = 0 + + if isinstance(raw_data, list): + # Multiple games in one file + for i, raw_game in enumerate(raw_data): + try: + processed_game = processor.process_raw_game(raw_game) + if processed_game is not None: + processed_game.game_id = f"{input_path.stem}_game_{i}" + processed_games.append(asdict(processed_game)) + else: + skipped_games += 1 + except Exception as e: + print(f" Error processing game {i} in {input_path.name}: {e}") + skipped_games += 1 + else: + # Single game in file + try: + processed_game = processor.process_raw_game(raw_data) + if processed_game is not None: + processed_game.game_id = input_path.stem + processed_games.append(asdict(processed_game)) + else: + skipped_games += 1 + except Exception as e: + print(f" Error processing {input_path.name}: {e}") + return False + + if not processed_games: + print(f" No valid games found in {input_path.name} (Player4 never spoke in any games)") + return False + + # Save processed data + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, 'w') as f: + json.dump(processed_games, f, indent=2) + + total_training_examples = sum(len(game['training_examples']) for game in processed_games) + print(f" Success: {len(processed_games)} games, {total_training_examples} training examples") + if skipped_games > 0: + print(f" Skipped: {skipped_games} games (Player4 didn't speak)") + + return True + + except Exception as e: + print(f" Failed to process {input_path.name}: {e}") + return False + +def main(): + """Main processing function""" + raw_dir = Path("players/player_4/raw") + processed_dir = Path("players/player_4/processed") + + # Check if raw directory exists + if not raw_dir.exists(): + print(f"Error: {raw_dir} directory not found!") + print("Please create the 'raw' directory and place your batch_*.json files there.") + return + + # Find all batch files + batch_files = sorted(raw_dir.glob("batch_*.json")) + + if not batch_files: + print(f"No batch_*.json files found in {raw_dir}/") + print("Expected files like: batch_1.json, batch_2.json, etc.") + return + + print(f"Found {len(batch_files)} batch files to process") + print("Looking for Player4 in each game...") + + # Process each batch file + success_count = 0 + for batch_file in batch_files: + output_file = processed_dir / batch_file.name + + if process_batch_file(batch_file, output_file): + success_count += 1 + + print(f"\nProcessing complete!") + print(f"Successfully processed: {success_count}/{len(batch_files)} files") + print(f"Processed files saved to: {processed_dir}/") + + # Show summary + if success_count > 0: + print(f"\nProcessed file structure for Player4:") + total_training_examples = 0 + total_games = 0 + for processed_file in sorted(processed_dir.glob("batch_*.json")): + try: + with open(processed_file, 'r') as f: + data = json.load(f) + if isinstance(data, list): + game_count = len(data) + training_count = sum(len(game.get('training_examples', [])) for game in data) + else: + game_count = 1 + training_count = len(data.get('training_examples', [])) + total_training_examples += training_count + total_games += game_count + print(f" {processed_file.name}: {game_count} games, {training_count} training examples") + except: + print(f" {processed_file.name}: (error reading)") + + print(f"\nTotal: {total_games} games processed, {total_training_examples} training examples extracted") + print(f"Ready for RL training!") + + if total_training_examples == 0: + print(f"\nWARNING: No training examples found for Player4") + print("Player4 may not be speaking in any games, or there may be an issue with the data format.") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/players/player_4/rules.txt b/players/player_4/rules.txt new file mode 100644 index 0000000..0ed3344 --- /dev/null +++ b/players/player_4/rules.txt @@ -0,0 +1,24 @@ +Project 1: Conversation +We'll be simulating a group conversation in which you will be coding players whose goal is to have a "good" interaction of ideas. There are some shared properties of conversations that make them better for all participants. Other properties of conversations are sought by individual participants. At the end of the conversation, the whole interaction will be measured according to the collective and individual scoring functions, which are added together for each participant. For some conversations, there will be one or more random players included who may steer the conversation in unpredictable ways. + +There are P players, and every player has a memory bank of B newsworthy items that they could contribute to the conversation. Half of the items are about a single subject, while the other half are about two subjects. Every item also has an importance, which is a number uniformly generated between 0 and 1. There are S subjects in total, and each player's memory bank is generated at random. The conversation has a fixed length L that everybody knows in advance. + +A conversation is a sequence of items contributed by players. On any turn, each player may propose to share an item. If multiple players attempt to contribute an item, the simulator will do the following: If the player currently talking wants to contribute again, they will be chosen with probability 0.5. If the player currently talking is not chosen, then the simulator will choose a player at random from among those who propose an item and have so far contributed the smallest number of items among that group. That way, each player can (if they wish) contribute approximately as often as other players over the long run. If nobody contributes anything on a given turn, then the sequence is filled by a "pause". If there are three consecutive pauses, then the conversation ends prematurely. + +The shared goals of a conversation are as follows: + +Coherence +For every item I, the (up to) 3 preceding items and (up to) 3 following items are collected into a set CI of context items. If a subject of I is never mentioned in CI then one point is lost from the final score. If all subjects in I are mentioned at least twice in CI then one point is added to the final score. The window defining CI does not extend beyond the start of the conversation or any pauses. +Importance +The total importance of all items in the final item sequence is added to the shared score. (Everybody agrees about the importance of each item.) +Nonrepetition +An item that is repeated has zero importance and does not contribute to the coherence score after the first instance of the item. +Freshness +Immediately after a pause, an item with a subject that was not previously present in the 5 turns prior to the pause gets a point added to the final score. An item with two novel subjects of this sort gets two bonus points. Repeated items do not get freshness points. +Nonmonotonousness +An item with a subject that was also present in each of the previous three items leads to the loss of a point. Also, repeated items lose one point, since the audience has already heard that news. +Individual goals depend on each player's preferred topics of conversation. The simulator gives each player a random permutation of the S subjects that represents the ranked preference of that player. An item with a subject that is of rank k for that player achieves a bonus of 1-k/|S| where |S| is the total number of possible subjects. An item with two subjects gives the player a bonus corresponing to the average of the two individual bonuses. Players know their own ranking, but not the ranking of other players. + +At the end of the game, the total score for each player is divided by L, resulting in an overall conversation quality that is the final metric by which players will be compared. Even though the shared goals lead to scores for all participants, such scores can still be used to compare players. For example, how does the global score change when one player is left out? How does the global score vary when all participants are running the same group's code. Make sure to code your player in an encapsulated fashion, so that several instances of the same player can be instatiated without sharing information across instances. We'll run a variety of configurations for various combinations of parameters as part of a tournament at the end of the project. The precise configurations for the tournament will be discussed in class. The simulator will accept a random number seed to facilitate repeatable simulations. + +Your primary goal is to code a player that achieves the highest conversation quality. Additionally, I would like to see some analysis of the resulting conversations. For example, how do long conversations compare to short ones? Are there emergent patterns in the conversations? How badly do one (or more) random players affect a conversation's quality? \ No newline at end of file diff --git a/players/player_4/sim.py b/players/player_4/sim.py new file mode 100644 index 0000000..95e3825 --- /dev/null +++ b/players/player_4/sim.py @@ -0,0 +1,65 @@ +import asyncio +import json +import aiofiles +from collections import deque +import os + +class StreamingSimulator: + def __init__(self, total_games=512, buffer_size=128, concurrent_games=32): + self.buffer = deque(maxlen=buffer_size) + self.total_games = total_games + self.total_games_so_far = 0 + self.concurrent_games = concurrent_games + + async def run_continuous(self): + """Continuously generate games and save batches""" + while True: + # Run game asynchronously + tasks = [self.run_game_async() for _ in range(self.concurrent_games)] + results = await asyncio.gather(*tasks) + for game_data in results: + self.buffer.append(game_data) + self.total_games_so_far += 1 + print(f'Total games simulated: {self.total_games_so_far}') + + # Save batch periodically + if len(self.buffer) >= self.buffer.maxlen: + await self.save_batch() + print(f'Saved batch of {self.buffer.maxlen} games.') + self.buffer.clear() + + # Stop if we've reached the total number of games + if self.total_games_so_far >= self.total_games: + break + + async def run_game_async(self): + """Run game simulator asynchronously""" + proc = await asyncio.create_subprocess_exec( + 'uv', + 'run', + 'main.py', + '--subjects', '20', + '--memory_size', '10', + '--length', '100', + '--player', 'prp', '9', + '--player', 'p4', '1', + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + + stdout, stderr = await proc.communicate() + # Remove everything from stdout before the first { character + json_output = "{" + stdout.decode().split("{", 1)[-1] + return json.loads(json_output) + + async def save_batch(self): + """Save current buffer to file""" + filename = f'players/player_4/raw/batch_{self.total_games_so_far // self.buffer.maxlen}.json' + os.makedirs(os.path.dirname(filename), exist_ok=True) + async with aiofiles.open(filename, 'w') as f: + await f.write(json.dumps(list(self.buffer))) + print(f'Saved batch to {filename}') + +streaming_simulator = StreamingSimulator() +asyncio.run(streaming_simulator.run_continuous()) + diff --git a/players/player_4/train.py b/players/player_4/train.py new file mode 100644 index 0000000..3486639 --- /dev/null +++ b/players/player_4/train.py @@ -0,0 +1,374 @@ +import json +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import numpy as np +from pathlib import Path +import sys +from typing import List, Dict, Tuple, Optional +from dataclasses import dataclass +import random +from collections import deque + +GAME_MAX_SUBJECTS = 20 +GAME_MAX_MEMORY_BANK = 10 +NETWORK_CONV_HISTORY_ITEMS = 5 + +@dataclass +class TrainingExample: + """Single training example from processed data""" + game_id: str + turn_number: int + state_tensor: torch.Tensor + action_index: int + reward: float + next_state_tensor: Optional[torch.Tensor] + done: bool + +class StateEncoder: + """Converts JSON state to fixed-size tensor representation""" + + def __init__(self, max_subjects=GAME_MAX_SUBJECTS, max_memory_items=GAME_MAX_MEMORY_BANK, max_conversation_items=NETWORK_CONV_HISTORY_ITEMS): + self.max_subjects = max_subjects + self.max_memory_items = max_memory_items + self.max_conversation_items = max_conversation_items + + def encode_state(self, state_json: Dict) -> torch.Tensor: + """Convert state JSON to tensor""" + features = [] + + # 1. Encode conversation history + conv_features = torch.zeros(self.max_conversation_items, self.max_subjects + 1) # +1 for importance + conversation = state_json['conversation_history'] + for i, item in enumerate(conversation[-self.max_conversation_items:]): + # One-hot encode subjects + for subject in item['subjects']: + if subject < self.max_subjects: + conv_features[i, subject] = 1.0 + # Add importance + conv_features[i, -1] = item['importance'] + + # Flatten conversation features + features.append(conv_features.flatten()) + + # 2. Encode memory bank + memory_features = torch.zeros(self.max_memory_items, self.max_subjects + 1) + memory_bank = state_json['my_memory_bank'] + for i, item in enumerate(memory_bank[:self.max_memory_items]): + # One-hot encode subjects + for subject in item['subjects']: + if subject < self.max_subjects: + memory_features[i, subject] = 1.0 + # Add importance + memory_features[i, -1] = item['importance'] + + # Flatten memory features + features.append(memory_features.flatten()) + + # 3. Encode preferences (ranking of subjects) + pref_features = torch.zeros(self.max_subjects) + preferences = state_json['my_preferences'] + for i, subject in enumerate(preferences): + if subject < self.max_subjects: + # Higher rank = higher value (inverse of position) + pref_features[subject] = 1.0 - (i / len(preferences)) + features.append(pref_features) + + # 4. Encode game metadata + metadata = torch.tensor([ + state_json['turn_number'] / state_json['conversation_length'], # Progress + len(state_json['conversation_history']) / self.max_conversation_items, # Conv fullness + state_json['pause_count'] / 3.0, # Normalized pauses + 1.0 if state_json['game_over'] else 0.0 # Game over flag + ]) + features.append(metadata) + + # Concatenate all features + return torch.cat(features) + + def get_feature_size(self) -> int: + """Calculate total feature vector size""" + conv_size = self.max_conversation_items * (self.max_subjects + 1) + memory_size = self.max_memory_items * (self.max_subjects + 1) + pref_size = self.max_subjects + metadata_size = 4 + return conv_size + memory_size + pref_size + metadata_size + +class QNetwork(nn.Module): + """Q-Network for action value estimation""" + + def __init__(self, state_size: int, max_actions: int, hidden_size: int = 512): + super().__init__() + self.max_actions = max_actions + + self.network = nn.Sequential( + nn.Linear(state_size, hidden_size), + nn.ReLU(), + nn.Dropout(0.2), + nn.Linear(hidden_size, hidden_size), + nn.ReLU(), + nn.Dropout(0.2), + nn.Linear(hidden_size, hidden_size // 2), + nn.ReLU(), + nn.Linear(hidden_size // 2, max_actions) + ) + + def forward(self, state: torch.Tensor, num_actions: int) -> torch.Tensor: + """ + Forward pass - returns Q-values for available actions + Args: + state: State tensor + num_actions: Number of available actions (memory_bank_size + 1) + """ + q_values = self.network(state) + # Only return Q-values for available actions + return q_values[:num_actions] + +class DataLoader: + """Loads and processes training data from JSON files""" + + def __init__(self, data_directory: str): + self.data_directory = Path(data_directory) + self.state_encoder = StateEncoder() + + def load_all_data(self) -> List[TrainingExample]: + """Load all training examples from batch files""" + training_examples = [] + + # Find all batch files + batch_files = sorted(self.data_directory.glob("batch_*.json")) + print(f"Found {len(batch_files)} batch files") + + for batch_file in batch_files: + print(f"Loading {batch_file.name}...") + try: + with open(batch_file, 'r') as f: + games_data = json.load(f) + + # Process each game + for game_data in games_data: + game_examples = self._process_game(game_data) + training_examples.extend(game_examples) + + except Exception as e: + print(f"Error loading {batch_file}: {e}") + + print(f"Loaded {len(training_examples)} training examples") + return training_examples + + def _process_game(self, game_data: Dict) -> List[TrainingExample]: + """Process a single game's training examples""" + examples = [] + training_data = game_data['training_examples'] + + for i, example in enumerate(training_data): + # Encode current state + state_tensor = self.state_encoder.encode_state(example['state_before']) + + # Convert action to index + action_index = self._action_to_index(example['our_action'], example['state_before']) + + # Get next state (if available) + next_state_tensor = None + if i + 1 < len(training_data): + next_example = training_data[i + 1] + # Only use as next state if consecutive turns + if next_example['turn_number'] == example['turn_number'] + 1: + next_state_tensor = self.state_encoder.encode_state(next_example['state_before']) + + training_example = TrainingExample( + game_id=example.get('game_id', 'unknown'), + turn_number=example['turn_number'], + state_tensor=state_tensor, + action_index=action_index, + reward=example['immediate_reward'], + next_state_tensor=next_state_tensor, + done=example['game_over'] + ) + + examples.append(training_example) + + return examples + + def _action_to_index(self, action: Dict, state: Dict) -> int: + """Convert action to index in memory bank""" + if action['action_type'] == 'pass': + # Pass action is index = len(memory_bank) + return len(state['my_memory_bank']) + else: + # Find which memory bank item was proposed + item_id = action['item_id'] + for i, memory_item in enumerate(state['my_memory_bank']): + if memory_item['item_id'] == item_id: + return i + # Fallback to pass if item not found + return len(state['my_memory_bank']) + +class QLearningTrainer: + """Q-Learning trainer""" + + def __init__(self, state_size: int, max_actions: int, lr: float = 1e-4, gamma: float = 0.95): + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + print(f"Using device: {self.device}") + + self.q_network = QNetwork(state_size, max_actions).to(self.device) + self.target_network = QNetwork(state_size, max_actions).to(self.device) + self.optimizer = optim.Adam(self.q_network.parameters(), lr=lr) + + self.gamma = gamma + self.update_target_every = 1000 # Update target network every N steps + self.training_step = 0 + + # Copy initial weights to target network + self.target_network.load_state_dict(self.q_network.state_dict()) + + def train(self, training_examples: List[TrainingExample], epochs: int = 50, batch_size: int = 32): + """Train the Q-network""" + print(f"Training for {epochs} epochs with batch size {batch_size}") + + # Filter examples that have next states (for proper Q-learning) + valid_examples = [ex for ex in training_examples if ex.next_state_tensor is not None] + print(f"Using {len(valid_examples)} examples with next states for Q-learning") + + for epoch in range(epochs): + # Shuffle training data + random.shuffle(valid_examples) + + total_loss = 0.0 + num_batches = 0 + + # Process in batches + for i in range(0, len(valid_examples), batch_size): + batch = valid_examples[i:i + batch_size] + loss = self._train_batch(batch) + total_loss += loss + num_batches += 1 + + # Update target network + if self.training_step % self.update_target_every == 0: + self.target_network.load_state_dict(self.q_network.state_dict()) + + self.training_step += 1 + + avg_loss = total_loss / num_batches if num_batches > 0 else 0 + print(f"Epoch {epoch + 1}/{epochs}, Average Loss: {avg_loss:.4f}") + + def _train_batch(self, batch: List[TrainingExample]) -> float: + """Train on a single batch""" + if not batch: + return 0.0 + + # Prepare batch data + states = torch.stack([ex.state_tensor for ex in batch]).to(self.device) + actions = torch.tensor([ex.action_index for ex in batch]).to(self.device) + rewards = torch.tensor([ex.reward for ex in batch], dtype=torch.float).to(self.device) + next_states = torch.stack([ex.next_state_tensor for ex in batch]).to(self.device) + dones = torch.tensor([ex.done for ex in batch], dtype=torch.bool).to(self.device) + + # Current Q-values + current_q_values = [] + for i, ex in enumerate(batch): + memory_size = len(ex.state_tensor) # Simplified - should extract actual memory size + num_actions = GAME_MAX_MEMORY_BANK + 1 + q_vals = self.q_network(states[i:i+1], num_actions) + # print(f"Q-values for example {i}: {q_vals}") + # print(f"Action taken: {actions[i]}, Q-value: {q_vals[0, actions[i]].item()}") + current_q_values.append(q_vals[0, actions[i]]) + + current_q_values = torch.stack(current_q_values) + + # Target Q-values using target network + with torch.no_grad(): + target_q_values = [] + for i, ex in enumerate(batch): + if dones[i]: + target_q_values.append(rewards[i]) + else: + num_actions = GAME_MAX_MEMORY_BANK + 1 + next_q_vals = self.target_network(next_states[i:i+1], num_actions) + target = rewards[i] + self.gamma * next_q_vals.max() + target_q_values.append(target) + + target_q_values = torch.stack(target_q_values) + + # Compute loss + loss = F.mse_loss(current_q_values, target_q_values) + + # Backpropagation + self.optimizer.zero_grad() + loss.backward() + self.optimizer.step() + + return loss.item() + + def save_model(self, path: str): + """Save trained model""" + torch.save({ + 'q_network_state_dict': self.q_network.state_dict(), + 'target_network_state_dict': self.target_network.state_dict(), + 'optimizer_state_dict': self.optimizer.state_dict(), + 'training_step': self.training_step + }, path) + print(f"Model saved to {path}") + + def load_model(self, path: str): + """Load trained model""" + checkpoint = torch.load(path, map_location=self.device) + self.q_network.load_state_dict(checkpoint['q_network_state_dict']) + self.target_network.load_state_dict(checkpoint['target_network_state_dict']) + self.optimizer.load_state_dict(checkpoint['optimizer_state_dict']) + self.training_step = checkpoint.get('training_step', 0) + print(f"Model loaded from {path}") + +def main(): + """Main training function""" + if len(sys.argv) != 2: + print("Usage: uv run players/player_4/train.py ") + print("Example: uv run players/player_4/train.py players/player_4/processed/") + return + + data_directory = sys.argv[1] + + # Check if directory exists + if not Path(data_directory).exists(): + print(f"Error: Directory {data_directory} not found!") + return + + print("=" * 60) + print("Q-LEARNING TRAINING FOR CONVERSATION GAME") + print("=" * 60) + + # Step 1: Load and process data + print("\n1. LOADING DATA...") + data_loader = DataLoader(data_directory) + training_examples = data_loader.load_all_data() + + if not training_examples: + print("No training examples found!") + return + + # Step 2: Initialize trainer + print("\n2. INITIALIZING Q-NETWORK...") + state_size = data_loader.state_encoder.get_feature_size() + max_actions = GAME_MAX_MEMORY_BANK + 1 # + 1 for pass action + + print(f"State vector size: {state_size}") + print(f"Maximum actions: {max_actions}") + + trainer = QLearningTrainer(state_size, max_actions, lr=1e-5, gamma=0.95) + + # Step 3: Train the model + print("\n3. TRAINING Q-NETWORK...") + trainer.train(training_examples, epochs=100, batch_size=32) + + # Step 4: Save the model + print("\n4. SAVING MODEL...") + model_path = Path(data_directory).parent / "trained_qnetwork.pth" + trainer.save_model(str(model_path)) + + print(f"\n✓ Training complete! Model saved to: {model_path}") + print("\nYou can now use this trained model to play games!") + +if __name__ == "__main__": + main() \ No newline at end of file From 319f6cd919bc1dd5e4abf89ef1b7badef82d8c7c Mon Sep 17 00:00:00 2001 From: Jace Date: Sat, 20 Sep 2025 23:11:17 -0400 Subject: [PATCH 13/24] readme --- players/player_4/readme.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 players/player_4/readme.txt diff --git a/players/player_4/readme.txt b/players/player_4/readme.txt new file mode 100644 index 0000000..2b2303f --- /dev/null +++ b/players/player_4/readme.txt @@ -0,0 +1,5 @@ +To use: + +uv run sim.py +uv run process_data.py +uv run train.py players/player_4/processed \ No newline at end of file From 44cc85847879104fe8f6e256400073a417f82e04 Mon Sep 17 00:00:00 2001 From: Jace Date: Sat, 20 Sep 2025 23:59:24 -0400 Subject: [PATCH 14/24] restore latest player --- main.py | 2 +- players/player_4/player.py | 216 +++++++++++++++++++++- players/player_4/player_4_team_player.py | 217 ----------------------- 3 files changed, 211 insertions(+), 224 deletions(-) delete mode 100644 players/player_4/player_4_team_player.py diff --git a/main.py b/main.py index 47fcdd4..08e75bb 100644 --- a/main.py +++ b/main.py @@ -9,7 +9,7 @@ from players.player_1.player import Player1 from players.player_2.player import Player2 from players.player_3.player import Player3 -from players.player_4.player_4_team_player import Player4 +from players.player_4.player import Player4 from players.player_5.player import Player5 from players.player_6.player import Player6 from players.player_7.player import Player7 diff --git a/players/player_4/player.py b/players/player_4/player.py index 0a9d8f9..caa3752 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -1,13 +1,217 @@ -import random +from collections import Counter from models.player import GameContext, Item, Player, PlayerSnapshot -class RandomPausePlayer(Player): +class Player4(Player): def __init__(self, snapshot: PlayerSnapshot, ctx: GameContext) -> None: # noqa: F821 super().__init__(snapshot, ctx) + self.ctx = ctx - def propose_item(self, history: list[Item]) -> Item | None: - if random.random() < 0.75: - return random.choice(self.memory_bank) - return None + @staticmethod + def _is_pause(x: Item | None) -> bool: + return x is None + + @staticmethod + def _subjects_in(items: list[Item]) -> Counter[int]: + c = Counter() + for it in items: + c.update(it.subjects) + return c + + @staticmethod + def _take_preceding_block(history: list[Item | None], k: int) -> list[Item]: + """Take up to k preceding *non-pause* items, stopping if a pause is hit.""" + out: list[Item] = [] + for x in reversed(history): + if x is None: + break + out.append(x) + if len(out) == k: + break + out.reverse() + return out + + @staticmethod + def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: + """ + When history ends with a pause, take up to k items *before that pause*, + not crossing an earlier pause. + """ + if not history or history[-1] is not None: + return [] + # walk left of the trailing pause + out: list[Item] = [] + # count = 0 + for count, x in enumerate(reversed(history[:-1])): + if x is None: + break + out.append(x) + if count + 1 == k: + break + out.reverse() + return out + + def _preference_bonus(self, item: Item) -> float: + """ + Average of (1 - k/|S|) over the item's subjects, where k is 1-based rank + in self.preferences (a permutation of all subjects). Unknown subjects -> 0. + Only called when history ends with a pause. + """ + S = len(self.preferences) + if S == 0 or not item.subjects: + return 0.0 + + def subj_bonus(s: int) -> float: + # worst-case (unknown) -> k = S -> 1 - S/S = 0 + k = self.preferences.index(s) + 1 if s in self.preferences else S + return 1.0 - (k / S) + + bonuses = [subj_bonus(s) for s in item.subjects] + return sum(bonuses) / len(bonuses) + + def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> float: + """ + Coherence over the previous up to 3 non-pause items: + - Hot-streak penalty: if any subject in `item` appears in *each* of the last 3 -> -1.0 + - Otherwise, reward based on total matched frequency across prev-3: + sum_match/len(item.subjects) + sum_match >= 4 -> +1.5 (e.g., 2+2) + sum_match == 3 -> +1.0 (e.g., 2+1) + sum_match == 2 -> +0.5 + sum_match == 1 -> +0.25 + else -> 0.0 + The window does not cross pauses. + """ + prev3 = self._take_preceding_block(history, 3) + if not prev3 or not item.subjects: + return 0.0 + + # Hot-streak penalty (subject present in all three previous items) + if len(prev3) == 3: + sets = [set(it.subjects) for it in prev3] + common_all3 = sets[0] & sets[1] & sets[2] + if any(s in common_all3 for s in item.subjects): + return -1.0 + """if sum_match >= 4: + elif sum_match == 3: + return 1.0 + elif sum_match == 2: + return 0.5 + elif sum_match == 1: + return 0.25 + else: + return 0.0""" + # Count subject frequencies across prev-3 + counts = Counter() + for it in prev3: + counts.update(it.subjects) + + # Total matched frequency across candidate subjects + sum_match = sum(counts.get(s, 0) for s in item.subjects) + return sum_match / len(item.subjects) + + def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: + """ + Lower is better. Uses: + 1) best (lowest) index among the item's subjects in self.preferences + 2) sum of indices (to prefer items whose subjects are overall higher-ranked) + 3) id string for deterministic ordering + Subjects not found in preferences rank after all known preferences. + """ + n = len(self.preferences) + idxs = [(self.preferences.index(s) if s in self.preferences else n) for s in item.subjects] + best = min(idxs) if idxs else n + total = sum(idxs) if idxs else n * 2 + return (best, total, str(item.id)) + + def _score_candidate(self, item: Item, history: list[Item | None]) -> float: + score = 0.0 + + # Repetition check: same item already in history? + already_seen = any(h is not None and h.id == item.id for h in history) + if already_seen: + # repeated items lose one point and contribute zero coherence/importance + score -= 1.0 + return score # early return: zero importance & coherence after first instance + + # Importance (only if not repeated) + score += float(item.importance) + + # Penalty: subject appeared in each of the previous 3 items? + prev3 = self._take_preceding_block(history, 3) + if len(prev3) == 3: + sets = [set(it.subjects) for it in prev3] + common_prev3 = sets[0].intersection(sets[1]).intersection(sets[2]) + if any(s in common_prev3 for s in item.subjects): + score -= 1.0 + + # Pause bonus: if most recent is a pause, and item has a subject not seen + # in the 5 turns prior to the pause (not crossing an earlier pause) -> +1 + if history and history[-1] is None: + window5 = self._take_window_before_pause(history, 5) + seen = set() + for it in window5: + seen.update(it.subjects) + + # Count how many candidate subjects are unseen in the last 5 turns + unseen_count = sum(1 for s in item.subjects if s not in seen) + + if unseen_count >= 2: + score += 2.0 # two or more unseen subjects → +2 + elif unseen_count >= 1: + score += 1.0 # exactly one unseen subject → +1 + + # Coherence relative to up to 3 preceding (no following at the end) + # Window cannot extend across a pause. + else: + context_items = self._take_preceding_block(history, 3) + subj_counts = self._subjects_in(context_items) + + if item.subjects and context_items: + # If any subject of I is never mentioned in CI -> -1 + if any(subj_counts.get(s, 0) == 0 for s in item.subjects): + score -= 1.0 + # If all subjects in I are mentioned at least twice in CI -> +1 + elif all(subj_counts.get(s, 0) >= 2 for s in item.subjects): + score += 1.0 + + score += self._preference_bonus(item) + score += self._coherence_prev3_score(item, history) + return score + + # ------------ selection + + def propose_item(self, history: list[Item | None]) -> Item | None: + """ + Pick the memory_bank item with the maximum score under the rules. + Tie-breaker: player preference order. + Returns None if no items available. + """ + if not self.memory_bank: + return None + + # Score all candidates + scored: list[tuple[float, Item]] = [ + (self._score_candidate(it, history), it) for it in self.memory_bank + ] + + # Find best score + if not scored: + return None + best_score = max(s for s, _ in scored) + # print(best_score) + # print(history) + max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence + threshold = max_possible * 0.3 + if len(history) != 0 and best_score < threshold: + return None + + # All with best score + tied = [it for s, it in scored if s == best_score] + + choice = tied[0] if len(tied) == 1 else min(tied, key=self._preference_tiebreak_key) + + # Track contribution if you care downstream + self.contributed_items.append(choice) + return choice diff --git a/players/player_4/player_4_team_player.py b/players/player_4/player_4_team_player.py deleted file mode 100644 index caa3752..0000000 --- a/players/player_4/player_4_team_player.py +++ /dev/null @@ -1,217 +0,0 @@ -from collections import Counter - -from models.player import GameContext, Item, Player, PlayerSnapshot - - -class Player4(Player): - def __init__(self, snapshot: PlayerSnapshot, ctx: GameContext) -> None: # noqa: F821 - super().__init__(snapshot, ctx) - self.ctx = ctx - - @staticmethod - def _is_pause(x: Item | None) -> bool: - return x is None - - @staticmethod - def _subjects_in(items: list[Item]) -> Counter[int]: - c = Counter() - for it in items: - c.update(it.subjects) - return c - - @staticmethod - def _take_preceding_block(history: list[Item | None], k: int) -> list[Item]: - """Take up to k preceding *non-pause* items, stopping if a pause is hit.""" - out: list[Item] = [] - for x in reversed(history): - if x is None: - break - out.append(x) - if len(out) == k: - break - out.reverse() - return out - - @staticmethod - def _take_window_before_pause(history: list[Item | None], k: int) -> list[Item]: - """ - When history ends with a pause, take up to k items *before that pause*, - not crossing an earlier pause. - """ - if not history or history[-1] is not None: - return [] - # walk left of the trailing pause - out: list[Item] = [] - # count = 0 - for count, x in enumerate(reversed(history[:-1])): - if x is None: - break - out.append(x) - if count + 1 == k: - break - out.reverse() - return out - - def _preference_bonus(self, item: Item) -> float: - """ - Average of (1 - k/|S|) over the item's subjects, where k is 1-based rank - in self.preferences (a permutation of all subjects). Unknown subjects -> 0. - Only called when history ends with a pause. - """ - S = len(self.preferences) - if S == 0 or not item.subjects: - return 0.0 - - def subj_bonus(s: int) -> float: - # worst-case (unknown) -> k = S -> 1 - S/S = 0 - k = self.preferences.index(s) + 1 if s in self.preferences else S - return 1.0 - (k / S) - - bonuses = [subj_bonus(s) for s in item.subjects] - return sum(bonuses) / len(bonuses) - - def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> float: - """ - Coherence over the previous up to 3 non-pause items: - - Hot-streak penalty: if any subject in `item` appears in *each* of the last 3 -> -1.0 - - Otherwise, reward based on total matched frequency across prev-3: - sum_match/len(item.subjects) - sum_match >= 4 -> +1.5 (e.g., 2+2) - sum_match == 3 -> +1.0 (e.g., 2+1) - sum_match == 2 -> +0.5 - sum_match == 1 -> +0.25 - else -> 0.0 - The window does not cross pauses. - """ - prev3 = self._take_preceding_block(history, 3) - if not prev3 or not item.subjects: - return 0.0 - - # Hot-streak penalty (subject present in all three previous items) - if len(prev3) == 3: - sets = [set(it.subjects) for it in prev3] - common_all3 = sets[0] & sets[1] & sets[2] - if any(s in common_all3 for s in item.subjects): - return -1.0 - """if sum_match >= 4: - elif sum_match == 3: - return 1.0 - elif sum_match == 2: - return 0.5 - elif sum_match == 1: - return 0.25 - else: - return 0.0""" - # Count subject frequencies across prev-3 - counts = Counter() - for it in prev3: - counts.update(it.subjects) - - # Total matched frequency across candidate subjects - sum_match = sum(counts.get(s, 0) for s in item.subjects) - return sum_match / len(item.subjects) - - def _preference_tiebreak_key(self, item: Item) -> tuple[int, int, str]: - """ - Lower is better. Uses: - 1) best (lowest) index among the item's subjects in self.preferences - 2) sum of indices (to prefer items whose subjects are overall higher-ranked) - 3) id string for deterministic ordering - Subjects not found in preferences rank after all known preferences. - """ - n = len(self.preferences) - idxs = [(self.preferences.index(s) if s in self.preferences else n) for s in item.subjects] - best = min(idxs) if idxs else n - total = sum(idxs) if idxs else n * 2 - return (best, total, str(item.id)) - - def _score_candidate(self, item: Item, history: list[Item | None]) -> float: - score = 0.0 - - # Repetition check: same item already in history? - already_seen = any(h is not None and h.id == item.id for h in history) - if already_seen: - # repeated items lose one point and contribute zero coherence/importance - score -= 1.0 - return score # early return: zero importance & coherence after first instance - - # Importance (only if not repeated) - score += float(item.importance) - - # Penalty: subject appeared in each of the previous 3 items? - prev3 = self._take_preceding_block(history, 3) - if len(prev3) == 3: - sets = [set(it.subjects) for it in prev3] - common_prev3 = sets[0].intersection(sets[1]).intersection(sets[2]) - if any(s in common_prev3 for s in item.subjects): - score -= 1.0 - - # Pause bonus: if most recent is a pause, and item has a subject not seen - # in the 5 turns prior to the pause (not crossing an earlier pause) -> +1 - if history and history[-1] is None: - window5 = self._take_window_before_pause(history, 5) - seen = set() - for it in window5: - seen.update(it.subjects) - - # Count how many candidate subjects are unseen in the last 5 turns - unseen_count = sum(1 for s in item.subjects if s not in seen) - - if unseen_count >= 2: - score += 2.0 # two or more unseen subjects → +2 - elif unseen_count >= 1: - score += 1.0 # exactly one unseen subject → +1 - - # Coherence relative to up to 3 preceding (no following at the end) - # Window cannot extend across a pause. - else: - context_items = self._take_preceding_block(history, 3) - subj_counts = self._subjects_in(context_items) - - if item.subjects and context_items: - # If any subject of I is never mentioned in CI -> -1 - if any(subj_counts.get(s, 0) == 0 for s in item.subjects): - score -= 1.0 - # If all subjects in I are mentioned at least twice in CI -> +1 - elif all(subj_counts.get(s, 0) >= 2 for s in item.subjects): - score += 1.0 - - score += self._preference_bonus(item) - score += self._coherence_prev3_score(item, history) - return score - - # ------------ selection - - def propose_item(self, history: list[Item | None]) -> Item | None: - """ - Pick the memory_bank item with the maximum score under the rules. - Tie-breaker: player preference order. - Returns None if no items available. - """ - if not self.memory_bank: - return None - - # Score all candidates - scored: list[tuple[float, Item]] = [ - (self._score_candidate(it, history), it) for it in self.memory_bank - ] - - # Find best score - if not scored: - return None - best_score = max(s for s, _ in scored) - # print(best_score) - # print(history) - max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence - threshold = max_possible * 0.3 - if len(history) != 0 and best_score < threshold: - return None - - # All with best score - tied = [it for s, it in scored if s == best_score] - - choice = tied[0] if len(tied) == 1 else min(tied, key=self._preference_tiebreak_key) - - # Track contribution if you care downstream - self.contributed_items.append(choice) - return choice From 18c1ca2901db375dd088aba9010575e750698e7f Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 22 Sep 2025 22:14:30 +0530 Subject: [PATCH 15/24] updated threshold --- players/player_4/player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index caa3752..2831336 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -203,10 +203,10 @@ def propose_item(self, history: list[Item | None]) -> Item | None: # print(best_score) # print(history) max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence - threshold = max_possible * 0.3 + threshold = 1 if len(history) != 0 and best_score < threshold: return None - + # All with best score tied = [it for s, it in scored if s == best_score] From b3d32a44834b8e0509bbe3a959985dd26c0d8c93 Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 22 Sep 2025 22:20:16 +0530 Subject: [PATCH 16/24] formatting --- players/player_4/player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index 2831336..f360667 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -202,11 +202,11 @@ def propose_item(self, history: list[Item | None]) -> Item | None: best_score = max(s for s, _ in scored) # print(best_score) # print(history) - max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence + #max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence threshold = 1 if len(history) != 0 and best_score < threshold: return None - + # All with best score tied = [it for s, it in scored if s == best_score] From 36f7408f9fb8f38a5076d33b3c61f2d68ceac153 Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Mon, 22 Sep 2025 22:22:13 +0530 Subject: [PATCH 17/24] formatting --- players/player_4/player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index f360667..9a0bf09 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -202,7 +202,7 @@ def propose_item(self, history: list[Item | None]) -> Item | None: best_score = max(s for s, _ in scored) # print(best_score) # print(history) - #max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence + # max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence threshold = 1 if len(history) != 0 and best_score < threshold: return None From 2fd16474796543ea702814ea37b8808cc58dfecb Mon Sep 17 00:00:00 2001 From: Akshat Bhandari Date: Wed, 24 Sep 2025 22:47:34 +0530 Subject: [PATCH 18/24] formatting --- players/player_4/player.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/players/player_4/player.py b/players/player_4/player.py index 9a0bf09..bfd76d9 100644 --- a/players/player_4/player.py +++ b/players/player_4/player.py @@ -74,13 +74,8 @@ def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> floa """ Coherence over the previous up to 3 non-pause items: - Hot-streak penalty: if any subject in `item` appears in *each* of the last 3 -> -1.0 - - Otherwise, reward based on total matched frequency across prev-3: + - Otherwise, reward based on total matched frequency across prev-3 for each subject: sum_match/len(item.subjects) - sum_match >= 4 -> +1.5 (e.g., 2+2) - sum_match == 3 -> +1.0 (e.g., 2+1) - sum_match == 2 -> +0.5 - sum_match == 1 -> +0.25 - else -> 0.0 The window does not cross pauses. """ prev3 = self._take_preceding_block(history, 3) @@ -93,15 +88,6 @@ def _coherence_prev3_score(self, item: Item, history: list[Item | None]) -> floa common_all3 = sets[0] & sets[1] & sets[2] if any(s in common_all3 for s in item.subjects): return -1.0 - """if sum_match >= 4: - elif sum_match == 3: - return 1.0 - elif sum_match == 2: - return 0.5 - elif sum_match == 1: - return 0.25 - else: - return 0.0""" # Count subject frequencies across prev-3 counts = Counter() for it in prev3: @@ -200,9 +186,7 @@ def propose_item(self, history: list[Item | None]) -> Item | None: if not scored: return None best_score = max(s for s, _ in scored) - # print(best_score) - # print(history) - # max_possible = 1.0 + 2.0 + 1.5 # importance + pause + coherence + threshold = 1 if len(history) != 0 and best_score < threshold: return None @@ -212,6 +196,6 @@ def propose_item(self, history: list[Item | None]) -> Item | None: choice = tied[0] if len(tied) == 1 else min(tied, key=self._preference_tiebreak_key) - # Track contribution if you care downstream + # Track contribution self.contributed_items.append(choice) return choice From b1bb4efb6f85072fd138ee471d605064ff67185c Mon Sep 17 00:00:00 2001 From: Jace Date: Wed, 24 Sep 2025 16:39:44 -0400 Subject: [PATCH 19/24] sim framework --- players/player_4/cuda_test.py | 6 - players/player_4/process_data.py | 348 ---------------------------- players/player_4/readme.txt | 5 - players/player_4/rules.txt | 24 -- players/player_4/scenarios.yaml | 59 +++++ players/player_4/sim.py | 170 +++++++++----- players/player_4/train.py | 374 ------------------------------- 7 files changed, 175 insertions(+), 811 deletions(-) delete mode 100644 players/player_4/cuda_test.py delete mode 100644 players/player_4/process_data.py delete mode 100644 players/player_4/readme.txt delete mode 100644 players/player_4/rules.txt create mode 100644 players/player_4/scenarios.yaml delete mode 100644 players/player_4/train.py diff --git a/players/player_4/cuda_test.py b/players/player_4/cuda_test.py deleted file mode 100644 index 02ae371..0000000 --- a/players/player_4/cuda_test.py +++ /dev/null @@ -1,6 +0,0 @@ -import torch - -print(torch.cuda.is_available()) -print(torch.cuda.device_count()) -print(torch.cuda.current_device()) -print(torch.cuda.get_device_name(0)) \ No newline at end of file diff --git a/players/player_4/process_data.py b/players/player_4/process_data.py deleted file mode 100644 index 6553653..0000000 --- a/players/player_4/process_data.py +++ /dev/null @@ -1,348 +0,0 @@ -#!/usr/bin/env python3 -""" -process.py - Convert raw game JSON files to processed training data - -Usage: python process.py -- Reads from raw/batch_*.json -- Automatically identifies Player4 by looking at speaker_name -- Extracts training examples ONLY when Player4 acts (proposes item or passes) -- Game state includes ONLY Player4's memory bank and preferences (realistic) -- Saves to processed/batch_*.json -- Skips games where Player4 never speaks -""" - -import json -import os -from pathlib import Path -from typing import Dict, List, Any, Optional -from dataclasses import dataclass, asdict - -@dataclass -class ProcessedItem: - """Processed item representation""" - subjects: List[int] - importance: float - item_id: str - -@dataclass -class ProcessedGameState: - """Game state at a specific turn - Player4's view only""" - turn_number: int - conversation_history: List[ProcessedItem] - my_memory_bank: List[ProcessedItem] # Only Player4's memory bank - my_preferences: List[int] # Only Player4's preferences - conversation_length: int - pause_count: int - game_over: bool - -@dataclass -class ProcessedAction: - """Action taken by a player""" - player_id: str - action_type: str # "propose_item", "pass" - item_id: Optional[str] # None if pass - item_subjects: Optional[List[int]] # None if pass - item_importance: Optional[float] # None if pass - -@dataclass -class TrainingExample: - """Training example for Player4""" - game_id: str - turn_number: int - state_before: ProcessedGameState - our_action: ProcessedAction - immediate_reward: float - game_over: bool - -@dataclass -class ProcessedGame: - """Complete processed game - focused on Player4 training data""" - game_id: str - player4_id: str - training_examples: List[TrainingExample] # Training data for Player4 - final_scores: Dict[str, Dict[str, float]] - game_metadata: Dict[str, Any] - -class GameDataProcessor: - """Processes raw game JSON into structured format""" - - def __init__(self): - self.processed_games = [] - - def find_player4_id(self, raw_game: Dict) -> Optional[str]: - """ - First pass: Find Player4's ID by looking at speaker_name - Returns None if Player4 never speaks - """ - for turn_data in raw_game['turn_impact']: - speaker_name = turn_data.get('speaker_name', '') - if speaker_name == 'Player4': - return turn_data['speaker_id'] - return None - - def process_raw_game(self, raw_game: Dict) -> Optional[ProcessedGame]: - """Convert raw game JSON to ProcessedGame""" - - # First pass: Find Player4's ID - player4_id = self.find_player4_id(raw_game) - if player4_id is None: - print(" Skipping game - Player4 never speaks") - return None - - print(f" Found Player4 ID: {player4_id[:8]}...") - - # Extract player information - players = {p['id']: p for p in raw_game['scores']['player_scores']} - - # Check if Player4 is in the final scores - if player4_id not in players: - print(f" Skipping game - Player4 {player4_id} not found in player scores") - return None - - # Second pass: Process each turn, but only extract training data when Player4 acts - training_examples = [] - conversation_history = [] - - for turn_data in raw_game['turn_impact']: - # Player4 makes a decision every turn: either propose an item or pass - # We create training examples for every turn where Player4 had to decide - player4_action = None - - if player4_id in turn_data['proposals']: - # Player4 chose to propose an item - proposal = turn_data['proposals'][player4_id] - player4_action = ProcessedAction( - player_id=player4_id, - action_type="propose_item", - item_id=proposal['id'], - item_subjects=proposal['subjects'], - item_importance=proposal['importance'] - ) - else: - # Player4 chose to pass - # This happens whether others proposed or no one proposed - player4_action = ProcessedAction( - player_id=player4_id, - action_type="pass", - item_id=None, - item_subjects=None, - item_importance=None - ) - - # Create game state before this turn - Player4's view only - game_state_before = ProcessedGameState( - turn_number=turn_data['turn'], - conversation_history=[ - ProcessedItem( - subjects=item['subjects'], - importance=item['importance'], - item_id=item['id'] - ) for item in conversation_history - ], - my_memory_bank=[ - ProcessedItem( - subjects=item['subjects'], - importance=item['importance'], - item_id=item['id'] - ) for item in players[player4_id]['memory_bank'] - ], - my_preferences=players[player4_id]['preferences'], - conversation_length=raw_game['scores']['conversation_length'], - pause_count=raw_game['scores']['pauses'], - game_over=turn_data['is_over'] - ) - - # Update conversation history for next turn - if turn_data.get('item'): - conversation_history.append(turn_data['item']) - - # Calculate immediate reward for Player4's decision - immediate_reward = 0.0 - if turn_data['speaker_id'] == player4_id: - immediate_reward = turn_data['score_impact']['total'] - elif player4_action.action_type == "propose_item": - immediate_reward = self._calculate_individual_bonus( - turn_data['item']['subjects'], - players[player4_id]['preferences'] - ) - else: - other_player_spoken_item_subjects = turn_data.get('item', {}).get('subjects', []) - if other_player_spoken_item_subjects: - immediate_reward = self._calculate_individual_bonus( - other_player_spoken_item_subjects, - players[player4_id]['preferences'] - ) - else: - immediate_reward = 0.0 - - training_example = TrainingExample( - game_id=f"game_{len(self.processed_games)}", - turn_number=turn_data['turn'], - state_before=game_state_before, - our_action=player4_action, - immediate_reward=immediate_reward, - game_over=turn_data['is_over'] - ) - - training_examples.append(training_example) - - - # Create final processed game - processed_game = ProcessedGame( - game_id=f"game_{len(self.processed_games)}", - player4_id=player4_id, - training_examples=training_examples, - final_scores={ - pid: player_data['scores'] - for pid, player_data in players.items() - }, - game_metadata={ - 'player4_id': player4_id, - 'player4_training_examples': len(training_examples), - 'conversation_length': raw_game['scores']['conversation_length'], - 'total_pauses': raw_game['scores']['pauses'], - 'num_players': len(players), - 'shared_score_breakdown': raw_game['score_breakdown'] - } - ) - - return processed_game - - def _calculate_individual_bonus(self, subjects: List[int], preferences: List[int]) -> float: - """Calculate individual preference bonus for subjects""" - if not subjects: - return 0.0 - - bonus = 0.0 - for subject in subjects: - if subject in preferences: - rank = preferences.index(subject) - bonus += 1.0 - (rank / len(preferences)) - return bonus / len(subjects) - -def process_batch_file(input_path: Path, output_path: Path) -> bool: - """Process a single batch file""" - try: - print(f"Processing {input_path.name}...") - - # Load raw data - with open(input_path, 'r') as f: - raw_data = json.load(f) - - # Initialize processor - processor = GameDataProcessor() - - # Process each game in the batch - processed_games = [] - skipped_games = 0 - - if isinstance(raw_data, list): - # Multiple games in one file - for i, raw_game in enumerate(raw_data): - try: - processed_game = processor.process_raw_game(raw_game) - if processed_game is not None: - processed_game.game_id = f"{input_path.stem}_game_{i}" - processed_games.append(asdict(processed_game)) - else: - skipped_games += 1 - except Exception as e: - print(f" Error processing game {i} in {input_path.name}: {e}") - skipped_games += 1 - else: - # Single game in file - try: - processed_game = processor.process_raw_game(raw_data) - if processed_game is not None: - processed_game.game_id = input_path.stem - processed_games.append(asdict(processed_game)) - else: - skipped_games += 1 - except Exception as e: - print(f" Error processing {input_path.name}: {e}") - return False - - if not processed_games: - print(f" No valid games found in {input_path.name} (Player4 never spoke in any games)") - return False - - # Save processed data - output_path.parent.mkdir(parents=True, exist_ok=True) - with open(output_path, 'w') as f: - json.dump(processed_games, f, indent=2) - - total_training_examples = sum(len(game['training_examples']) for game in processed_games) - print(f" Success: {len(processed_games)} games, {total_training_examples} training examples") - if skipped_games > 0: - print(f" Skipped: {skipped_games} games (Player4 didn't speak)") - - return True - - except Exception as e: - print(f" Failed to process {input_path.name}: {e}") - return False - -def main(): - """Main processing function""" - raw_dir = Path("players/player_4/raw") - processed_dir = Path("players/player_4/processed") - - # Check if raw directory exists - if not raw_dir.exists(): - print(f"Error: {raw_dir} directory not found!") - print("Please create the 'raw' directory and place your batch_*.json files there.") - return - - # Find all batch files - batch_files = sorted(raw_dir.glob("batch_*.json")) - - if not batch_files: - print(f"No batch_*.json files found in {raw_dir}/") - print("Expected files like: batch_1.json, batch_2.json, etc.") - return - - print(f"Found {len(batch_files)} batch files to process") - print("Looking for Player4 in each game...") - - # Process each batch file - success_count = 0 - for batch_file in batch_files: - output_file = processed_dir / batch_file.name - - if process_batch_file(batch_file, output_file): - success_count += 1 - - print(f"\nProcessing complete!") - print(f"Successfully processed: {success_count}/{len(batch_files)} files") - print(f"Processed files saved to: {processed_dir}/") - - # Show summary - if success_count > 0: - print(f"\nProcessed file structure for Player4:") - total_training_examples = 0 - total_games = 0 - for processed_file in sorted(processed_dir.glob("batch_*.json")): - try: - with open(processed_file, 'r') as f: - data = json.load(f) - if isinstance(data, list): - game_count = len(data) - training_count = sum(len(game.get('training_examples', [])) for game in data) - else: - game_count = 1 - training_count = len(data.get('training_examples', [])) - total_training_examples += training_count - total_games += game_count - print(f" {processed_file.name}: {game_count} games, {training_count} training examples") - except: - print(f" {processed_file.name}: (error reading)") - - print(f"\nTotal: {total_games} games processed, {total_training_examples} training examples extracted") - print(f"Ready for RL training!") - - if total_training_examples == 0: - print(f"\nWARNING: No training examples found for Player4") - print("Player4 may not be speaking in any games, or there may be an issue with the data format.") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/players/player_4/readme.txt b/players/player_4/readme.txt deleted file mode 100644 index 2b2303f..0000000 --- a/players/player_4/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -To use: - -uv run sim.py -uv run process_data.py -uv run train.py players/player_4/processed \ No newline at end of file diff --git a/players/player_4/rules.txt b/players/player_4/rules.txt deleted file mode 100644 index 0ed3344..0000000 --- a/players/player_4/rules.txt +++ /dev/null @@ -1,24 +0,0 @@ -Project 1: Conversation -We'll be simulating a group conversation in which you will be coding players whose goal is to have a "good" interaction of ideas. There are some shared properties of conversations that make them better for all participants. Other properties of conversations are sought by individual participants. At the end of the conversation, the whole interaction will be measured according to the collective and individual scoring functions, which are added together for each participant. For some conversations, there will be one or more random players included who may steer the conversation in unpredictable ways. - -There are P players, and every player has a memory bank of B newsworthy items that they could contribute to the conversation. Half of the items are about a single subject, while the other half are about two subjects. Every item also has an importance, which is a number uniformly generated between 0 and 1. There are S subjects in total, and each player's memory bank is generated at random. The conversation has a fixed length L that everybody knows in advance. - -A conversation is a sequence of items contributed by players. On any turn, each player may propose to share an item. If multiple players attempt to contribute an item, the simulator will do the following: If the player currently talking wants to contribute again, they will be chosen with probability 0.5. If the player currently talking is not chosen, then the simulator will choose a player at random from among those who propose an item and have so far contributed the smallest number of items among that group. That way, each player can (if they wish) contribute approximately as often as other players over the long run. If nobody contributes anything on a given turn, then the sequence is filled by a "pause". If there are three consecutive pauses, then the conversation ends prematurely. - -The shared goals of a conversation are as follows: - -Coherence -For every item I, the (up to) 3 preceding items and (up to) 3 following items are collected into a set CI of context items. If a subject of I is never mentioned in CI then one point is lost from the final score. If all subjects in I are mentioned at least twice in CI then one point is added to the final score. The window defining CI does not extend beyond the start of the conversation or any pauses. -Importance -The total importance of all items in the final item sequence is added to the shared score. (Everybody agrees about the importance of each item.) -Nonrepetition -An item that is repeated has zero importance and does not contribute to the coherence score after the first instance of the item. -Freshness -Immediately after a pause, an item with a subject that was not previously present in the 5 turns prior to the pause gets a point added to the final score. An item with two novel subjects of this sort gets two bonus points. Repeated items do not get freshness points. -Nonmonotonousness -An item with a subject that was also present in each of the previous three items leads to the loss of a point. Also, repeated items lose one point, since the audience has already heard that news. -Individual goals depend on each player's preferred topics of conversation. The simulator gives each player a random permutation of the S subjects that represents the ranked preference of that player. An item with a subject that is of rank k for that player achieves a bonus of 1-k/|S| where |S| is the total number of possible subjects. An item with two subjects gives the player a bonus corresponing to the average of the two individual bonuses. Players know their own ranking, but not the ranking of other players. - -At the end of the game, the total score for each player is divided by L, resulting in an overall conversation quality that is the final metric by which players will be compared. Even though the shared goals lead to scores for all participants, such scores can still be used to compare players. For example, how does the global score change when one player is left out? How does the global score vary when all participants are running the same group's code. Make sure to code your player in an encapsulated fashion, so that several instances of the same player can be instatiated without sharing information across instances. We'll run a variety of configurations for various combinations of parameters as part of a tournament at the end of the project. The precise configurations for the tournament will be discussed in class. The simulator will accept a random number seed to facilitate repeatable simulations. - -Your primary goal is to code a player that achieves the highest conversation quality. Additionally, I would like to see some analysis of the resulting conversations. For example, how do long conversations compare to short ones? Are there emergent patterns in the conversations? How badly do one (or more) random players affect a conversation's quality? \ No newline at end of file diff --git a/players/player_4/scenarios.yaml b/players/player_4/scenarios.yaml new file mode 100644 index 0000000..c6da243 --- /dev/null +++ b/players/player_4/scenarios.yaml @@ -0,0 +1,59 @@ +scenarios: + - name: "test" + runs: 10 + args: + subjects: 20 + memory_size: 10 + length: 10 + players: + - ["p4", 5] + - ["prp", 1] + + - name: "20s_10m_10l_2p_allprp" + runs: 100 + args: + subjects: 20 + memory_size: 10 + length: 10 + players: + - ["prp", 2] + - name: "20s_10m_10l_6p_allprp" + runs: 100 + args: + subjects: 20 + memory_size: 10 + length: 10 + players: + - ["prp", 6] + - name: "20s_10m_10l_10p_allprp" + runs: 100 + args: + subjects: 20 + memory_size: 10 + length: 10 + players: + - ["prp", 10] + - name: "20s_10m_10l_11p_allprp" + runs: 100 + args: + subjects: 20 + memory_size: 10 + length: 10 + players: + - ["prp", 11] + - name: "20s_10m_10l_12p_allprp" + runs: 100 + args: + subjects: 20 + memory_size: 10 + length: 10 + players: + - ["prp", 12] + - name: "20s_10m_10l_20p_allprp" + runs: 100 + args: + subjects: 20 + memory_size: 10 + length: 10 + players: + - ["prp", 20] \ No newline at end of file diff --git a/players/player_4/sim.py b/players/player_4/sim.py index 95e3825..db361f6 100644 --- a/players/player_4/sim.py +++ b/players/player_4/sim.py @@ -1,65 +1,127 @@ import asyncio import json -import aiofiles -from collections import deque -import os +import yaml +from datetime import datetime +from pathlib import Path +import multiprocessing +import time +import random -class StreamingSimulator: - def __init__(self, total_games=512, buffer_size=128, concurrent_games=32): - self.buffer = deque(maxlen=buffer_size) - self.total_games = total_games - self.total_games_so_far = 0 - self.concurrent_games = concurrent_games - - async def run_continuous(self): - """Continuously generate games and save batches""" - while True: - # Run game asynchronously - tasks = [self.run_game_async() for _ in range(self.concurrent_games)] - results = await asyncio.gather(*tasks) - for game_data in results: - self.buffer.append(game_data) - self.total_games_so_far += 1 - print(f'Total games simulated: {self.total_games_so_far}') - - # Save batch periodically - if len(self.buffer) >= self.buffer.maxlen: - await self.save_batch() - print(f'Saved batch of {self.buffer.maxlen} games.') - self.buffer.clear() - - # Stop if we've reached the total number of games - if self.total_games_so_far >= self.total_games: - break - - async def run_game_async(self): - """Run game simulator asynchronously""" +async def run_simulation(scenario_name, args, run_index, semaphore): + """Run a single simulation""" + cmd = ['uv', 'run', 'main.py'] + + # Generate random seed for this run + run_seed = random.randint(0, 2**31 - 1) + cmd.extend(['--seed', str(run_seed)]) + + # Add simple arguments + for key in ['subjects', 'memory_size', 'length']: + if key in args: + cmd.extend([f'--{key}', str(args[key])]) + + # Add player arguments + if 'players' in args: + for player_type, count in args['players']: + cmd.extend(['--player', player_type, str(count)]) + + async with semaphore: proc = await asyncio.create_subprocess_exec( - 'uv', - 'run', - 'main.py', - '--subjects', '20', - '--memory_size', '10', - '--length', '100', - '--player', 'prp', '9', - '--player', 'p4', '1', + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) - stdout, stderr = await proc.communicate() - # Remove everything from stdout before the first { character + stdout, _ = await proc.communicate() + + # Parse JSON output json_output = "{" + stdout.decode().split("{", 1)[-1] - return json.loads(json_output) - - async def save_batch(self): - """Save current buffer to file""" - filename = f'players/player_4/raw/batch_{self.total_games_so_far // self.buffer.maxlen}.json' - os.makedirs(os.path.dirname(filename), exist_ok=True) - async with aiofiles.open(filename, 'w') as f: - await f.write(json.dumps(list(self.buffer))) - print(f'Saved batch to {filename}') + result = json.loads(json_output) + + # Filter scores to only keep player_scores with id and scores fields + filtered_scores = {} + if 'scores' in result: + if 'player_scores' in result['scores']: + filtered_scores['player_scores'] = [ + {'id': player['id'], 'scores': player['scores']} + for player in result['scores']['player_scores'] + ] + if "shared_score_breakdown" in result['scores']: + filtered_scores['shared_score_breakdown'] = result['scores']['shared_score_breakdown'] + + # Keep only turn_impact and filtered scores + filtered_result = { + # 'turn_impact': result.get('turn_impact'), + 'scores': filtered_scores + } + + return { + 'scenario': scenario_name, + 'run': run_index, + 'seed': run_seed, + 'args': args, + 'result': filtered_result + } + +async def run_scenario(scenario, semaphore): + """Run all iterations of a single scenario""" + name = scenario['name'] + runs = scenario['runs'] + + print(f"Starting scenario '{name}' ({runs} runs)...") + start = time.time() + + # Run all iterations of this scenario + results = await asyncio.gather(*[ + run_simulation(name, scenario['args'], i, semaphore) + for i in range(runs) + ]) + + elapsed = time.time() - start + print(f" Completed '{name}': {runs} runs in {elapsed:.1f}s ({elapsed/runs}s per run)") + + return results -streaming_simulator = StreamingSimulator() -asyncio.run(streaming_simulator.run_continuous()) +async def main(): + import sys + + # Load config + config_file = sys.argv[1] if len(sys.argv) > 1 else 'players/player_4/scenarios.yaml' + with open(config_file) as f: + scenarios = yaml.safe_load(f)['scenarios'] + + # Create all tasks + tasks = [] + for scenario in scenarios: + for i in range(scenario['runs']): + tasks.append((scenario['name'], scenario['args'], i)) + + print(f"Running {len(tasks)} simulations on {multiprocessing.cpu_count()*2} parallel processes...") + start_time = time.time() + + # Run with concurrency limit + semaphore = asyncio.Semaphore(multiprocessing.cpu_count() * 2) + + # Run each scenario and flatten results + all_results = [] + for scenario in scenarios: + scenario_results = await run_scenario(scenario, semaphore) + all_results.extend(scenario_results) + + elapsed = time.time() - start_time + print(f"\nAll scenarios complete!") + + # Save results + output_dir = Path('players/player_4/results') + output_dir.mkdir(exist_ok=True) + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output_file = output_dir / f'results_{timestamp}.json' + + with open(output_file, 'w') as f: + json.dump(all_results, f, indent=2) + + print(f"Saved {len(all_results)} results to {output_file}") +if __name__ == '__main__': + asyncio.run(main()) \ No newline at end of file diff --git a/players/player_4/train.py b/players/player_4/train.py deleted file mode 100644 index 3486639..0000000 --- a/players/player_4/train.py +++ /dev/null @@ -1,374 +0,0 @@ -import json -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.optim as optim -import numpy as np -from pathlib import Path -import sys -from typing import List, Dict, Tuple, Optional -from dataclasses import dataclass -import random -from collections import deque - -GAME_MAX_SUBJECTS = 20 -GAME_MAX_MEMORY_BANK = 10 -NETWORK_CONV_HISTORY_ITEMS = 5 - -@dataclass -class TrainingExample: - """Single training example from processed data""" - game_id: str - turn_number: int - state_tensor: torch.Tensor - action_index: int - reward: float - next_state_tensor: Optional[torch.Tensor] - done: bool - -class StateEncoder: - """Converts JSON state to fixed-size tensor representation""" - - def __init__(self, max_subjects=GAME_MAX_SUBJECTS, max_memory_items=GAME_MAX_MEMORY_BANK, max_conversation_items=NETWORK_CONV_HISTORY_ITEMS): - self.max_subjects = max_subjects - self.max_memory_items = max_memory_items - self.max_conversation_items = max_conversation_items - - def encode_state(self, state_json: Dict) -> torch.Tensor: - """Convert state JSON to tensor""" - features = [] - - # 1. Encode conversation history - conv_features = torch.zeros(self.max_conversation_items, self.max_subjects + 1) # +1 for importance - conversation = state_json['conversation_history'] - for i, item in enumerate(conversation[-self.max_conversation_items:]): - # One-hot encode subjects - for subject in item['subjects']: - if subject < self.max_subjects: - conv_features[i, subject] = 1.0 - # Add importance - conv_features[i, -1] = item['importance'] - - # Flatten conversation features - features.append(conv_features.flatten()) - - # 2. Encode memory bank - memory_features = torch.zeros(self.max_memory_items, self.max_subjects + 1) - memory_bank = state_json['my_memory_bank'] - for i, item in enumerate(memory_bank[:self.max_memory_items]): - # One-hot encode subjects - for subject in item['subjects']: - if subject < self.max_subjects: - memory_features[i, subject] = 1.0 - # Add importance - memory_features[i, -1] = item['importance'] - - # Flatten memory features - features.append(memory_features.flatten()) - - # 3. Encode preferences (ranking of subjects) - pref_features = torch.zeros(self.max_subjects) - preferences = state_json['my_preferences'] - for i, subject in enumerate(preferences): - if subject < self.max_subjects: - # Higher rank = higher value (inverse of position) - pref_features[subject] = 1.0 - (i / len(preferences)) - features.append(pref_features) - - # 4. Encode game metadata - metadata = torch.tensor([ - state_json['turn_number'] / state_json['conversation_length'], # Progress - len(state_json['conversation_history']) / self.max_conversation_items, # Conv fullness - state_json['pause_count'] / 3.0, # Normalized pauses - 1.0 if state_json['game_over'] else 0.0 # Game over flag - ]) - features.append(metadata) - - # Concatenate all features - return torch.cat(features) - - def get_feature_size(self) -> int: - """Calculate total feature vector size""" - conv_size = self.max_conversation_items * (self.max_subjects + 1) - memory_size = self.max_memory_items * (self.max_subjects + 1) - pref_size = self.max_subjects - metadata_size = 4 - return conv_size + memory_size + pref_size + metadata_size - -class QNetwork(nn.Module): - """Q-Network for action value estimation""" - - def __init__(self, state_size: int, max_actions: int, hidden_size: int = 512): - super().__init__() - self.max_actions = max_actions - - self.network = nn.Sequential( - nn.Linear(state_size, hidden_size), - nn.ReLU(), - nn.Dropout(0.2), - nn.Linear(hidden_size, hidden_size), - nn.ReLU(), - nn.Dropout(0.2), - nn.Linear(hidden_size, hidden_size // 2), - nn.ReLU(), - nn.Linear(hidden_size // 2, max_actions) - ) - - def forward(self, state: torch.Tensor, num_actions: int) -> torch.Tensor: - """ - Forward pass - returns Q-values for available actions - Args: - state: State tensor - num_actions: Number of available actions (memory_bank_size + 1) - """ - q_values = self.network(state) - # Only return Q-values for available actions - return q_values[:num_actions] - -class DataLoader: - """Loads and processes training data from JSON files""" - - def __init__(self, data_directory: str): - self.data_directory = Path(data_directory) - self.state_encoder = StateEncoder() - - def load_all_data(self) -> List[TrainingExample]: - """Load all training examples from batch files""" - training_examples = [] - - # Find all batch files - batch_files = sorted(self.data_directory.glob("batch_*.json")) - print(f"Found {len(batch_files)} batch files") - - for batch_file in batch_files: - print(f"Loading {batch_file.name}...") - try: - with open(batch_file, 'r') as f: - games_data = json.load(f) - - # Process each game - for game_data in games_data: - game_examples = self._process_game(game_data) - training_examples.extend(game_examples) - - except Exception as e: - print(f"Error loading {batch_file}: {e}") - - print(f"Loaded {len(training_examples)} training examples") - return training_examples - - def _process_game(self, game_data: Dict) -> List[TrainingExample]: - """Process a single game's training examples""" - examples = [] - training_data = game_data['training_examples'] - - for i, example in enumerate(training_data): - # Encode current state - state_tensor = self.state_encoder.encode_state(example['state_before']) - - # Convert action to index - action_index = self._action_to_index(example['our_action'], example['state_before']) - - # Get next state (if available) - next_state_tensor = None - if i + 1 < len(training_data): - next_example = training_data[i + 1] - # Only use as next state if consecutive turns - if next_example['turn_number'] == example['turn_number'] + 1: - next_state_tensor = self.state_encoder.encode_state(next_example['state_before']) - - training_example = TrainingExample( - game_id=example.get('game_id', 'unknown'), - turn_number=example['turn_number'], - state_tensor=state_tensor, - action_index=action_index, - reward=example['immediate_reward'], - next_state_tensor=next_state_tensor, - done=example['game_over'] - ) - - examples.append(training_example) - - return examples - - def _action_to_index(self, action: Dict, state: Dict) -> int: - """Convert action to index in memory bank""" - if action['action_type'] == 'pass': - # Pass action is index = len(memory_bank) - return len(state['my_memory_bank']) - else: - # Find which memory bank item was proposed - item_id = action['item_id'] - for i, memory_item in enumerate(state['my_memory_bank']): - if memory_item['item_id'] == item_id: - return i - # Fallback to pass if item not found - return len(state['my_memory_bank']) - -class QLearningTrainer: - """Q-Learning trainer""" - - def __init__(self, state_size: int, max_actions: int, lr: float = 1e-4, gamma: float = 0.95): - self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - print(f"Using device: {self.device}") - - self.q_network = QNetwork(state_size, max_actions).to(self.device) - self.target_network = QNetwork(state_size, max_actions).to(self.device) - self.optimizer = optim.Adam(self.q_network.parameters(), lr=lr) - - self.gamma = gamma - self.update_target_every = 1000 # Update target network every N steps - self.training_step = 0 - - # Copy initial weights to target network - self.target_network.load_state_dict(self.q_network.state_dict()) - - def train(self, training_examples: List[TrainingExample], epochs: int = 50, batch_size: int = 32): - """Train the Q-network""" - print(f"Training for {epochs} epochs with batch size {batch_size}") - - # Filter examples that have next states (for proper Q-learning) - valid_examples = [ex for ex in training_examples if ex.next_state_tensor is not None] - print(f"Using {len(valid_examples)} examples with next states for Q-learning") - - for epoch in range(epochs): - # Shuffle training data - random.shuffle(valid_examples) - - total_loss = 0.0 - num_batches = 0 - - # Process in batches - for i in range(0, len(valid_examples), batch_size): - batch = valid_examples[i:i + batch_size] - loss = self._train_batch(batch) - total_loss += loss - num_batches += 1 - - # Update target network - if self.training_step % self.update_target_every == 0: - self.target_network.load_state_dict(self.q_network.state_dict()) - - self.training_step += 1 - - avg_loss = total_loss / num_batches if num_batches > 0 else 0 - print(f"Epoch {epoch + 1}/{epochs}, Average Loss: {avg_loss:.4f}") - - def _train_batch(self, batch: List[TrainingExample]) -> float: - """Train on a single batch""" - if not batch: - return 0.0 - - # Prepare batch data - states = torch.stack([ex.state_tensor for ex in batch]).to(self.device) - actions = torch.tensor([ex.action_index for ex in batch]).to(self.device) - rewards = torch.tensor([ex.reward for ex in batch], dtype=torch.float).to(self.device) - next_states = torch.stack([ex.next_state_tensor for ex in batch]).to(self.device) - dones = torch.tensor([ex.done for ex in batch], dtype=torch.bool).to(self.device) - - # Current Q-values - current_q_values = [] - for i, ex in enumerate(batch): - memory_size = len(ex.state_tensor) # Simplified - should extract actual memory size - num_actions = GAME_MAX_MEMORY_BANK + 1 - q_vals = self.q_network(states[i:i+1], num_actions) - # print(f"Q-values for example {i}: {q_vals}") - # print(f"Action taken: {actions[i]}, Q-value: {q_vals[0, actions[i]].item()}") - current_q_values.append(q_vals[0, actions[i]]) - - current_q_values = torch.stack(current_q_values) - - # Target Q-values using target network - with torch.no_grad(): - target_q_values = [] - for i, ex in enumerate(batch): - if dones[i]: - target_q_values.append(rewards[i]) - else: - num_actions = GAME_MAX_MEMORY_BANK + 1 - next_q_vals = self.target_network(next_states[i:i+1], num_actions) - target = rewards[i] + self.gamma * next_q_vals.max() - target_q_values.append(target) - - target_q_values = torch.stack(target_q_values) - - # Compute loss - loss = F.mse_loss(current_q_values, target_q_values) - - # Backpropagation - self.optimizer.zero_grad() - loss.backward() - self.optimizer.step() - - return loss.item() - - def save_model(self, path: str): - """Save trained model""" - torch.save({ - 'q_network_state_dict': self.q_network.state_dict(), - 'target_network_state_dict': self.target_network.state_dict(), - 'optimizer_state_dict': self.optimizer.state_dict(), - 'training_step': self.training_step - }, path) - print(f"Model saved to {path}") - - def load_model(self, path: str): - """Load trained model""" - checkpoint = torch.load(path, map_location=self.device) - self.q_network.load_state_dict(checkpoint['q_network_state_dict']) - self.target_network.load_state_dict(checkpoint['target_network_state_dict']) - self.optimizer.load_state_dict(checkpoint['optimizer_state_dict']) - self.training_step = checkpoint.get('training_step', 0) - print(f"Model loaded from {path}") - -def main(): - """Main training function""" - if len(sys.argv) != 2: - print("Usage: uv run players/player_4/train.py ") - print("Example: uv run players/player_4/train.py players/player_4/processed/") - return - - data_directory = sys.argv[1] - - # Check if directory exists - if not Path(data_directory).exists(): - print(f"Error: Directory {data_directory} not found!") - return - - print("=" * 60) - print("Q-LEARNING TRAINING FOR CONVERSATION GAME") - print("=" * 60) - - # Step 1: Load and process data - print("\n1. LOADING DATA...") - data_loader = DataLoader(data_directory) - training_examples = data_loader.load_all_data() - - if not training_examples: - print("No training examples found!") - return - - # Step 2: Initialize trainer - print("\n2. INITIALIZING Q-NETWORK...") - state_size = data_loader.state_encoder.get_feature_size() - max_actions = GAME_MAX_MEMORY_BANK + 1 # + 1 for pass action - - print(f"State vector size: {state_size}") - print(f"Maximum actions: {max_actions}") - - trainer = QLearningTrainer(state_size, max_actions, lr=1e-5, gamma=0.95) - - # Step 3: Train the model - print("\n3. TRAINING Q-NETWORK...") - trainer.train(training_examples, epochs=100, batch_size=32) - - # Step 4: Save the model - print("\n4. SAVING MODEL...") - model_path = Path(data_directory).parent / "trained_qnetwork.pth" - trainer.save_model(str(model_path)) - - print(f"\n✓ Training complete! Model saved to: {model_path}") - print("\nYou can now use this trained model to play games!") - -if __name__ == "__main__": - main() \ No newline at end of file From 694e956455c7416199740735a42d0b0f812663b8 Mon Sep 17 00:00:00 2001 From: Jace Date: Wed, 24 Sep 2025 16:40:41 -0400 Subject: [PATCH 20/24] remove torch dep --- pyproject.toml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f05754f..46005df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,22 +9,9 @@ dependencies = [ "openai", "ruff>=0.12.8", "aiofiles", - "torch", "numpy", ] -[[tool.uv.index]] -name = "pytorch-cu128" -url = "https://download.pytorch.org/whl/cu128" -explicit = true -[tool.uv.sources] -torch = [ - { index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -torchvision = [ - { index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] - [tool.ruff] line-length = 100 From 891eee0be57e5bdaaf4811781eed6b76bf4f49a6 Mon Sep 17 00:00:00 2001 From: Jace Date: Wed, 24 Sep 2025 16:40:57 -0400 Subject: [PATCH 21/24] sync --- uv.lock | 298 -------------------------------------------------------- 1 file changed, 298 deletions(-) diff --git a/uv.lock b/uv.lock index 2cbd5b0..5979a04 100644 --- a/uv.lock +++ b/uv.lock @@ -65,8 +65,6 @@ dependencies = [ { name = "openai" }, { name = "pygame" }, { name = "ruff" }, - { name = "torch", version = "2.8.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "torch", version = "2.8.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] [package.dev-dependencies] @@ -81,8 +79,6 @@ requires-dist = [ { name = "openai" }, { name = "pygame", specifier = ">=2.6.1" }, { name = "ruff", specifier = ">=0.12.8" }, - { name = "torch", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "torch", marker = "sys_platform == 'linux' or sys_platform == 'win32'", index = "https://download.pytorch.org/whl/cu128" }, ] [package.metadata.requires-dev] @@ -97,24 +93,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] -[[package]] -name = "filelock" -version = "3.19.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, -] - -[[package]] -name = "fsspec" -version = "2025.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" }, -] - [[package]] name = "h11" version = "0.16.0" @@ -161,18 +139,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - [[package]] name = "jiter" version = "0.10.0" @@ -209,52 +175,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, ] -[[package]] -name = "markupsafe" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, -] - -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "networkx" -version = "3.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, -] - [[package]] name = "numpy" version = "2.3.3" @@ -307,132 +227,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, ] -[[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.27.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/5b/4e4fff7bad39adf89f735f2bc87248c81db71205b62bcc0d5ca5b606b3c3/nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039", size = 322364134, upload-time = "2025-06-03T21:58:04.013Z" }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, -] - [[package]] name = "openai" version = "1.107.0" @@ -535,15 +329,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, ] -[[package]] -name = "setuptools" -version = "80.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, -] - [[package]] name = "sniffio" version = "1.3.1" @@ -553,77 +338,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "torch" -version = "2.8.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "sys_platform != 'linux' and sys_platform != 'win32'", -] -dependencies = [ - { name = "filelock", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "fsspec", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "jinja2", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "networkx", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "setuptools", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "sympy", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, - { name = "typing-extensions", marker = "sys_platform != 'linux' and sys_platform != 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/69/8b7b13bba430f5e21d77708b616f767683629fc4f8037564a177d20f90ed/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767", size = 73915128, upload-time = "2025-08-06T14:54:34.769Z" }, - { url = "https://files.pythonhosted.org/packages/04/6e/650bb7f28f771af0cb791b02348db8b7f5f64f40f6829ee82aa6ce99aabe/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211", size = 73632395, upload-time = "2025-08-06T14:55:28.645Z" }, -] - -[[package]] -name = "torch" -version = "2.8.0+cu128" -source = { registry = "https://download.pytorch.org/whl/cu128" } -resolution-markers = [ - "sys_platform == 'linux' or sys_platform == 'win32'", -] -dependencies = [ - { name = "filelock", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fsspec", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jinja2", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "networkx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "sympy", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313-win_amd64.whl" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl" }, - { url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313t-win_amd64.whl" }, -] - [[package]] name = "tqdm" version = "4.67.1" @@ -636,18 +350,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] -[[package]] -name = "triton" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/7b/0a685684ed5322d2af0bddefed7906674f67974aa88b0fae6e82e3b766f6/triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb", size = 155569223, upload-time = "2025-07-30T19:58:44.017Z" }, - { url = "https://files.pythonhosted.org/packages/20/63/8cb444ad5cdb25d999b7d647abac25af0ee37d292afc009940c05b82dda0/triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d", size = 155659780, upload-time = "2025-07-30T19:58:51.171Z" }, -] - [[package]] name = "typing-extensions" version = "4.15.0" From ecc1fd88dec8428813b287dafae27a1f5b994f6c Mon Sep 17 00:00:00 2001 From: Jace Date: Wed, 24 Sep 2025 16:47:31 -0400 Subject: [PATCH 22/24] redo deps --- pyproject.toml | 3 +- uv.lock | 278 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 279 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 46005df..845a900 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,8 +8,9 @@ dependencies = [ "pygame>=2.6.1", "openai", "ruff>=0.12.8", + "numpy>=2.3.3", + "torch>=2.8.0", "aiofiles", - "numpy", ] [tool.ruff] diff --git a/uv.lock b/uv.lock index 5979a04..7331629 100644 --- a/uv.lock +++ b/uv.lock @@ -65,6 +65,7 @@ dependencies = [ { name = "openai" }, { name = "pygame" }, { name = "ruff" }, + { name = "torch" }, ] [package.dev-dependencies] @@ -75,10 +76,11 @@ dev = [ [package.metadata] requires-dist = [ { name = "aiofiles" }, - { name = "numpy" }, + { name = "numpy", specifier = ">=2.3.3" }, { name = "openai" }, { name = "pygame", specifier = ">=2.6.1" }, { name = "ruff", specifier = ">=0.12.8" }, + { name = "torch", specifier = ">=2.8.0" }, ] [package.metadata.requires-dev] @@ -93,6 +95,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, +] + +[[package]] +name = "fsspec" +version = "2025.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -139,6 +159,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + [[package]] name = "jiter" version = "0.10.0" @@ -175,6 +207,52 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, ] +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "networkx" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, +] + [[package]] name = "numpy" version = "2.3.3" @@ -227,6 +305,132 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/5b/4e4fff7bad39adf89f735f2bc87248c81db71205b62bcc0d5ca5b606b3c3/nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039", size = 322364134, upload-time = "2025-06-03T21:58:04.013Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, +] + [[package]] name = "openai" version = "1.107.0" @@ -329,6 +533,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, ] +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -338,6 +551,57 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "torch" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/4e/469ced5a0603245d6a19a556e9053300033f9c5baccf43a3d25ba73e189e/torch-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2b2f96814e0345f5a5aed9bf9734efa913678ed19caf6dc2cddb7930672d6128", size = 101936856, upload-time = "2025-08-06T14:54:01.526Z" }, + { url = "https://files.pythonhosted.org/packages/16/82/3948e54c01b2109238357c6f86242e6ecbf0c63a1af46906772902f82057/torch-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:65616ca8ec6f43245e1f5f296603e33923f4c30f93d65e103d9e50c25b35150b", size = 887922844, upload-time = "2025-08-06T14:55:50.78Z" }, + { url = "https://files.pythonhosted.org/packages/e3/54/941ea0a860f2717d86a811adf0c2cd01b3983bdd460d0803053c4e0b8649/torch-2.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:659df54119ae03e83a800addc125856effda88b016dfc54d9f65215c3975be16", size = 241330968, upload-time = "2025-08-06T14:54:45.293Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/8b7b13bba430f5e21d77708b616f767683629fc4f8037564a177d20f90ed/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767", size = 73915128, upload-time = "2025-08-06T14:54:34.769Z" }, + { url = "https://files.pythonhosted.org/packages/15/0e/8a800e093b7f7430dbaefa80075aee9158ec22e4c4fc3c1a66e4fb96cb4f/torch-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:83c13411a26fac3d101fe8035a6b0476ae606deb8688e904e796a3534c197def", size = 102020139, upload-time = "2025-08-06T14:54:39.047Z" }, + { url = "https://files.pythonhosted.org/packages/4a/15/5e488ca0bc6162c86a33b58642bc577c84ded17c7b72d97e49b5833e2d73/torch-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:8f0a9d617a66509ded240add3754e462430a6c1fc5589f86c17b433dd808f97a", size = 887990692, upload-time = "2025-08-06T14:56:18.286Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a8/6a04e4b54472fc5dba7ca2341ab219e529f3c07b6941059fbf18dccac31f/torch-2.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a7242b86f42be98ac674b88a4988643b9bc6145437ec8f048fea23f72feb5eca", size = 241603453, upload-time = "2025-08-06T14:55:22.945Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/650bb7f28f771af0cb791b02348db8b7f5f64f40f6829ee82aa6ce99aabe/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211", size = 73632395, upload-time = "2025-08-06T14:55:28.645Z" }, +] + [[package]] name = "tqdm" version = "4.67.1" @@ -350,6 +614,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] +[[package]] +name = "triton" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/7b/0a685684ed5322d2af0bddefed7906674f67974aa88b0fae6e82e3b766f6/triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb", size = 155569223, upload-time = "2025-07-30T19:58:44.017Z" }, + { url = "https://files.pythonhosted.org/packages/20/63/8cb444ad5cdb25d999b7d647abac25af0ee37d292afc009940c05b82dda0/triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d", size = 155659780, upload-time = "2025-07-30T19:58:51.171Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" From 0c299dcc709dca6008e90fe6a8007c4fd837f206 Mon Sep 17 00:00:00 2001 From: Jace Date: Wed, 24 Sep 2025 16:51:06 -0400 Subject: [PATCH 23/24] add readme and instructions on how to simulate --- players/player_4/readme.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 players/player_4/readme.txt diff --git a/players/player_4/readme.txt b/players/player_4/readme.txt new file mode 100644 index 0000000..4cbf695 --- /dev/null +++ b/players/player_4/readme.txt @@ -0,0 +1,4 @@ +To simulate games: + +1. Make changes to scenarios.yaml +2. From the root of the folder, uv run players/player_4/sim.py \ No newline at end of file From 30bced58b8fb767394bc9638acd57780a48f7df7 Mon Sep 17 00:00:00 2001 From: Jace Date: Mon, 29 Sep 2025 12:37:53 -0400 Subject: [PATCH 24/24] analysis --- pyproject.toml | 4 + uv.lock | 377 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 374 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 845a900..33606eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,10 @@ dependencies = [ "numpy>=2.3.3", "torch>=2.8.0", "aiofiles", + "numpy", + "pandas", + "matplotlib", + "seaborn" ] [tool.ruff] diff --git a/uv.lock b/uv.lock index 5af3890..2a518e2 100644 --- a/uv.lock +++ b/uv.lock @@ -55,16 +55,74 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, +] + [[package]] name = "conversation" version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "aiofiles" }, + { name = "matplotlib" }, { name = "numpy" }, { name = "openai" }, + { name = "pandas" }, { name = "pygame" }, { name = "ruff" }, + { name = "seaborn" }, { name = "torch" }, ] @@ -76,16 +134,29 @@ dev = [ [package.metadata] requires-dist = [ { name = "aiofiles" }, + { name = "matplotlib" }, + { name = "numpy" }, { name = "numpy", specifier = ">=2.3.3" }, { name = "openai" }, + { name = "pandas" }, { name = "pygame", specifier = ">=2.6.1" }, { name = "ruff", specifier = ">=0.12.8" }, + { name = "seaborn" }, { name = "torch", specifier = ">=2.8.0" }, ] [package.metadata.requires-dev] dev = [{ name = "ruff", specifier = ">=0.12.8" }] +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + [[package]] name = "distro" version = "1.9.0" @@ -104,6 +175,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, ] +[[package]] +name = "fonttools" +version = "4.60.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/d9/4eabd956fe123651a1f0efe29d9758b3837b5ae9a98934bdb571117033bb/fonttools-4.60.0.tar.gz", hash = "sha256:8f5927f049091a0ca74d35cce7f78e8f7775c83a6901a8fbe899babcc297146a", size = 3553671, upload-time = "2025-09-17T11:34:01.504Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/6b/d090cd54abe88192fe3010f573508b2592cf1d1f98b14bcb799a8ad20525/fonttools-4.60.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:97100ba820936cdb5148b634e0884f0088699c7e2f1302ae7bba3747c7a19fb3", size = 2824791, upload-time = "2025-09-17T11:32:47.002Z" }, + { url = "https://files.pythonhosted.org/packages/97/8c/7ccb5a27aac9a535623fe04935fb9f469a4f8a1253991af9fbac2fe88c17/fonttools-4.60.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:03fccf84f377f83e99a5328a9ebe6b41e16fcf64a1450c352b6aa7e0deedbc01", size = 2347081, upload-time = "2025-09-17T11:32:49.204Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1a/c14f0bb20b4cb7849dc0519f0ab0da74318d52236dc23168530569958599/fonttools-4.60.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a3ef06671f862cd7da78ab105fbf8dce9da3634a8f91b3a64ed5c29c0ac6a9a8", size = 4902095, upload-time = "2025-09-17T11:32:51.848Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a0/c7c91f07c40de5399cbaec7d25e04c9afac6c8f80036a98c125efdb5fe1a/fonttools-4.60.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f2195faf96594c238462c420c7eff97d1aa51de595434f806ec3952df428616", size = 4959137, upload-time = "2025-09-17T11:32:54.185Z" }, + { url = "https://files.pythonhosted.org/packages/38/d2/169e49498df9f2c721763aa39b0bf3d08cb762864ebc8a8ddb99f5ba7ec8/fonttools-4.60.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3887008865fa4f56cff58a1878f1300ba81a4e34f76daf9b47234698493072ee", size = 4900467, upload-time = "2025-09-17T11:32:56.664Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/bfb56b89c3eab8bcb739c7fd1e8a43285c8dd833e1e1d18d4f54f2f641af/fonttools-4.60.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5567bd130378f21231d3856d8f0571dcdfcd77e47832978c26dabe572d456daa", size = 5043508, upload-time = "2025-09-17T11:32:58.944Z" }, + { url = "https://files.pythonhosted.org/packages/77/30/2b511c7eb99faee1fd9a0b42e984fb91275da3d681da650af4edf409d0fd/fonttools-4.60.0-cp313-cp313-win32.whl", hash = "sha256:699d0b521ec0b188ac11f2c14ccf6a926367795818ddf2bd00a273e9a052dd20", size = 2216037, upload-time = "2025-09-17T11:33:01.192Z" }, + { url = "https://files.pythonhosted.org/packages/3d/73/a2cc5ee4faeb0302cc81942c27f3b516801bf489fdc422a1b20090fff695/fonttools-4.60.0-cp313-cp313-win_amd64.whl", hash = "sha256:24296163268e7c800009711ce5c0e9997be8882c0bd546696c82ef45966163a6", size = 2265190, upload-time = "2025-09-17T11:33:03.935Z" }, + { url = "https://files.pythonhosted.org/packages/86/dd/a126706e45e0ce097cef6de4108b5597795acaa945fdbdd922dbc090d335/fonttools-4.60.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:b6fe3efdc956bdad95145cea906ad9ff345c17b706356dfc1098ce3230591343", size = 2821835, upload-time = "2025-09-17T11:33:06.094Z" }, + { url = "https://files.pythonhosted.org/packages/ac/90/5c17f311bbd983fd614b82a7a06da967b5d3c87e3e61cf34de6029a92ff4/fonttools-4.60.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:764b2aaab839762a3aa3207e5b3f0e0dfa41799e0b091edec5fcbccc584fdab5", size = 2344536, upload-time = "2025-09-17T11:33:08.574Z" }, + { url = "https://files.pythonhosted.org/packages/60/67/48c1a6229b2a5668c4111fbd1694ca417adedc1254c5cd2f9a11834c429d/fonttools-4.60.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b81c7c47d9e78106a4d70f1dbeb49150513171715e45e0d2661809f2b0e3f710", size = 4842494, upload-time = "2025-09-17T11:33:11.338Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/83b0b37d02b7e321cbe2b8fcec0aa18571f0a47d3dc222196404371d83b6/fonttools-4.60.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799ff60ee66b300ebe1fe6632b1cc55a66400fe815cef7b034d076bce6b1d8fc", size = 4943203, upload-time = "2025-09-17T11:33:13.285Z" }, + { url = "https://files.pythonhosted.org/packages/c9/07/11163e49497c53392eaca210a474104e4987c17ca7731f8754ba0d416a67/fonttools-4.60.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f9878abe155ddd1b433bab95d027a686898a6afba961f3c5ca14b27488f2d772", size = 4889233, upload-time = "2025-09-17T11:33:15.175Z" }, + { url = "https://files.pythonhosted.org/packages/60/90/e85005d955cb26e7de015d5678778b8cc3293c0f3d717865675bd641fbfc/fonttools-4.60.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ded432b7133ea4602fdb4731a4a7443a8e9548edad28987b99590cf6da626254", size = 4998335, upload-time = "2025-09-17T11:33:17.217Z" }, + { url = "https://files.pythonhosted.org/packages/2a/82/0374ad53729de6e3788ecdb8a3731ce6592c5ffa9bff823cef2ffe0164af/fonttools-4.60.0-cp314-cp314-win32.whl", hash = "sha256:5d97cf3a9245316d5978628c05642b939809c4f55ca632ca40744cb9de6e8d4a", size = 2219840, upload-time = "2025-09-17T11:33:19.494Z" }, + { url = "https://files.pythonhosted.org/packages/11/c3/804cd47453dcafb7976f9825b43cc0e61a2fe30eddb971b681cd72c4ca65/fonttools-4.60.0-cp314-cp314-win_amd64.whl", hash = "sha256:61b9ef46dd5e9dcb6f437eb0cc5ed83d5049e1bf9348e31974ffee1235db0f8f", size = 2269891, upload-time = "2025-09-17T11:33:21.743Z" }, + { url = "https://files.pythonhosted.org/packages/75/bf/1bd760aca04098e7028b4e0e5f73b41ff74b322275698071454652476a44/fonttools-4.60.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bba7e3470cf353e1484a36dfb4108f431c2859e3f6097fe10118eeae92166773", size = 2893361, upload-time = "2025-09-17T11:33:23.68Z" }, + { url = "https://files.pythonhosted.org/packages/25/35/7a2c09aa990ed77f34924def383f44fc576a5596cc3df8438071e1baa1ac/fonttools-4.60.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5ac6439a38c27b3287063176b3303b34982024b01e2e95bba8ac1e45f6d41c1", size = 2374086, upload-time = "2025-09-17T11:33:25.988Z" }, + { url = "https://files.pythonhosted.org/packages/77/a9/f85ed2493e82837ff73421f3f7a1c3ae8f0b14051307418c916d9563da1f/fonttools-4.60.0-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4acd21e9f125a1257da59edf7a6e9bd4abd76282770715c613f1fe482409e9f9", size = 4848766, upload-time = "2025-09-17T11:33:28.018Z" }, + { url = "https://files.pythonhosted.org/packages/d1/91/29830eda31ae9231a06d5246e5d0c686422d03456ed666e13576c24c3f97/fonttools-4.60.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4a6fc53039ea047e35dc62b958af9cd397eedbc3fa42406d2910ae091b9ae37", size = 5084613, upload-time = "2025-09-17T11:33:30.562Z" }, + { url = "https://files.pythonhosted.org/packages/48/01/615905e7db2568fe1843145077e680443494b7caab2089527b7e112c7606/fonttools-4.60.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ef34f44eadf133e94e82c775a33ee3091dd37ee0161c5f5ea224b46e3ce0fb8e", size = 4956620, upload-time = "2025-09-17T11:33:32.497Z" }, + { url = "https://files.pythonhosted.org/packages/97/8e/64e65255871ec2f13b6c00b5b12d08b928b504867cfb7e7ed73e5e941832/fonttools-4.60.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d112cae3e7ad1bb5d7f7a60365fcf6c181374648e064a8c07617b240e7c828ee", size = 4973202, upload-time = "2025-09-17T11:33:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/04d16243eb441e8de61074c7809e92d2e35df4cd11af5632e486bc630dab/fonttools-4.60.0-cp314-cp314t-win32.whl", hash = "sha256:0f7b2c251dc338973e892a1e153016114e7a75f6aac7a49b84d5d1a4c0608d08", size = 2281217, upload-time = "2025-09-17T11:33:36.965Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5f/09bd2f9f28ef0d6f3620fa19699d11c4bc83ff8a2786d8ccdd97c209b19a/fonttools-4.60.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a72771106bc7434098db35abecd84d608857f6e116d3ef00366b213c502ce9", size = 2344738, upload-time = "2025-09-17T11:33:39.372Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a4/247d3e54eb5ed59e94e09866cfc4f9567e274fbf310ba390711851f63b3b/fonttools-4.60.0-py3-none-any.whl", hash = "sha256:496d26e4d14dcccdd6ada2e937e4d174d3138e3d73f5c9b6ec6eb2fd1dab4f66", size = 1142186, upload-time = "2025-09-17T11:33:59.287Z" }, +] + [[package]] name = "fsspec" version = "2025.9.0" @@ -207,6 +311,65 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, ] +[[package]] +name = "kiwisolver" +version = "1.4.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" }, + { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" }, + { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" }, + { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" }, + { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" }, + { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" }, + { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" }, + { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" }, + { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" }, + { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" }, + { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" }, + { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" }, + { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" }, + { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" }, + { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" }, + { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" }, + { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" }, + { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" }, + { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" }, + { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" }, + { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" }, + { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" }, + { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" }, + { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" }, + { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" }, + { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" }, + { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" }, + { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" }, + { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" }, + { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" }, +] + [[package]] name = "markupsafe" version = "3.0.2" @@ -235,6 +398,53 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, ] +[[package]] +name = "matplotlib" +version = "3.10.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a0/59/c3e6453a9676ffba145309a73c462bb407f4400de7de3f2b41af70720a3c/matplotlib-3.10.6.tar.gz", hash = "sha256:ec01b645840dd1996df21ee37f208cd8ba57644779fa20464010638013d3203c", size = 34804264, upload-time = "2025-08-30T00:14:25.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/db/18380e788bb837e724358287b08e223b32bc8dccb3b0c12fa8ca20bc7f3b/matplotlib-3.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:819e409653c1106c8deaf62e6de6b8611449c2cd9939acb0d7d4e57a3d95cc7a", size = 8273231, upload-time = "2025-08-30T00:13:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0f/38dd49445b297e0d4f12a322c30779df0d43cb5873c7847df8a82e82ec67/matplotlib-3.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59c8ac8382fefb9cb71308dde16a7c487432f5255d8f1fd32473523abecfecdf", size = 8128730, upload-time = "2025-08-30T00:13:15.556Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b8/9eea6630198cb303d131d95d285a024b3b8645b1763a2916fddb44ca8760/matplotlib-3.10.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84e82d9e0fd70c70bc55739defbd8055c54300750cbacf4740c9673a24d6933a", size = 8698539, upload-time = "2025-08-30T00:13:17.297Z" }, + { url = "https://files.pythonhosted.org/packages/71/34/44c7b1f075e1ea398f88aeabcc2907c01b9cc99e2afd560c1d49845a1227/matplotlib-3.10.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25f7a3eb42d6c1c56e89eacd495661fc815ffc08d9da750bca766771c0fd9110", size = 9529702, upload-time = "2025-08-30T00:13:19.248Z" }, + { url = "https://files.pythonhosted.org/packages/b5/7f/e5c2dc9950c7facaf8b461858d1b92c09dd0cf174fe14e21953b3dda06f7/matplotlib-3.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9c862d91ec0b7842920a4cfdaaec29662195301914ea54c33e01f1a28d014b2", size = 9593742, upload-time = "2025-08-30T00:13:21.181Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1d/70c28528794f6410ee2856cd729fa1f1756498b8d3126443b0a94e1a8695/matplotlib-3.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:1b53bd6337eba483e2e7d29c5ab10eee644bc3a2491ec67cc55f7b44583ffb18", size = 8122753, upload-time = "2025-08-30T00:13:23.44Z" }, + { url = "https://files.pythonhosted.org/packages/e8/74/0e1670501fc7d02d981564caf7c4df42974464625935424ca9654040077c/matplotlib-3.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:cbd5eb50b7058b2892ce45c2f4e92557f395c9991f5c886d1bb74a1582e70fd6", size = 7992973, upload-time = "2025-08-30T00:13:26.632Z" }, + { url = "https://files.pythonhosted.org/packages/b1/4e/60780e631d73b6b02bd7239f89c451a72970e5e7ec34f621eda55cd9a445/matplotlib-3.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:acc86dd6e0e695c095001a7fccff158c49e45e0758fdf5dcdbb0103318b59c9f", size = 8316869, upload-time = "2025-08-30T00:13:28.262Z" }, + { url = "https://files.pythonhosted.org/packages/f8/15/baa662374a579413210fc2115d40c503b7360a08e9cc254aa0d97d34b0c1/matplotlib-3.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e228cd2ffb8f88b7d0b29e37f68ca9aaf83e33821f24a5ccc4f082dd8396bc27", size = 8178240, upload-time = "2025-08-30T00:13:30.007Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3f/3c38e78d2aafdb8829fcd0857d25aaf9e7dd2dfcf7ec742765b585774931/matplotlib-3.10.6-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:658bc91894adeab669cf4bb4a186d049948262987e80f0857216387d7435d833", size = 8711719, upload-time = "2025-08-30T00:13:31.72Z" }, + { url = "https://files.pythonhosted.org/packages/96/4b/2ec2bbf8cefaa53207cc56118d1fa8a0f9b80642713ea9390235d331ede4/matplotlib-3.10.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8913b7474f6dd83ac444c9459c91f7f0f2859e839f41d642691b104e0af056aa", size = 9541422, upload-time = "2025-08-30T00:13:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/83/7d/40255e89b3ef11c7871020563b2dd85f6cb1b4eff17c0f62b6eb14c8fa80/matplotlib-3.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:091cea22e059b89f6d7d1a18e2c33a7376c26eee60e401d92a4d6726c4e12706", size = 9594068, upload-time = "2025-08-30T00:13:35.833Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a9/0213748d69dc842537a113493e1c27daf9f96bd7cc316f933dc8ec4de985/matplotlib-3.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:491e25e02a23d7207629d942c666924a6b61e007a48177fdd231a0097b7f507e", size = 8200100, upload-time = "2025-08-30T00:13:37.668Z" }, + { url = "https://files.pythonhosted.org/packages/be/15/79f9988066ce40b8a6f1759a934ea0cde8dc4adc2262255ee1bc98de6ad0/matplotlib-3.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3d80d60d4e54cda462e2cd9a086d85cd9f20943ead92f575ce86885a43a565d5", size = 8042142, upload-time = "2025-08-30T00:13:39.426Z" }, + { url = "https://files.pythonhosted.org/packages/7c/58/e7b6d292beae6fb4283ca6fb7fa47d7c944a68062d6238c07b497dd35493/matplotlib-3.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:70aaf890ce1d0efd482df969b28a5b30ea0b891224bb315810a3940f67182899", size = 8273802, upload-time = "2025-08-30T00:13:41.006Z" }, + { url = "https://files.pythonhosted.org/packages/9f/f6/7882d05aba16a8cdd594fb9a03a9d3cca751dbb6816adf7b102945522ee9/matplotlib-3.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1565aae810ab79cb72e402b22facfa6501365e73ebab70a0fdfb98488d2c3c0c", size = 8131365, upload-time = "2025-08-30T00:13:42.664Z" }, + { url = "https://files.pythonhosted.org/packages/94/bf/ff32f6ed76e78514e98775a53715eca4804b12bdcf35902cdd1cf759d324/matplotlib-3.10.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3b23315a01981689aa4e1a179dbf6ef9fbd17143c3eea77548c2ecfb0499438", size = 9533961, upload-time = "2025-08-30T00:13:44.372Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c3/6bf88c2fc2da7708a2ff8d2eeb5d68943130f50e636d5d3dcf9d4252e971/matplotlib-3.10.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30fdd37edf41a4e6785f9b37969de57aea770696cb637d9946eb37470c94a453", size = 9804262, upload-time = "2025-08-30T00:13:46.614Z" }, + { url = "https://files.pythonhosted.org/packages/0f/7a/e05e6d9446d2d577b459427ad060cd2de5742d0e435db3191fea4fcc7e8b/matplotlib-3.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bc31e693da1c08012c764b053e702c1855378e04102238e6a5ee6a7117c53a47", size = 9595508, upload-time = "2025-08-30T00:13:48.731Z" }, + { url = "https://files.pythonhosted.org/packages/39/fb/af09c463ced80b801629fd73b96f726c9f6124c3603aa2e480a061d6705b/matplotlib-3.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:05be9bdaa8b242bc6ff96330d18c52f1fc59c6fb3a4dd411d953d67e7e1baf98", size = 8252742, upload-time = "2025-08-30T00:13:50.539Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f9/b682f6db9396d9ab8f050c0a3bfbb5f14fb0f6518f08507c04cc02f8f229/matplotlib-3.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:f56a0d1ab05d34c628592435781d185cd99630bdfd76822cd686fb5a0aecd43a", size = 8124237, upload-time = "2025-08-30T00:13:54.3Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d2/b69b4a0923a3c05ab90527c60fdec899ee21ca23ede7f0fb818e6620d6f2/matplotlib-3.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:94f0b4cacb23763b64b5dace50d5b7bfe98710fed5f0cef5c08135a03399d98b", size = 8316956, upload-time = "2025-08-30T00:13:55.932Z" }, + { url = "https://files.pythonhosted.org/packages/28/e9/dc427b6f16457ffaeecb2fc4abf91e5adb8827861b869c7a7a6d1836fa73/matplotlib-3.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cc332891306b9fb39462673d8225d1b824c89783fee82840a709f96714f17a5c", size = 8178260, upload-time = "2025-08-30T00:14:00.942Z" }, + { url = "https://files.pythonhosted.org/packages/c4/89/1fbd5ad611802c34d1c7ad04607e64a1350b7fb9c567c4ec2c19e066ed35/matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee1d607b3fb1590deb04b69f02ea1d53ed0b0bf75b2b1a5745f269afcbd3cdd3", size = 9541422, upload-time = "2025-08-30T00:14:02.664Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/65fec8716025b22c1d72d5a82ea079934c76a547696eaa55be6866bc89b1/matplotlib-3.10.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:376a624a218116461696b27b2bbf7a8945053e6d799f6502fc03226d077807bf", size = 9803678, upload-time = "2025-08-30T00:14:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b0/40fb2b3a1ab9381bb39a952e8390357c8be3bdadcf6d5055d9c31e1b35ae/matplotlib-3.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:83847b47f6524c34b4f2d3ce726bb0541c48c8e7692729865c3df75bfa0f495a", size = 9594077, upload-time = "2025-08-30T00:14:07.012Z" }, + { url = "https://files.pythonhosted.org/packages/76/34/c4b71b69edf5b06e635eee1ed10bfc73cf8df058b66e63e30e6a55e231d5/matplotlib-3.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c7e0518e0d223683532a07f4b512e2e0729b62674f1b3a1a69869f98e6b1c7e3", size = 8342822, upload-time = "2025-08-30T00:14:09.041Z" }, + { url = "https://files.pythonhosted.org/packages/e8/62/aeabeef1a842b6226a30d49dd13e8a7a1e81e9ec98212c0b5169f0a12d83/matplotlib-3.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:4dd83e029f5b4801eeb87c64efd80e732452781c16a9cf7415b7b63ec8f374d7", size = 8172588, upload-time = "2025-08-30T00:14:11.166Z" }, +] + [[package]] name = "mpmath" version = "1.3.0" @@ -342,7 +552,7 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, @@ -353,7 +563,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, @@ -380,9 +590,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, @@ -393,7 +603,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, @@ -450,6 +660,97 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/ed/e8a4fd20390f2858b95227c288df8fe0c835f7c77625f7583609161684ba/openai-1.107.0-py3-none-any.whl", hash = "sha256:3dcfa3cbb116bd6924b27913b8da28c4a787379ff60049588547a1013e6d6438", size = 950968, upload-time = "2025-09-08T19:25:45.552Z" }, ] +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/8e/0e90233ac205ad182bd6b422532695d2b9414944a280488105d598c70023/pandas-2.3.2.tar.gz", hash = "sha256:ab7b58f8f82706890924ccdfb5f48002b83d2b5a3845976a9fb705d36c34dcdb", size = 4488684, upload-time = "2025-08-21T10:28:29.257Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/64/a2f7bf678af502e16b472527735d168b22b7824e45a4d7e96a4fbb634b59/pandas-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0c6ecbac99a354a051ef21c5307601093cb9e0f4b1855984a084bfec9302699e", size = 11531061, upload-time = "2025-08-21T10:27:34.647Z" }, + { url = "https://files.pythonhosted.org/packages/54/4c/c3d21b2b7769ef2f4c2b9299fcadd601efa6729f1357a8dbce8dd949ed70/pandas-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6f048aa0fd080d6a06cc7e7537c09b53be6642d330ac6f54a600c3ace857ee9", size = 10668666, upload-time = "2025-08-21T10:27:37.203Z" }, + { url = "https://files.pythonhosted.org/packages/50/e2/f775ba76ecfb3424d7f5862620841cf0edb592e9abd2d2a5387d305fe7a8/pandas-2.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0064187b80a5be6f2f9c9d6bdde29372468751dfa89f4211a3c5871854cfbf7a", size = 11332835, upload-time = "2025-08-21T10:27:40.188Z" }, + { url = "https://files.pythonhosted.org/packages/8f/52/0634adaace9be2d8cac9ef78f05c47f3a675882e068438b9d7ec7ef0c13f/pandas-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac8c320bded4718b298281339c1a50fb00a6ba78cb2a63521c39bec95b0209b", size = 12057211, upload-time = "2025-08-21T10:27:43.117Z" }, + { url = "https://files.pythonhosted.org/packages/0b/9d/2df913f14b2deb9c748975fdb2491da1a78773debb25abbc7cbc67c6b549/pandas-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:114c2fe4f4328cf98ce5716d1532f3ab79c5919f95a9cfee81d9140064a2e4d6", size = 12749277, upload-time = "2025-08-21T10:27:45.474Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/da1a2417026bd14d98c236dba88e39837182459d29dcfcea510b2ac9e8a1/pandas-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:48fa91c4dfb3b2b9bfdb5c24cd3567575f4e13f9636810462ffed8925352be5a", size = 13415256, upload-time = "2025-08-21T10:27:49.885Z" }, + { url = "https://files.pythonhosted.org/packages/22/3c/f2af1ce8840ef648584a6156489636b5692c162771918aa95707c165ad2b/pandas-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:12d039facec710f7ba305786837d0225a3444af7bbd9c15c32ca2d40d157ed8b", size = 10982579, upload-time = "2025-08-21T10:28:08.435Z" }, + { url = "https://files.pythonhosted.org/packages/f3/98/8df69c4097a6719e357dc249bf437b8efbde808038268e584421696cbddf/pandas-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c624b615ce97864eb588779ed4046186f967374185c047070545253a52ab2d57", size = 12028163, upload-time = "2025-08-21T10:27:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/0e/23/f95cbcbea319f349e10ff90db488b905c6883f03cbabd34f6b03cbc3c044/pandas-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0cee69d583b9b128823d9514171cabb6861e09409af805b54459bd0c821a35c2", size = 11391860, upload-time = "2025-08-21T10:27:54.673Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1b/6a984e98c4abee22058aa75bfb8eb90dce58cf8d7296f8bc56c14bc330b0/pandas-2.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2319656ed81124982900b4c37f0e0c58c015af9a7bbc62342ba5ad07ace82ba9", size = 11309830, upload-time = "2025-08-21T10:27:56.957Z" }, + { url = "https://files.pythonhosted.org/packages/15/d5/f0486090eb18dd8710bf60afeaf638ba6817047c0c8ae5c6a25598665609/pandas-2.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b37205ad6f00d52f16b6d09f406434ba928c1a1966e2771006a9033c736d30d2", size = 11883216, upload-time = "2025-08-21T10:27:59.302Z" }, + { url = "https://files.pythonhosted.org/packages/10/86/692050c119696da19e20245bbd650d8dfca6ceb577da027c3a73c62a047e/pandas-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:837248b4fc3a9b83b9c6214699a13f069dc13510a6a6d7f9ba33145d2841a012", size = 12699743, upload-time = "2025-08-21T10:28:02.447Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d7/612123674d7b17cf345aad0a10289b2a384bff404e0463a83c4a3a59d205/pandas-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d2c3554bd31b731cd6490d94a28f3abb8dd770634a9e06eb6d2911b9827db370", size = 13186141, upload-time = "2025-08-21T10:28:05.377Z" }, +] + +[[package]] +name = "pillow" +version = "11.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, + { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, + { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, + { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, + { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, + { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, + { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, + { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, + { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, + { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, + { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, + { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, + { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, + { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, + { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, + { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, + { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, + { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, + { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, + { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, + { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, + { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, + { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, + { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, + { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, + { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, + { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, + { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, +] + [[package]] name = "pydantic" version = "2.11.7" @@ -508,6 +809,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/11/17f7f319ca91824b86557e9303e3b7a71991ef17fd45286bf47d7f0a38e6/pygame-2.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:813af4fba5d0b2cb8e58f5d95f7910295c34067dcc290d34f1be59c48bd1ea6a", size = 10620084, upload-time = "2024-09-29T11:48:51.587Z" }, ] +[[package]] +name = "pyparsing" +version = "3.2.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, +] + [[package]] name = "ruff" version = "0.12.8" @@ -533,6 +864,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, ] +[[package]] +name = "seaborn" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914, upload-time = "2024-01-25T13:21:49.598Z" }, +] + [[package]] name = "setuptools" version = "80.9.0" @@ -542,6 +887,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -619,7 +973,7 @@ name = "triton" version = "3.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "setuptools" }, + { name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/30/7b/0a685684ed5322d2af0bddefed7906674f67974aa88b0fae6e82e3b766f6/triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb", size = 155569223, upload-time = "2025-07-30T19:58:44.017Z" }, @@ -646,3 +1000,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7 wheels = [ { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, ] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, +]