Skip to content

Commit

Permalink
Merge pull request #6 from AutoResearch/add-synthetic-models
Browse files Browse the repository at this point in the history
Luce-Choice-Ratio added to synthetic data
  • Loading branch information
TheLemonPig authored Jul 11, 2023
2 parents 15a7a3e + fbc1513 commit 87bc988
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 0 deletions.
242 changes: 242 additions & 0 deletions src/autora/experiment_runner/synthetic/psychology/luce_choice_ratio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
from functools import partial

import numpy as np

from autora.experiment_runner.synthetic.utilities import SyntheticExperimentCollection
from autora.variable import DV, IV, ValueType, VariableCollection


def luce_choice_ratio(
name="Luce-Choice-Ratio",
added_noise=0.01,
resolution=8,
maximum_similarity=10,
focus=0.8,
rng=np.random.default_rng(),
):
"""
Luce-Choice-Ratio
Args:
name: name of the experiment
added_noise: standard deviation of normally distributed noise added to y-values
resolution: number of allowed values for stimulus DVs
maximum_similarity: upperbound for DVs
focus: parameter measuring participant focus
rng: integer used to seed the random number generator
Shepard-Luce Choice Rule according to:
- Equation (4) in Logan, G. D., & Gordon, R. D. (2001).
- and in Executive control of visual attention in dual-task situations.
Psychological review, 108(2), 393.
- Equation (5) in Luce, R. D. (1963). Detection and recognition.
Examples:
First we seed numpy to get replicable results:
>>> np.random.seed(42)
We can instantiate a Shepard-Cue Choice Experiment. We use a seed to get replicable results:
>>> l_s_experiment = luce_choice_ratio(rng=42)
We can look at the name of the experiment:
>>> l_s_experiment.name
'Luce-Choice-Ratio'
To call the ground truth, we can use an attribute of the experiment:
>>> l_s_experiment.ground_truth(np.array([[1,2,3,4]]))
array([[0.21052632]])
We can also run an experiment:
>>> l_s_experiment.experiment_runner(np.array([[1,2,3,4]]))
array([[0.21016246]])
To plot the experiment use:
>>> l_s_experiment.plotter()
>>> plt.show() # doctest: +SKIP
"""
minimum_similarity = 1 / maximum_similarity

params = dict(
name=name,
added_noise=added_noise,
maximum_similarity=maximum_similarity,
minimum_similarity=minimum_similarity,
resolution=resolution,
focus=focus,
rng=rng,
)

similarity_category_A1 = IV(
name="similarity_category_A1",
allowed_values=np.linspace(minimum_similarity, maximum_similarity, resolution),
value_range=(minimum_similarity, maximum_similarity),
units="similarity",
variable_label="Similarity with Category A1",
type=ValueType.REAL,
)

similarity_category_A2 = IV(
name="similarity_category_A2",
allowed_values=np.linspace(minimum_similarity, maximum_similarity, resolution),
value_range=(minimum_similarity, maximum_similarity),
units="similarity",
variable_label="Similarity with Category A2",
type=ValueType.REAL,
)

similarity_category_B1 = IV(
name="similarity_category_B1",
allowed_values=np.linspace(minimum_similarity, maximum_similarity, resolution),
value_range=(minimum_similarity, maximum_similarity),
units="similarity",
variable_label="Similarity with Category B1",
type=ValueType.REAL,
)

similarity_category_B2 = IV(
name="similarity_category_B2",
allowed_values=np.linspace(minimum_similarity, maximum_similarity, resolution),
value_range=(minimum_similarity, maximum_similarity),
units="similarity",
variable_label="Similarity with Category B2",
type=ValueType.REAL,
)

choose_A1 = DV(
name="choose_A1",
value_range=(0, 1),
units="probability",
variable_label="Probability of Choosing A1",
type=ValueType.PROBABILITY,
)

variables = VariableCollection(
independent_variables=[
similarity_category_A1,
similarity_category_A2,
similarity_category_B1,
similarity_category_B2,
],
dependent_variables=[choose_A1],
)

