adaptivetesting 1.1.0__tar.gz → 1.1.1__tar.gz

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.
Files changed (99) hide show
  1. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/PKG-INFO +1 -1
  2. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/__gen_response_pattern.py +19 -5
  3. adaptivetesting-1.1.1/adaptivetesting/tests/test_generate_response_pattern.py +106 -0
  4. adaptivetesting-1.1.1/examples/.gitignore +3 -0
  5. adaptivetesting-1.1.1/examples/simulation.py +134 -0
  6. adaptivetesting-1.1.0/examples/real_world.py → adaptivetesting-1.1.1/examples/testing.py +17 -14
  7. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/pyproject.toml +7 -1
  8. adaptivetesting-1.1.1/uv.lock +5341 -0
  9. adaptivetesting-1.1.0/adaptivetesting/tests/test_generate_response_pattern.py +0 -59
  10. adaptivetesting-1.1.0/examples/basic.py +0 -39
  11. adaptivetesting-1.1.0/profiler/profile_bm_beta_simu.py +0 -49
  12. adaptivetesting-1.1.0/profiler/profile_bm_normal_simu.py +0 -48
  13. adaptivetesting-1.1.0/profiler/profile_ml_simu.py +0 -45
  14. adaptivetesting-1.1.0/profiler/profile_sim_pool.py +0 -42
  15. adaptivetesting-1.1.0/uv.lock +0 -1075
  16. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.github/workflows/publish.yml +0 -0
  17. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.github/workflows/python-test.yml +0 -0
  18. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.gitignore +0 -0
  19. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.gitlab-ci.yml +0 -0
  20. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.python-version +0 -0
  21. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.vscode/extensions.json +0 -0
  22. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.vscode/settings.json +0 -0
  23. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/BREAKING_CHANGES.md +0 -0
  24. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/LICENSE +0 -0
  25. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/README.md +0 -0
  26. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/__init__.py +0 -0
  27. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/data/__csv_context.py +0 -0
  28. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/data/__init__.py +0 -0
  29. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/data/__pickle_context.py +0 -0
  30. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__default_implementation.py +0 -0
  31. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__init__.py +0 -0
  32. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__pre_test.py +0 -0
  33. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__semi_implementation.py +0 -0
  34. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__test_assembler.py +0 -0
  35. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/__init__.py +0 -0
  36. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__bayes_modal_estimation.py +0 -0
  37. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__expect_a_posteriori.py +0 -0
  38. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__functions/__bayes.py +0 -0
  39. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__functions/__estimators.py +0 -0
  40. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__init__.py +0 -0
  41. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__ml_estimation.py +0 -0
  42. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__prior.py +0 -0
  43. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__test_information.py +0 -0
  44. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/item_selection/__init__.py +0 -0
  45. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/item_selection/__maximum_information_criterion.py +0 -0
  46. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/item_selection/__urrys_rule.py +0 -0
  47. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__adaptive_test.py +0 -0
  48. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__algorithm_exception.py +0 -0
  49. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__init__.py +0 -0
  50. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__item_pool.py +0 -0
  51. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__item_selection_exception.py +0 -0
  52. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__misc.py +0 -0
  53. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__test_item.py +0 -0
  54. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__test_result.py +0 -0
  55. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/services/__estimator_interface.py +0 -0
  56. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/services/__init__.py +0 -0
  57. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/services/__item_selection_protocol.py +0 -0
  58. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/services/__test_results_interface.py +0 -0
  59. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/simulation/__init__.py +0 -0
  60. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/simulation/__simulation.py +0 -0
  61. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/__init__.py +0 -0
  62. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_adaptive_test.py +0 -0
  63. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_bayes_modal.py +0 -0
  64. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_eap.py +0 -0
  65. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_information_test.py +0 -0
  66. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_item_selection.py +0 -0
  67. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_load_test_items.py +0 -0
  68. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_mle.py +0 -0
  69. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_pretest.py +0 -0
  70. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_simulation.py +0 -0
  71. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_standard_error.py +0 -0
  72. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_test_assembler.py +0 -0
  73. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/Makefile +0 -0
  74. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/data.md +0 -0
  75. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/implementations.md +0 -0
  76. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/index.md +0 -0
  77. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/make.bat +0 -0
  78. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/math.md +0 -0
  79. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/models.md +0 -0
  80. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/readme.md +0 -0
  81. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/services.md +0 -0
  82. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/simulation.md +0 -0
  83. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/_static/logo.svg +0 -0
  84. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/_static/main.css +0 -0
  85. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/conf.py +0 -0
  86. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/data.rst +0 -0
  87. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/implementations.rst +0 -0
  88. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/index.rst +0 -0
  89. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/math.rst +0 -0
  90. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/models.rst +0 -0
  91. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/services.rst +0 -0
  92. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/simulation.rst +0 -0
  93. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/tests.rst +0 -0
  94. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/tests.md +0 -0
  95. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/images/default.svg +0 -0
  96. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/images/semi-adaptive.svg +0 -0
  97. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/mypy.ini +0 -0
  98. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/requirements.txt +0 -0
  99. {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adaptivetesting
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: adaptivetesting is a Python package that can be used to simulate and evaluate custom CAT scenarios as well as implement them in real-world testing scenarios from a single codebase
5
5
  Project-URL: Homepage, https://github.com/condecon/adaptivetesting
6
6
  Project-URL: Documentation, https://github.com/condecon/adaptivetesting/tree/main/docs
@@ -17,7 +17,12 @@ def generate_response_pattern(ability: float,
17
17
  Returns:
18
18
  list[int]: response pattern
19
19
  """
20
- # check if items is list of items or ItemPool
20
+ # Set seed once at the beginning if provided
21
+ if seed is not None:
22
+ rng = np.random.RandomState(seed)
23
+ else:
24
+ rng = np.random.RandomState()
25
+
21
26
  responses: list[int] = []
22
27
 
23
28
  for item in items:
@@ -27,10 +32,19 @@ def generate_response_pattern(ability: float,
27
32
  c=np.array(item.c),
28
33
  d=np.array(item.d))
29
34
 
30
- # simulate response based on probability of scucess
31
- if seed is not None:
32
- np.random.seed(seed)
33
- response = int(np.random.binomial(n=1, p=probability_of_success)) # ensure that the result is not an array
35
+ # Handle numpy scalar/array return properly
36
+ if hasattr(probability_of_success, 'item'):
37
+ prob_scalar = probability_of_success.item()
38
+ else:
39
+ prob_scalar = float(probability_of_success)
40
+
41
+ # Validate probability bounds
42
+ if not (0 <= prob_scalar <= 1):
43
+ raise ValueError(f"Invalid probability: {prob_scalar}. Must be between 0 and 1.")
44
+
45
+ # simulate response based on probability of success
46
+ random_val = rng.random_sample()
47
+ response = 1 if random_val < prob_scalar else 0
34
48
  responses.append(response)
35
49
 
36
50
  return responses
@@ -0,0 +1,106 @@
1
+ # flake8: noqa
2
+ import unittest
3
+ import math
4
+ from adaptivetesting.models import ItemPool
5
+ from adaptivetesting.math.estimators import MLEstimator
6
+ from adaptivetesting.math import generate_response_pattern
7
+
8
+
9
+ source_dictionary = {"a": [1.0507,
10
+ 0.9943,
11
+ 0.9914,
12
+ 1.2737,
13
+ 0.9548,
14
+ 1.3033,
15
+ 0.6902,
16
+ 1.1169,
17
+ 1.0248,
18
+ 1.0432,
19
+ 1.0759,
20
+ 0.8995,
21
+ 0.9334,
22
+ 0.7963,
23
+ 0.7856,
24
+ 1.0607,
25
+ 1.0896,
26
+ 1.0106,
27
+ 1.1845,
28
+ 1.41,
29
+ 0.9018,
30
+ 0.5382,
31
+ 1.2011,
32
+ 0.8582,
33
+ 0.8624, 1.2051, 0.943, 0.7559, 1.0363, 0.9722, 1.0012, 1.0771, 0.9259, 1.1289, 0.9559, 1.0664, 1.2194, 1.087, 0.9348, 1.2298, 1.1987, 1.1097, 1.0477, 0.8744, 1.2721, 0.8799, 1.4375, 1.3065, 0.9529, 0.7947],
34
+ "b":
35
+ [-0.5605, -0.2302, 1.5587, 0.0705, 0.1293, 1.7151, 0.4609, -1.2651, -0.6869, -0.4457, 1.2241, 0.3598, 0.4008, 0.1107, -0.5558, 1.7869, 0.4979, -1.9666, 0.7014, -0.4728, -1.0678, -0.218, -1.026, -0.7289, -0.625, -1.6867, 0.8378, 0.1534, -1.1381, 1.2538, 0.4265, -0.2951, 0.8951, 0.8781, 0.8216, 0.6886, 0.5539, -0.0619, -0.306, -0.3805, -0.6947, -0.2079, -1.2654, 2.169, 1.208, -1.1231, -
36
+ 0.4029, -0.4667, 0.78, -0.0834], "c": [0.0597, 0.2406, 0.1503, 0.1288, 0.1006, 0.2201, 0.091, 0.0721, 0.0427, 0.043, 0.1205, 0.0632, 0.0541, 0.1686, 0.0119, 0.1752, 0.088, 0.1022, 0.2052, 0.2297, 0.0706, 0.2403, 0.1821, 0.1716, 0.0132, 0.0988, 0.1195, 0.1401, 0.1746, 0.2289, 0.1546, 0.1071, 0.1355, 0.0146, 0.0652, 0.0993, 0.0494, 0.208, 0.0382, 0.2009, 0.1367, 0.1656, 0.0429, 0.1583, 0.078, 0.1811, 0.0997, 0.2423, 0.2418, 0.1817], "d": [0.8143, 0.8054, 0.8983, 0.8169, 0.8828, 0.9463, 0.792, 0.8511, 0.8679, 0.967, 0.9814, 0.9705, 0.9185, 0.9875, 0.8791, 0.8941, 0.8341, 0.8368, 0.755, 0.8757, 0.9678, 0.7516, 0.768, 0.7911, 0.9426, 0.9338, 0.993, 0.8666, 0.7686, 0.9122, 0.9396, 0.7843, 0.8491, 0.8062, 0.7645, 0.849, 0.7662, 0.8065, 0.7637, 0.9176, 0.8244, 0.7752, 0.768, 0.9701, 0.9386, 0.9542, 0.9955, 0.7759, 0.7748, 0.9497]}
37
+
38
+
39
+ class TestGenerateResponsePattern(unittest.TestCase):
40
+ def test_compare_generation_to_estimation(self):
41
+ item_pool = ItemPool.load_from_dict(source_dictionary)
42
+
43
+ # results
44
+ results = []
45
+
46
+ for i in range(100):
47
+ # generate pattern
48
+ responses = generate_response_pattern(0, item_pool.test_items, seed=i)
49
+
50
+ # estimate
51
+ estimator = MLEstimator(responses, item_pool.test_items)
52
+ estimation = estimator.get_estimation()
53
+ results.append(estimation)
54
+
55
+ mean = sum(results) / len(results)
56
+ print(mean)
57
+ self.assertAlmostEqual(mean, 0, delta=0.3)
58
+
59
+ def test_debug_probabilities(self):
60
+ from adaptivetesting.math.estimators.__functions.__estimators import probability_y1
61
+ import numpy as np
62
+
63
+ item_pool = ItemPool.load_from_dict(source_dictionary)
64
+
65
+ print("First 5 items - Expected probabilities for ability=0:")
66
+ for i, item in enumerate(item_pool.test_items[:5]):
67
+ prob = probability_y1(mu=np.array(0.0),
68
+ a=np.array(item.a),
69
+ b=np.array(item.b),
70
+ c=np.array(item.c),
71
+ d=np.array(item.d))
72
+ print(f"Item {i}: a={item.a:.3f}, b={item.b:.3f}, c={item.c:.3f}, d={item.d:.3f} -> P={float(prob):.3f}")
73
+
74
+ def test_calculate_expected_vs_actual(self):
75
+ from adaptivetesting.math.estimators.__functions.__estimators import probability_y1
76
+ import numpy as np
77
+
78
+ item_pool = ItemPool.load_from_dict(source_dictionary)
79
+
80
+ # Calculate expected probabilities for all items
81
+ expected_probs = []
82
+ for item in item_pool.test_items:
83
+ prob = probability_y1(mu=np.array(0.0),
84
+ a=np.array(item.a),
85
+ b=np.array(item.b),
86
+ c=np.array(item.c),
87
+ d=np.array(item.d))
88
+ expected_probs.append(float(prob))
89
+
90
+ expected_total = sum(expected_probs)
91
+ expected_percentage = expected_total / len(expected_probs) * 100
92
+
93
+ percentage_results = []
94
+ for i in range(100):
95
+ # Generate actual responses
96
+ responses = generate_response_pattern(0, item_pool.test_items, seed=i)
97
+ actual_correct = sum(responses)
98
+ actual_percentage = actual_correct / len(responses) * 100
99
+ percentage_results.append(actual_percentage)
100
+
101
+ print(f"Expected percentage correct: {expected_percentage:.1f}%")
102
+ print(f"Actual percentage correct: {actual_percentage:.1f}%")
103
+
104
+ # The difference should be within reasonable bounds for random sampling
105
+ # With 50 items, we expect some variation
106
+ self.assertAlmostEqual(actual_percentage, expected_percentage, delta=3)
@@ -0,0 +1,3 @@
1
+ item_pool.csv
2
+ data/
3
+ *.pdf
@@ -0,0 +1,134 @@
1
+ # flake8: noqa
2
+ import pandas as pd
3
+ previc_item_pool = pd.read_csv("item_pool.csv")
4
+ previc_item_pool.head()
5
+
6
+ from adaptivetesting.models import ItemPool
7
+ item_pool = ItemPool.load_from_list(
8
+ b=previc_item_pool["Difficulty"]
9
+ )
10
+
11
+ from scipy.stats import norm
12
+ from numpy.random import seed
13
+ seed(1234)
14
+ theta_samples = norm.rvs(loc=0, scale=2, size=100)
15
+
16
+
17
+ from adaptivetesting.implementations import TestAssembler
18
+ from adaptivetesting.simulation import SimulationPool
19
+ from adaptivetesting.models import ResultOutputFormat, StoppingCriterion
20
+ from adaptivetesting.math.item_selection import maximum_information_criterion
21
+ from adaptivetesting.math.estimators import BayesModal, NormalPrior
22
+
23
+ tests = [
24
+ TestAssembler(
25
+ item_pool=item_pool,
26
+ simulation_id="example_bm",
27
+ participant_id=str(index),
28
+ ability_estimator=BayesModal,
29
+ estimator_args={
30
+ "prior": NormalPrior(0,1),
31
+ "optimization_interval":(-10, 10)
32
+ },
33
+ item_selector=maximum_information_criterion,
34
+ true_ability_level=theta,
35
+ simulation=True,
36
+ seed=index
37
+ )
38
+ for index, theta in enumerate(theta_samples)
39
+ ]
40
+
41
+ sim_pool = SimulationPool(
42
+ adaptive_tests=tests,
43
+ test_result_output=ResultOutputFormat.CSV,
44
+ criterion=StoppingCriterion.SE,
45
+ value=0.4
46
+ )
47
+ sim_pool.start()
48
+
49
+ import os
50
+
51
+ dfs = []
52
+ for i in range(100):
53
+ filename = f"data/example_bm/{i}.csv"
54
+ if os.path.exists(filename):
55
+ try:
56
+ df = pd.read_csv(filename)
57
+ # Get the last row of every file
58
+ last_row = df.tail(1)
59
+ dfs.append(last_row)
60
+ # If there is an error, skip the file
61
+ except Exception:
62
+ pass
63
+ results_df = pd.concat(dfs, ignore_index=True)
64
+ mse = ((results_df["ability_estimation"] - results_df["true_ability_level"]) ** 2).mean()
65
+ print(f"MSE of ability estimates: {mse:.4f}")
66
+
67
+
68
+ import matplotlib.pyplot as plt
69
+
70
+ fig, ax = plt.subplots()
71
+ ax.scatter(results_df["true_ability_level"],
72
+ results_df["ability_estimation"],
73
+ label="BM")
74
+ ax.plot(results_df["ability_estimation"],
75
+ results_df["ability_estimation"],
76
+ color="black")
77
+ ax.set_xlabel("True ability level")
78
+ ax.set_ylabel("Estimated ability level")
79
+ fig.savefig("sim_results_bm.pdf")
80
+
81
+ # other example
82
+ from adaptivetesting.math.estimators import ExpectedAPosteriori, CustomPrior
83
+ from scipy.stats.distributions import t
84
+
85
+ tests = [
86
+ TestAssembler(
87
+ item_pool=item_pool,
88
+ simulation_id="example_eap",
89
+ participant_id=str(index),
90
+ ability_estimator=ExpectedAPosteriori,
91
+ estimator_args={
92
+ "prior": CustomPrior(t, 3),
93
+ "optimization_interval":(-10, 10)
94
+ },
95
+ item_selector=maximum_information_criterion,
96
+ true_ability_level=theta,
97
+ simulation=True,
98
+ seed=index
99
+ )
100
+ for index, theta in enumerate(theta_samples)
101
+ ]
102
+
103
+ sim_pool = SimulationPool(
104
+ adaptive_tests=tests,
105
+ test_result_output=ResultOutputFormat.CSV,
106
+ criterion=StoppingCriterion.SE,
107
+ value=0.4
108
+ )
109
+ sim_pool.start()
110
+
111
+ dfs = []
112
+ for i in range(100):
113
+ filename = f"data/example_eap/{i}.csv"
114
+ if os.path.exists(filename):
115
+ try:
116
+ df = pd.read_csv(filename)
117
+ # Get the last row of every file
118
+ last_row = df.tail(1)
119
+ dfs.append(last_row)
120
+ # If there is an error, skip the file
121
+ except Exception:
122
+ pass
123
+ results_df = pd.concat(dfs, ignore_index=True)
124
+ mse = ((results_df["ability_estimation"] - results_df["true_ability_level"]) ** 2).mean()
125
+ print(f"MSE of ability estimates: {mse:.4f}")
126
+
127
+
128
+ ax.scatter(results_df["true_ability_level"],
129
+ results_df["ability_estimation"],
130
+ label="EAP")
131
+ ax.set_xlabel("True ability level")
132
+ ax.set_ylabel("Estimated ability level")
133
+ ax.legend()
134
+ fig.savefig("sim_results_eap.pdf")
@@ -1,31 +1,33 @@
1
+ # flake8: noqa
2
+ # setup item pool
3
+ import pandas as pd
4
+ previc_item_pool = pd.read_csv("item_pool.csv")
5
+ previc_item_pool.head()
6
+
7
+ from adaptivetesting.models import ItemPool
8
+ item_pool = ItemPool.load_from_list(
9
+ b=previc_item_pool["Difficulty"]
10
+ )
11
+
12
+ # import psychopy
1
13
  from psychopy import visual, event
2
14
  from psychopy.hardware import keyboard
3
15
  from adaptivetesting.implementations import TestAssembler
4
16
  from adaptivetesting.models import AdaptiveTest, ItemPool, TestItem
5
17
  from adaptivetesting.data import CSVContext
6
- from adaptivetesting.math.estimators import BayesModal, CustomPrior
18
+ from adaptivetesting.math.estimators import ExpectedAPosteriori, CustomPrior
7
19
  from adaptivetesting.math.item_selection import maximum_information_criterion
8
20
  from scipy.stats import t
9
21
  import pandas as pd
10
22
 
11
-
12
- # Create item pool from DataFrame
13
- items_data = pd.DataFrame({
14
- "a": [1.32, 1.07, 0.84, 1.19, 0.95], # discrimination
15
- "b": [-0.63, 0.18, -0.84, 0.41, -0.25], # difficulty
16
- "c": [0.17, 0.10, 0.19, 0.15, 0.12], # guessing
17
- "d": [0.87, 0.93, 1.0, 0.89, 0.94] # upper asymptote
18
- })
19
- item_pool = ItemPool.load_from_dataframe(items_data)
20
-
21
23
  # Create adaptive test
22
24
  adaptive_test: AdaptiveTest = TestAssembler(
23
25
  item_pool=item_pool,
24
26
  simulation_id="example",
25
27
  participant_id="dummy",
26
- ability_estimator=BayesModal,
28
+ ability_estimator=ExpectedAPosteriori,
27
29
  estimator_args={
28
- "prior": CustomPrior(t, 100),
30
+ "prior": CustomPrior(t, 3),
29
31
  "optimization_interval":(-10, 10)
30
32
  },
31
33
  item_selector=maximum_information_criterion,
@@ -51,7 +53,8 @@ keyboard.Keyboard()
51
53
  def get_response(item: TestItem) -> int:
52
54
  # get index
53
55
  item_difficulty: float = item.b
54
- stimuli: str = [item for item in item_pool.test_items if item["Difficulty"] == item_difficulty][0]["word"]
56
+ # select corresponding word from item pool data frame
57
+ stimuli: str = previc_item_pool.loc[previc_item_pool["Difficulty"] == item_difficulty, "word"].values[0]
55
58
 
56
59
  # create text box and display stimulus
57
60
  text_box = visual.TextBox2(win=win,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name="adaptivetesting"
3
- version="1.1.0"
3
+ version="1.1.1"
4
4
  author="Jonas Engicht"
5
5
  author_email="jonas.engicht@uni-jena.de"
6
6
  description="adaptivetesting is a Python package that can be used to simulate and evaluate custom CAT scenarios as well as implement them in real-world testing scenarios from a single codebase"
@@ -37,6 +37,12 @@ dev = [
37
37
  "sphinx-markdown-builder>=0.6.8",
38
38
  "types-tqdm>=4.67.0.20250516",
39
39
  ]
40
+ plot = [
41
+ "matplotlib>=3.10.5",
42
+ ]
43
+ test = [
44
+ "psychopy>=2025.1.1",
45
+ ]
40
46
 
41
47
  [tool.uv.sources]
42
48
  adaptivetesting = { workspace = true }