edsl 0.1.36.dev5__py3-none-any.whl → 0.1.36.dev6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edsl/Base.py +303 -303
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +47 -47
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +804 -804
- edsl/agents/AgentList.py +337 -337
- edsl/agents/Invigilator.py +222 -222
- edsl/agents/InvigilatorBase.py +294 -294
- edsl/agents/PromptConstructor.py +312 -312
- edsl/agents/__init__.py +3 -3
- edsl/agents/descriptors.py +86 -86
- edsl/agents/prompt_helpers.py +129 -129
- edsl/auto/AutoStudy.py +117 -117
- edsl/auto/StageBase.py +230 -230
- edsl/auto/StageGenerateSurvey.py +178 -178
- edsl/auto/StageLabelQuestions.py +125 -125
- edsl/auto/StagePersona.py +61 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
- edsl/auto/StagePersonaDimensionValues.py +74 -74
- edsl/auto/StagePersonaDimensions.py +69 -69
- edsl/auto/StageQuestions.py +73 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +224 -224
- edsl/base/Base.py +289 -289
- edsl/config.py +149 -149
- edsl/conjure/AgentConstructionMixin.py +152 -152
- edsl/conjure/Conjure.py +62 -62
- edsl/conjure/InputData.py +659 -659
- edsl/conjure/InputDataCSV.py +48 -48
- edsl/conjure/InputDataMixinQuestionStats.py +182 -182
- edsl/conjure/InputDataPyRead.py +91 -91
- edsl/conjure/InputDataSPSS.py +8 -8
- edsl/conjure/InputDataStata.py +8 -8
- edsl/conjure/QuestionOptionMixin.py +76 -76
- edsl/conjure/QuestionTypeMixin.py +23 -23
- edsl/conjure/RawQuestion.py +65 -65
- edsl/conjure/SurveyResponses.py +7 -7
- edsl/conjure/__init__.py +9 -9
- edsl/conjure/naming_utilities.py +263 -263
- edsl/conjure/utilities.py +201 -201
- edsl/conversation/Conversation.py +238 -238
- edsl/conversation/car_buying.py +58 -58
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +849 -849
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +527 -527
- edsl/data/CacheEntry.py +228 -228
- edsl/data/CacheHandler.py +149 -149
- edsl/data/RemoteCacheSync.py +83 -83
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +4 -4
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +73 -73
- edsl/enums.py +173 -173
- edsl/exceptions/__init__.py +50 -50
- edsl/exceptions/agents.py +40 -40
- edsl/exceptions/configuration.py +16 -16
- edsl/exceptions/coop.py +10 -10
- edsl/exceptions/data.py +14 -14
- edsl/exceptions/general.py +34 -34
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +91 -91
- edsl/exceptions/results.py +26 -26
- edsl/exceptions/surveys.py +34 -34
- edsl/inference_services/AnthropicService.py +87 -87
- edsl/inference_services/AwsBedrock.py +115 -115
- edsl/inference_services/AzureAI.py +217 -217
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +156 -156
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +147 -147
- edsl/inference_services/InferenceServicesCollection.py +72 -68
- edsl/inference_services/MistralAIService.py +123 -123
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +224 -224
- edsl/inference_services/TestService.py +89 -89
- edsl/inference_services/TogetherAIService.py +170 -170
- edsl/inference_services/models_available_cache.py +118 -94
- edsl/inference_services/rate_limits_cache.py +25 -25
- edsl/inference_services/registry.py +39 -39
- edsl/inference_services/write_available.py +10 -10
- edsl/jobs/Answers.py +56 -56
- edsl/jobs/Jobs.py +1112 -1112
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/buckets/BucketCollection.py +63 -63
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +248 -248
- edsl/jobs/interviews/Interview.py +651 -651
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
- edsl/jobs/interviews/InterviewExceptionEntry.py +182 -182
- edsl/jobs/interviews/InterviewStatistic.py +63 -63
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
- edsl/jobs/interviews/InterviewStatusLog.py +92 -92
- edsl/jobs/interviews/ReportErrors.py +66 -66
- edsl/jobs/interviews/interview_status_enum.py +9 -9
- edsl/jobs/runners/JobsRunnerAsyncio.py +337 -337
- edsl/jobs/runners/JobsRunnerStatus.py +332 -332
- edsl/jobs/tasks/QuestionTaskCreator.py +242 -242
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +441 -441
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +163 -163
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/LanguageModel.py +718 -718
- edsl/language_models/ModelList.py +102 -102
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/__init__.py +2 -2
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- edsl/language_models/registry.py +137 -137
- edsl/language_models/repair.py +156 -156
- edsl/language_models/unused/ReplicateBase.py +83 -83
- edsl/language_models/utilities.py +64 -64
- edsl/notebooks/Notebook.py +259 -259
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +358 -358
- edsl/prompts/__init__.py +2 -2
- edsl/questions/AnswerValidatorMixin.py +289 -289
- edsl/questions/QuestionBase.py +616 -616
- edsl/questions/QuestionBaseGenMixin.py +161 -161
- edsl/questions/QuestionBasePromptsMixin.py +266 -266
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +183 -183
- edsl/questions/QuestionFreeText.py +113 -113
- edsl/questions/QuestionFunctional.py +159 -159
- edsl/questions/QuestionList.py +231 -231
- edsl/questions/QuestionMultipleChoice.py +286 -286
- edsl/questions/QuestionNumerical.py +153 -153
- edsl/questions/QuestionRank.py +324 -324
- edsl/questions/Quick.py +41 -41
- edsl/questions/RegisterQuestionsMeta.py +71 -71
- edsl/questions/ResponseValidatorABC.py +174 -174
- edsl/questions/SimpleAskMixin.py +73 -73
- edsl/questions/__init__.py +26 -26
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +87 -87
- edsl/questions/derived/QuestionTopK.py +91 -91
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +418 -418
- edsl/questions/prompt_templates/question_budget.jinja +13 -13
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
- edsl/questions/prompt_templates/question_extract.jinja +11 -11
- edsl/questions/prompt_templates/question_free_text.jinja +3 -3
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
- edsl/questions/prompt_templates/question_list.jinja +17 -17
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
- edsl/questions/prompt_templates/question_numerical.jinja +36 -36
- edsl/questions/question_registry.py +147 -147
- edsl/questions/settings.py +12 -12
- edsl/questions/templates/budget/answering_instructions.jinja +7 -7
- edsl/questions/templates/budget/question_presentation.jinja +7 -7
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
- edsl/questions/templates/extract/answering_instructions.jinja +7 -7
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
- edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
- edsl/questions/templates/list/answering_instructions.jinja +3 -3
- edsl/questions/templates/list/question_presentation.jinja +5 -5
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
- edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
- edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
- edsl/questions/templates/numerical/question_presentation.jinja +6 -6
- edsl/questions/templates/rank/answering_instructions.jinja +11 -11
- edsl/questions/templates/rank/question_presentation.jinja +15 -15
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
- edsl/questions/templates/top_k/question_presentation.jinja +22 -22
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
- edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
- edsl/results/Dataset.py +293 -293
- edsl/results/DatasetExportMixin.py +693 -693
- edsl/results/DatasetTree.py +145 -145
- edsl/results/Result.py +433 -433
- edsl/results/Results.py +1158 -1158
- edsl/results/ResultsDBMixin.py +238 -238
- edsl/results/ResultsExportMixin.py +43 -43
- edsl/results/ResultsFetchMixin.py +33 -33
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/ResultsToolsMixin.py +98 -98
- edsl/results/Selector.py +118 -118
- edsl/results/__init__.py +2 -2
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/FileStore.py +443 -443
- edsl/scenarios/Scenario.py +507 -507
- edsl/scenarios/ScenarioHtmlMixin.py +59 -59
- edsl/scenarios/ScenarioList.py +1101 -1101
- edsl/scenarios/ScenarioListExportMixin.py +52 -52
- edsl/scenarios/ScenarioListPdfMixin.py +261 -261
- edsl/scenarios/__init__.py +2 -2
- edsl/shared.py +1 -1
- edsl/study/ObjectEntry.py +173 -173
- edsl/study/ProofOfWork.py +113 -113
- edsl/study/SnapShot.py +80 -80
- edsl/study/Study.py +528 -528
- edsl/study/__init__.py +4 -4
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +324 -324
- edsl/surveys/RuleCollection.py +387 -387
- edsl/surveys/Survey.py +1772 -1772
- edsl/surveys/SurveyCSS.py +261 -261
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/SurveyFlowVisualizationMixin.py +121 -121
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/__init__.py +3 -3
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +56 -56
- edsl/surveys/instructions/ChangeInstruction.py +47 -47
- edsl/surveys/instructions/Instruction.py +51 -51
- edsl/surveys/instructions/InstructionCollection.py +77 -77
- edsl/templates/error_reporting/base.html +23 -23
- edsl/templates/error_reporting/exceptions_by_model.html +34 -34
- edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
- edsl/templates/error_reporting/exceptions_by_type.html +16 -16
- edsl/templates/error_reporting/interview_details.html +115 -115
- edsl/templates/error_reporting/interviews.html +9 -9
- edsl/templates/error_reporting/overview.html +4 -4
- edsl/templates/error_reporting/performance_plot.html +1 -1
- edsl/templates/error_reporting/report.css +73 -73
- edsl/templates/error_reporting/report.html +117 -117
- edsl/templates/error_reporting/report.js +25 -25
- edsl/tools/__init__.py +1 -1
- edsl/tools/clusters.py +192 -192
- edsl/tools/embeddings.py +27 -27
- edsl/tools/embeddings_plotting.py +118 -118
- edsl/tools/plotting.py +112 -112
- edsl/tools/summarize.py +18 -18
- edsl/utilities/SystemInfo.py +28 -28
- edsl/utilities/__init__.py +22 -22
- edsl/utilities/ast_utilities.py +25 -25
- edsl/utilities/data/Registry.py +6 -6
- edsl/utilities/data/__init__.py +1 -1
- edsl/utilities/data/scooter_results.json +1 -1
- edsl/utilities/decorators.py +77 -77
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
- edsl/utilities/interface.py +627 -627
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +391 -391
- {edsl-0.1.36.dev5.dist-info → edsl-0.1.36.dev6.dist-info}/LICENSE +21 -21
- {edsl-0.1.36.dev5.dist-info → edsl-0.1.36.dev6.dist-info}/METADATA +1 -1
- edsl-0.1.36.dev6.dist-info/RECORD +279 -0
- edsl-0.1.36.dev5.dist-info/RECORD +0 -279
- {edsl-0.1.36.dev5.dist-info → edsl-0.1.36.dev6.dist-info}/WHEEL +0 -0
edsl/agents/AgentList.py
CHANGED
@@ -1,337 +1,337 @@
|
|
1
|
-
"""A list of Agent objects.
|
2
|
-
|
3
|
-
Example usage:
|
4
|
-
|
5
|
-
.. code-block:: python
|
6
|
-
|
7
|
-
al = AgentList([Agent.example(), Agent.example()])
|
8
|
-
len(al)
|
9
|
-
2
|
10
|
-
|
11
|
-
"""
|
12
|
-
|
13
|
-
from __future__ import annotations
|
14
|
-
import csv
|
15
|
-
import json
|
16
|
-
from collections import UserList
|
17
|
-
from typing import Any, List, Optional, Union
|
18
|
-
from rich import print_json
|
19
|
-
from rich.table import Table
|
20
|
-
from simpleeval import EvalWithCompoundTypes
|
21
|
-
from edsl.Base import Base
|
22
|
-
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
23
|
-
|
24
|
-
from collections.abc import Iterable
|
25
|
-
|
26
|
-
|
27
|
-
def is_iterable(obj):
|
28
|
-
return isinstance(obj, Iterable)
|
29
|
-
|
30
|
-
|
31
|
-
class AgentList(UserList, Base):
|
32
|
-
"""A list of Agents."""
|
33
|
-
|
34
|
-
def __init__(self, data: Optional[list["Agent"]] = None):
|
35
|
-
"""Initialize a new AgentList.
|
36
|
-
|
37
|
-
:param data: A list of Agents.
|
38
|
-
"""
|
39
|
-
if data is not None:
|
40
|
-
super().__init__(data)
|
41
|
-
else:
|
42
|
-
super().__init__()
|
43
|
-
|
44
|
-
def shuffle(self, seed: Optional[str] = "edsl") -> AgentList:
|
45
|
-
"""Shuffle the AgentList.
|
46
|
-
|
47
|
-
:param seed: The seed for the random number generator.
|
48
|
-
"""
|
49
|
-
import random
|
50
|
-
|
51
|
-
random.seed(seed)
|
52
|
-
random.shuffle(self.data)
|
53
|
-
return self
|
54
|
-
|
55
|
-
def sample(self, n: int, seed="edsl") -> AgentList:
|
56
|
-
"""Return a random sample of agents.
|
57
|
-
|
58
|
-
:param n: The number of agents to sample.
|
59
|
-
:param seed: The seed for the random number generator.
|
60
|
-
"""
|
61
|
-
import random
|
62
|
-
|
63
|
-
random.seed(seed)
|
64
|
-
return AgentList(random.sample(self.data, n))
|
65
|
-
|
66
|
-
def rename(self, old_name, new_name):
|
67
|
-
"""Rename a trait in the AgentList.
|
68
|
-
|
69
|
-
:param old_name: The old name of the trait.
|
70
|
-
:param new_name: The new name of the trait.
|
71
|
-
"""
|
72
|
-
for agent in self.data:
|
73
|
-
agent.rename(old_name, new_name)
|
74
|
-
return self
|
75
|
-
|
76
|
-
def select(self, *traits) -> AgentList:
|
77
|
-
"""Selects agents with only the references traits.
|
78
|
-
|
79
|
-
>>> from edsl.agents.Agent import Agent
|
80
|
-
>>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
|
81
|
-
>>> al.select('a')
|
82
|
-
AgentList([Agent(traits = {'a': 1}), Agent(traits = {'a': 1})])
|
83
|
-
|
84
|
-
"""
|
85
|
-
|
86
|
-
if len(traits) == 1:
|
87
|
-
traits_to_select = [list(traits)[0]]
|
88
|
-
else:
|
89
|
-
traits_to_select = list(traits)
|
90
|
-
|
91
|
-
return AgentList([agent.select(*traits_to_select) for agent in self.data])
|
92
|
-
|
93
|
-
def filter(self, expression: str) -> AgentList:
|
94
|
-
"""
|
95
|
-
Filter a list of agents based on an expression.
|
96
|
-
|
97
|
-
>>> from edsl.agents.Agent import Agent
|
98
|
-
>>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
|
99
|
-
>>> al.filter("b == 2")
|
100
|
-
AgentList([Agent(traits = {'a': 1, 'b': 2})])
|
101
|
-
"""
|
102
|
-
|
103
|
-
def create_evaluator(agent: "Agent"):
|
104
|
-
"""Create an evaluator for the given result.
|
105
|
-
The 'combined_dict' is a mapping of all values for that Result object.
|
106
|
-
"""
|
107
|
-
return EvalWithCompoundTypes(names=agent.traits)
|
108
|
-
|
109
|
-
try:
|
110
|
-
# iterates through all the results and evaluates the expression
|
111
|
-
new_data = [
|
112
|
-
agent for agent in self.data if create_evaluator(agent).eval(expression)
|
113
|
-
]
|
114
|
-
except Exception as e:
|
115
|
-
print(f"Exception:{e}")
|
116
|
-
raise Exception(f"Error in filter. Exception:{e}")
|
117
|
-
|
118
|
-
return AgentList(new_data)
|
119
|
-
|
120
|
-
@property
|
121
|
-
def all_traits(self):
|
122
|
-
d = {}
|
123
|
-
for agent in self:
|
124
|
-
d.update(agent.traits)
|
125
|
-
return list(d.keys())
|
126
|
-
|
127
|
-
@classmethod
|
128
|
-
def from_csv(cls, file_path: str):
|
129
|
-
"""Load AgentList from a CSV file.
|
130
|
-
|
131
|
-
>>> import csv
|
132
|
-
>>> import os
|
133
|
-
>>> with open('/tmp/agents.csv', 'w') as f:
|
134
|
-
... writer = csv.writer(f)
|
135
|
-
... _ = writer.writerow(['age', 'hair', 'height'])
|
136
|
-
... _ = writer.writerow([22, 'brown', 5.5])
|
137
|
-
>>> al = AgentList.from_csv('/tmp/agents.csv')
|
138
|
-
>>> al
|
139
|
-
AgentList([Agent(traits = {'age': '22', 'hair': 'brown', 'height': '5.5'})])
|
140
|
-
>>> os.remove('/tmp/agents.csv')
|
141
|
-
|
142
|
-
:param file_path: The path to the CSV file.
|
143
|
-
"""
|
144
|
-
from edsl.agents.Agent import Agent
|
145
|
-
|
146
|
-
agent_list = []
|
147
|
-
with open(file_path, "r") as f:
|
148
|
-
reader = csv.DictReader(f)
|
149
|
-
for row in reader:
|
150
|
-
agent_list.append(Agent(row))
|
151
|
-
return cls(agent_list)
|
152
|
-
|
153
|
-
def translate_traits(self, values_codebook: dict[str, str]):
|
154
|
-
"""Translate traits to a new codebook.
|
155
|
-
|
156
|
-
:param codebook: The new codebook.
|
157
|
-
"""
|
158
|
-
for agent in self.data:
|
159
|
-
agent.translate_traits(codebook)
|
160
|
-
return self
|
161
|
-
|
162
|
-
def remove_trait(self, trait: str):
|
163
|
-
"""Remove traits from the AgentList.
|
164
|
-
|
165
|
-
:param traits: The traits to remove.
|
166
|
-
>>> from edsl.agents.Agent import Agent
|
167
|
-
>>> al = AgentList([Agent({'age': 22, 'hair': 'brown', 'height': 5.5}), Agent({'age': 22, 'hair': 'brown', 'height': 5.5})])
|
168
|
-
>>> al.remove_trait('age')
|
169
|
-
AgentList([Agent(traits = {'hair': 'brown', 'height': 5.5}), Agent(traits = {'hair': 'brown', 'height': 5.5})])
|
170
|
-
"""
|
171
|
-
for agent in self.data:
|
172
|
-
_ = agent.remove_trait(trait)
|
173
|
-
return self
|
174
|
-
|
175
|
-
def add_trait(self, trait, values):
|
176
|
-
"""Adds a new trait to every agent, with values taken from values.
|
177
|
-
|
178
|
-
:param trait: The name of the trait.
|
179
|
-
:param values: The valeues(s) of the trait. If a single value is passed, it is used for all agents.
|
180
|
-
|
181
|
-
>>> al = AgentList.example()
|
182
|
-
>>> al.add_trait('new_trait', 1)
|
183
|
-
AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5, 'new_trait': 1}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5, 'new_trait': 1})])
|
184
|
-
>>> al.select('new_trait').to_scenario_list().to_list()
|
185
|
-
[1, 1]
|
186
|
-
>>> al.add_trait('new_trait', [1, 2, 3])
|
187
|
-
Traceback (most recent call last):
|
188
|
-
...
|
189
|
-
ValueError: The passed values have to be the same length as the agent list.
|
190
|
-
"""
|
191
|
-
if not is_iterable(values):
|
192
|
-
value = values
|
193
|
-
for agent in self.data:
|
194
|
-
agent.add_trait(trait, value)
|
195
|
-
return self
|
196
|
-
|
197
|
-
if len(values) != len(self):
|
198
|
-
raise ValueError(
|
199
|
-
"The passed values have to be the same length as the agent list."
|
200
|
-
)
|
201
|
-
for agent, value in zip(self.data, values):
|
202
|
-
agent.add_trait(trait, value)
|
203
|
-
return self
|
204
|
-
|
205
|
-
@staticmethod
|
206
|
-
def get_codebook(file_path: str):
|
207
|
-
"""Return the codebook for a CSV file.
|
208
|
-
|
209
|
-
:param file_path: The path to the CSV file.
|
210
|
-
"""
|
211
|
-
with open(file_path, "r") as f:
|
212
|
-
reader = csv.DictReader(f)
|
213
|
-
return {field: None for field in reader.fieldnames}
|
214
|
-
|
215
|
-
def __hash__(self) -> int:
|
216
|
-
from edsl.utilities.utilities import dict_hash
|
217
|
-
|
218
|
-
data = self.to_dict()
|
219
|
-
# data['agent_list'] = sorted(data['agent_list'], key=lambda x: dict_hash(x)
|
220
|
-
return dict_hash(self._to_dict(sorted=True))
|
221
|
-
|
222
|
-
def _to_dict(self, sorted=False):
|
223
|
-
if sorted:
|
224
|
-
data = self.data[:]
|
225
|
-
data.sort(key=lambda x: hash(x))
|
226
|
-
else:
|
227
|
-
data = self.data
|
228
|
-
|
229
|
-
return {"agent_list": [agent.to_dict() for agent in data]}
|
230
|
-
|
231
|
-
def __eq__(self, other: AgentList) -> bool:
|
232
|
-
return self._to_dict(sorted=True) == other._to_dict(sorted=True)
|
233
|
-
|
234
|
-
@add_edsl_version
|
235
|
-
def to_dict(self):
|
236
|
-
"""Return dictionary of AgentList to serialization."""
|
237
|
-
return self._to_dict()
|
238
|
-
|
239
|
-
def __repr__(self):
|
240
|
-
return f"AgentList({self.data})"
|
241
|
-
|
242
|
-
def print(self, format: Optional[str] = None):
|
243
|
-
"""Print the AgentList."""
|
244
|
-
print_json(json.dumps(self._to_dict()))
|
245
|
-
|
246
|
-
def _repr_html_(self):
|
247
|
-
"""Return an HTML representation of the AgentList."""
|
248
|
-
from edsl.utilities.utilities import data_to_html
|
249
|
-
|
250
|
-
return data_to_html(self.to_dict()["agent_list"])
|
251
|
-
|
252
|
-
def to_scenario_list(self) -> "ScenarioList":
|
253
|
-
"""Return a list of scenarios."""
|
254
|
-
from edsl.scenarios.ScenarioList import ScenarioList
|
255
|
-
from edsl.scenarios.Scenario import Scenario
|
256
|
-
|
257
|
-
return ScenarioList([Scenario(agent.traits) for agent in self.data])
|
258
|
-
|
259
|
-
@classmethod
|
260
|
-
@remove_edsl_version
|
261
|
-
def from_dict(cls, data: dict) -> "AgentList":
|
262
|
-
"""Deserialize the dictionary back to an AgentList object.
|
263
|
-
|
264
|
-
:param: data: A dictionary representing an AgentList.
|
265
|
-
>>> from edsl.agents.Agent import Agent
|
266
|
-
>>> al = AgentList([Agent.example(), Agent.example()])
|
267
|
-
>>> al2 = AgentList.from_dict(al.to_dict())
|
268
|
-
>>> al2 == al
|
269
|
-
True
|
270
|
-
"""
|
271
|
-
from edsl.agents.Agent import Agent
|
272
|
-
|
273
|
-
agents = [Agent.from_dict(agent_dict) for agent_dict in data["agent_list"]]
|
274
|
-
return cls(agents)
|
275
|
-
|
276
|
-
@classmethod
|
277
|
-
def example(cls, randomize: bool = False) -> AgentList:
|
278
|
-
"""
|
279
|
-
Returns an example AgentList instance.
|
280
|
-
|
281
|
-
:param randomize: If True, uses Agent's randomize method.
|
282
|
-
"""
|
283
|
-
from edsl.agents.Agent import Agent
|
284
|
-
|
285
|
-
return cls([Agent.example(randomize), Agent.example(randomize)])
|
286
|
-
|
287
|
-
@classmethod
|
288
|
-
def from_list(self, trait_name: str, values: List[Any]):
|
289
|
-
"""Create an AgentList from a list of values.
|
290
|
-
|
291
|
-
:param trait_name: The name of the trait.
|
292
|
-
:param values: A list of values.
|
293
|
-
"""
|
294
|
-
from edsl.agents.Agent import Agent
|
295
|
-
|
296
|
-
return AgentList([Agent({trait_name: value}) for value in values])
|
297
|
-
|
298
|
-
def __mul__(self, other: AgentList) -> AgentList:
|
299
|
-
"""Takes the cross product of two AgentLists."""
|
300
|
-
from itertools import product
|
301
|
-
|
302
|
-
new_sl = []
|
303
|
-
for s1, s2 in list(product(self, other)):
|
304
|
-
new_sl.append(s1 + s2)
|
305
|
-
return AgentList(new_sl)
|
306
|
-
|
307
|
-
def code(self, string=True) -> Union[str, list[str]]:
|
308
|
-
"""Return code to construct an AgentList.
|
309
|
-
|
310
|
-
>>> al = AgentList.example()
|
311
|
-
>>> print(al.code())
|
312
|
-
from edsl.agents.Agent import Agent
|
313
|
-
from edsl.agents.AgentList import AgentList
|
314
|
-
agent_list = AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
|
315
|
-
"""
|
316
|
-
lines = [
|
317
|
-
"from edsl.agents.Agent import Agent",
|
318
|
-
"from edsl.agents.AgentList import AgentList",
|
319
|
-
]
|
320
|
-
lines.append(f"agent_list = AgentList({self.data})")
|
321
|
-
if string:
|
322
|
-
return "\n".join(lines)
|
323
|
-
return lines
|
324
|
-
|
325
|
-
def rich_print(self) -> Table:
|
326
|
-
"""Display an object as a rich table."""
|
327
|
-
table = Table(title="AgentList")
|
328
|
-
table.add_column("Agents", style="bold")
|
329
|
-
for agent in self.data:
|
330
|
-
table.add_row(agent.rich_print())
|
331
|
-
return table
|
332
|
-
|
333
|
-
|
334
|
-
if __name__ == "__main__":
|
335
|
-
import doctest
|
336
|
-
|
337
|
-
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
1
|
+
"""A list of Agent objects.
|
2
|
+
|
3
|
+
Example usage:
|
4
|
+
|
5
|
+
.. code-block:: python
|
6
|
+
|
7
|
+
al = AgentList([Agent.example(), Agent.example()])
|
8
|
+
len(al)
|
9
|
+
2
|
10
|
+
|
11
|
+
"""
|
12
|
+
|
13
|
+
from __future__ import annotations
|
14
|
+
import csv
|
15
|
+
import json
|
16
|
+
from collections import UserList
|
17
|
+
from typing import Any, List, Optional, Union
|
18
|
+
from rich import print_json
|
19
|
+
from rich.table import Table
|
20
|
+
from simpleeval import EvalWithCompoundTypes
|
21
|
+
from edsl.Base import Base
|
22
|
+
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
23
|
+
|
24
|
+
from collections.abc import Iterable
|
25
|
+
|
26
|
+
|
27
|
+
def is_iterable(obj):
|
28
|
+
return isinstance(obj, Iterable)
|
29
|
+
|
30
|
+
|
31
|
+
class AgentList(UserList, Base):
|
32
|
+
"""A list of Agents."""
|
33
|
+
|
34
|
+
def __init__(self, data: Optional[list["Agent"]] = None):
|
35
|
+
"""Initialize a new AgentList.
|
36
|
+
|
37
|
+
:param data: A list of Agents.
|
38
|
+
"""
|
39
|
+
if data is not None:
|
40
|
+
super().__init__(data)
|
41
|
+
else:
|
42
|
+
super().__init__()
|
43
|
+
|
44
|
+
def shuffle(self, seed: Optional[str] = "edsl") -> AgentList:
|
45
|
+
"""Shuffle the AgentList.
|
46
|
+
|
47
|
+
:param seed: The seed for the random number generator.
|
48
|
+
"""
|
49
|
+
import random
|
50
|
+
|
51
|
+
random.seed(seed)
|
52
|
+
random.shuffle(self.data)
|
53
|
+
return self
|
54
|
+
|
55
|
+
def sample(self, n: int, seed="edsl") -> AgentList:
|
56
|
+
"""Return a random sample of agents.
|
57
|
+
|
58
|
+
:param n: The number of agents to sample.
|
59
|
+
:param seed: The seed for the random number generator.
|
60
|
+
"""
|
61
|
+
import random
|
62
|
+
|
63
|
+
random.seed(seed)
|
64
|
+
return AgentList(random.sample(self.data, n))
|
65
|
+
|
66
|
+
def rename(self, old_name, new_name):
|
67
|
+
"""Rename a trait in the AgentList.
|
68
|
+
|
69
|
+
:param old_name: The old name of the trait.
|
70
|
+
:param new_name: The new name of the trait.
|
71
|
+
"""
|
72
|
+
for agent in self.data:
|
73
|
+
agent.rename(old_name, new_name)
|
74
|
+
return self
|
75
|
+
|
76
|
+
def select(self, *traits) -> AgentList:
|
77
|
+
"""Selects agents with only the references traits.
|
78
|
+
|
79
|
+
>>> from edsl.agents.Agent import Agent
|
80
|
+
>>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
|
81
|
+
>>> al.select('a')
|
82
|
+
AgentList([Agent(traits = {'a': 1}), Agent(traits = {'a': 1})])
|
83
|
+
|
84
|
+
"""
|
85
|
+
|
86
|
+
if len(traits) == 1:
|
87
|
+
traits_to_select = [list(traits)[0]]
|
88
|
+
else:
|
89
|
+
traits_to_select = list(traits)
|
90
|
+
|
91
|
+
return AgentList([agent.select(*traits_to_select) for agent in self.data])
|
92
|
+
|
93
|
+
def filter(self, expression: str) -> AgentList:
|
94
|
+
"""
|
95
|
+
Filter a list of agents based on an expression.
|
96
|
+
|
97
|
+
>>> from edsl.agents.Agent import Agent
|
98
|
+
>>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
|
99
|
+
>>> al.filter("b == 2")
|
100
|
+
AgentList([Agent(traits = {'a': 1, 'b': 2})])
|
101
|
+
"""
|
102
|
+
|
103
|
+
def create_evaluator(agent: "Agent"):
|
104
|
+
"""Create an evaluator for the given result.
|
105
|
+
The 'combined_dict' is a mapping of all values for that Result object.
|
106
|
+
"""
|
107
|
+
return EvalWithCompoundTypes(names=agent.traits)
|
108
|
+
|
109
|
+
try:
|
110
|
+
# iterates through all the results and evaluates the expression
|
111
|
+
new_data = [
|
112
|
+
agent for agent in self.data if create_evaluator(agent).eval(expression)
|
113
|
+
]
|
114
|
+
except Exception as e:
|
115
|
+
print(f"Exception:{e}")
|
116
|
+
raise Exception(f"Error in filter. Exception:{e}")
|
117
|
+
|
118
|
+
return AgentList(new_data)
|
119
|
+
|
120
|
+
@property
|
121
|
+
def all_traits(self):
|
122
|
+
d = {}
|
123
|
+
for agent in self:
|
124
|
+
d.update(agent.traits)
|
125
|
+
return list(d.keys())
|
126
|
+
|
127
|
+
@classmethod
|
128
|
+
def from_csv(cls, file_path: str):
|
129
|
+
"""Load AgentList from a CSV file.
|
130
|
+
|
131
|
+
>>> import csv
|
132
|
+
>>> import os
|
133
|
+
>>> with open('/tmp/agents.csv', 'w') as f:
|
134
|
+
... writer = csv.writer(f)
|
135
|
+
... _ = writer.writerow(['age', 'hair', 'height'])
|
136
|
+
... _ = writer.writerow([22, 'brown', 5.5])
|
137
|
+
>>> al = AgentList.from_csv('/tmp/agents.csv')
|
138
|
+
>>> al
|
139
|
+
AgentList([Agent(traits = {'age': '22', 'hair': 'brown', 'height': '5.5'})])
|
140
|
+
>>> os.remove('/tmp/agents.csv')
|
141
|
+
|
142
|
+
:param file_path: The path to the CSV file.
|
143
|
+
"""
|
144
|
+
from edsl.agents.Agent import Agent
|
145
|
+
|
146
|
+
agent_list = []
|
147
|
+
with open(file_path, "r") as f:
|
148
|
+
reader = csv.DictReader(f)
|
149
|
+
for row in reader:
|
150
|
+
agent_list.append(Agent(row))
|
151
|
+
return cls(agent_list)
|
152
|
+
|
153
|
+
def translate_traits(self, values_codebook: dict[str, str]):
|
154
|
+
"""Translate traits to a new codebook.
|
155
|
+
|
156
|
+
:param codebook: The new codebook.
|
157
|
+
"""
|
158
|
+
for agent in self.data:
|
159
|
+
agent.translate_traits(codebook)
|
160
|
+
return self
|
161
|
+
|
162
|
+
def remove_trait(self, trait: str):
|
163
|
+
"""Remove traits from the AgentList.
|
164
|
+
|
165
|
+
:param traits: The traits to remove.
|
166
|
+
>>> from edsl.agents.Agent import Agent
|
167
|
+
>>> al = AgentList([Agent({'age': 22, 'hair': 'brown', 'height': 5.5}), Agent({'age': 22, 'hair': 'brown', 'height': 5.5})])
|
168
|
+
>>> al.remove_trait('age')
|
169
|
+
AgentList([Agent(traits = {'hair': 'brown', 'height': 5.5}), Agent(traits = {'hair': 'brown', 'height': 5.5})])
|
170
|
+
"""
|
171
|
+
for agent in self.data:
|
172
|
+
_ = agent.remove_trait(trait)
|
173
|
+
return self
|
174
|
+
|
175
|
+
def add_trait(self, trait, values):
|
176
|
+
"""Adds a new trait to every agent, with values taken from values.
|
177
|
+
|
178
|
+
:param trait: The name of the trait.
|
179
|
+
:param values: The valeues(s) of the trait. If a single value is passed, it is used for all agents.
|
180
|
+
|
181
|
+
>>> al = AgentList.example()
|
182
|
+
>>> al.add_trait('new_trait', 1)
|
183
|
+
AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5, 'new_trait': 1}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5, 'new_trait': 1})])
|
184
|
+
>>> al.select('new_trait').to_scenario_list().to_list()
|
185
|
+
[1, 1]
|
186
|
+
>>> al.add_trait('new_trait', [1, 2, 3])
|
187
|
+
Traceback (most recent call last):
|
188
|
+
...
|
189
|
+
ValueError: The passed values have to be the same length as the agent list.
|
190
|
+
"""
|
191
|
+
if not is_iterable(values):
|
192
|
+
value = values
|
193
|
+
for agent in self.data:
|
194
|
+
agent.add_trait(trait, value)
|
195
|
+
return self
|
196
|
+
|
197
|
+
if len(values) != len(self):
|
198
|
+
raise ValueError(
|
199
|
+
"The passed values have to be the same length as the agent list."
|
200
|
+
)
|
201
|
+
for agent, value in zip(self.data, values):
|
202
|
+
agent.add_trait(trait, value)
|
203
|
+
return self
|
204
|
+
|
205
|
+
@staticmethod
|
206
|
+
def get_codebook(file_path: str):
|
207
|
+
"""Return the codebook for a CSV file.
|
208
|
+
|
209
|
+
:param file_path: The path to the CSV file.
|
210
|
+
"""
|
211
|
+
with open(file_path, "r") as f:
|
212
|
+
reader = csv.DictReader(f)
|
213
|
+
return {field: None for field in reader.fieldnames}
|
214
|
+
|
215
|
+
def __hash__(self) -> int:
|
216
|
+
from edsl.utilities.utilities import dict_hash
|
217
|
+
|
218
|
+
data = self.to_dict()
|
219
|
+
# data['agent_list'] = sorted(data['agent_list'], key=lambda x: dict_hash(x)
|
220
|
+
return dict_hash(self._to_dict(sorted=True))
|
221
|
+
|
222
|
+
def _to_dict(self, sorted=False):
|
223
|
+
if sorted:
|
224
|
+
data = self.data[:]
|
225
|
+
data.sort(key=lambda x: hash(x))
|
226
|
+
else:
|
227
|
+
data = self.data
|
228
|
+
|
229
|
+
return {"agent_list": [agent.to_dict() for agent in data]}
|
230
|
+
|
231
|
+
def __eq__(self, other: AgentList) -> bool:
|
232
|
+
return self._to_dict(sorted=True) == other._to_dict(sorted=True)
|
233
|
+
|
234
|
+
@add_edsl_version
|
235
|
+
def to_dict(self):
|
236
|
+
"""Return dictionary of AgentList to serialization."""
|
237
|
+
return self._to_dict()
|
238
|
+
|
239
|
+
def __repr__(self):
|
240
|
+
return f"AgentList({self.data})"
|
241
|
+
|
242
|
+
def print(self, format: Optional[str] = None):
|
243
|
+
"""Print the AgentList."""
|
244
|
+
print_json(json.dumps(self._to_dict()))
|
245
|
+
|
246
|
+
def _repr_html_(self):
|
247
|
+
"""Return an HTML representation of the AgentList."""
|
248
|
+
from edsl.utilities.utilities import data_to_html
|
249
|
+
|
250
|
+
return data_to_html(self.to_dict()["agent_list"])
|
251
|
+
|
252
|
+
def to_scenario_list(self) -> "ScenarioList":
|
253
|
+
"""Return a list of scenarios."""
|
254
|
+
from edsl.scenarios.ScenarioList import ScenarioList
|
255
|
+
from edsl.scenarios.Scenario import Scenario
|
256
|
+
|
257
|
+
return ScenarioList([Scenario(agent.traits) for agent in self.data])
|
258
|
+
|
259
|
+
@classmethod
|
260
|
+
@remove_edsl_version
|
261
|
+
def from_dict(cls, data: dict) -> "AgentList":
|
262
|
+
"""Deserialize the dictionary back to an AgentList object.
|
263
|
+
|
264
|
+
:param: data: A dictionary representing an AgentList.
|
265
|
+
>>> from edsl.agents.Agent import Agent
|
266
|
+
>>> al = AgentList([Agent.example(), Agent.example()])
|
267
|
+
>>> al2 = AgentList.from_dict(al.to_dict())
|
268
|
+
>>> al2 == al
|
269
|
+
True
|
270
|
+
"""
|
271
|
+
from edsl.agents.Agent import Agent
|
272
|
+
|
273
|
+
agents = [Agent.from_dict(agent_dict) for agent_dict in data["agent_list"]]
|
274
|
+
return cls(agents)
|
275
|
+
|
276
|
+
@classmethod
|
277
|
+
def example(cls, randomize: bool = False) -> AgentList:
|
278
|
+
"""
|
279
|
+
Returns an example AgentList instance.
|
280
|
+
|
281
|
+
:param randomize: If True, uses Agent's randomize method.
|
282
|
+
"""
|
283
|
+
from edsl.agents.Agent import Agent
|
284
|
+
|
285
|
+
return cls([Agent.example(randomize), Agent.example(randomize)])
|
286
|
+
|
287
|
+
@classmethod
|
288
|
+
def from_list(self, trait_name: str, values: List[Any]):
|
289
|
+
"""Create an AgentList from a list of values.
|
290
|
+
|
291
|
+
:param trait_name: The name of the trait.
|
292
|
+
:param values: A list of values.
|
293
|
+
"""
|
294
|
+
from edsl.agents.Agent import Agent
|
295
|
+
|
296
|
+
return AgentList([Agent({trait_name: value}) for value in values])
|
297
|
+
|
298
|
+
def __mul__(self, other: AgentList) -> AgentList:
|
299
|
+
"""Takes the cross product of two AgentLists."""
|
300
|
+
from itertools import product
|
301
|
+
|
302
|
+
new_sl = []
|
303
|
+
for s1, s2 in list(product(self, other)):
|
304
|
+
new_sl.append(s1 + s2)
|
305
|
+
return AgentList(new_sl)
|
306
|
+
|
307
|
+
def code(self, string=True) -> Union[str, list[str]]:
|
308
|
+
"""Return code to construct an AgentList.
|
309
|
+
|
310
|
+
>>> al = AgentList.example()
|
311
|
+
>>> print(al.code())
|
312
|
+
from edsl.agents.Agent import Agent
|
313
|
+
from edsl.agents.AgentList import AgentList
|
314
|
+
agent_list = AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
|
315
|
+
"""
|
316
|
+
lines = [
|
317
|
+
"from edsl.agents.Agent import Agent",
|
318
|
+
"from edsl.agents.AgentList import AgentList",
|
319
|
+
]
|
320
|
+
lines.append(f"agent_list = AgentList({self.data})")
|
321
|
+
if string:
|
322
|
+
return "\n".join(lines)
|
323
|
+
return lines
|
324
|
+
|
325
|
+
def rich_print(self) -> Table:
|
326
|
+
"""Display an object as a rich table."""
|
327
|
+
table = Table(title="AgentList")
|
328
|
+
table.add_column("Agents", style="bold")
|
329
|
+
for agent in self.data:
|
330
|
+
table.add_row(agent.rich_print())
|
331
|
+
return table
|
332
|
+
|
333
|
+
|
334
|
+
if __name__ == "__main__":
|
335
|
+
import doctest
|
336
|
+
|
337
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|