def experiment_runner(
X: np.ndarray,
focus_: float = focus,
added_noise_: float = added_noise,
):
Y = np.zeros((X.shape[0], 1))
for idx, x in enumerate(X):
similarity_A1 = x[0]
similarity_A2 = x[1]
similarity_B1 = x[2]
similarity_B2 = x[3]

y = (similarity_A1 * focus + np.random.normal(0, added_noise_)) / (
similarity_A1 * focus
+ similarity_A2 * focus
+ similarity_B1 * (1 - focus_)
+ similarity_B2 * (1 - focus_)
)
# probability can't be negative or larger than 1 (the noise can make it so)
if y <= 0:
y = 0.0001
elif y >= 1:
y = 0.9999
Y[idx] = y

return Y

ground_truth = partial(experiment_runner, added_noise_=0.0)

def domain():
similarity_A1 = variables.independent_variables[0].allowed_values
similarity_A2 = variables.independent_variables[1].allowed_values
similarity_B1 = variables.independent_variables[2].allowed_values
similarity_B2 = variables.independent_variables[3].allowed_values

X = np.array(
np.meshgrid(
similarity_A1,
similarity_A2,
similarity_B1,
similarity_B2,
)
).T.reshape(-1, 4)

# remove all conditions from X where the focus is 0 and the similarity of A1 is 0
# or the similarity of A2 is 0
X = X[~((X[:, 0] == 0) & (X[:, 1] == 0) & (X[:, 2] == 0) & (X[:, 3] == 0))]
return X

def plotter(
model=None,
):
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt

similarity_A1 = np.linspace(
variables.independent_variables[0].value_range[0],
variables.independent_variables[0].value_range[1],
100,
)

similarity_A2 = 0.5 # 1 - similarity_A1

similarity_B1_list = [0.5, 0.75, 1]
similarity_B2 = 0

colors = mcolors.TABLEAU_COLORS
col_keys = list(colors.keys())
for idx, similarity_B1 in enumerate(similarity_B1_list):
# similarity_B2 = 1 - similarity_B1
X = np.zeros((len(similarity_A1), 4))

X[:, 0] = similarity_A1
X[:, 1] = similarity_A2
X[:, 2] = similarity_B1
X[:, 3] = similarity_B2

y = ground_truth(X)
plt.plot(
similarity_A1.reshape((len(similarity_A1), 1)),
y,
label=f"Similarity to B1 = {similarity_B1} (Original)",
c=colors[col_keys[idx]],
)

if model is not None:
y = model.predict(X)
plt.plot(
similarity_A1,
y,
label=f"Similarity to B1 = {similarity_B1} (Recovered)",
c=colors[col_keys[idx]],
linestyle="--",
)

x_limit = [np.min(similarity_A1), np.max(similarity_A1)]
y_limit = [0, 1]
x_label = "Similarity to Category A1"
y_label = "Probability of Selecting Category A1"

plt.xlim(x_limit)
plt.ylim(y_limit)
plt.xlabel(x_label, fontsize="large")
plt.ylabel(y_label, fontsize="large")
plt.legend(loc=4, fontsize="medium")
plt.title("Shepard-Luce Choice Ratio", fontsize="x-large")

collection = SyntheticExperimentCollection(
name=name,
description=luce_choice_ratio.__doc__,
variables=variables,
experiment_runner=experiment_runner,
ground_truth=ground_truth,
domain=domain,
plotter=plotter,
params=params,
factory_function=luce_choice_ratio,
)
return collection
4 changes: 4 additions & 0 deletions tests/test_bundled_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
expected_value_theory,
)
from autora.experiment_runner.synthetic.economics.prospect_theory import prospect_theory
from autora.experiment_runner.synthetic.psychology.luce_choice_ratio import (
luce_choice_ratio,
)
from autora.experiment_runner.synthetic.psychophysics.weber_fechner_law import (
weber_fechner_law,
)
Expand All @@ -16,6 +19,7 @@
all_bundled_models = [
("expected_value_theory", expected_value_theory),
("prospect_theory", prospect_theory),
("luce_choice_ratio", luce_choice_ratio),
("template_experiment", template_experiment),
("weber_fechner_law", weber_fechner_law),
]
Expand Down

0 comments on commit 87bc988

Please sign in to comment.