pysips 0.0.0__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.
@@ -0,0 +1,156 @@
1
+ import pytest
2
+
3
+ from pysips.crossover_proposal import CrossoverProposal
4
+
5
+ IMPORTMODULE = CrossoverProposal.__module__
6
+
7
+
8
+ class TestCrossoverProposal:
9
+
10
+ @pytest.fixture
11
+ def mock_model(self, mocker):
12
+ """Create a mock model for testing"""
13
+ mock_model = mocker.MagicMock()
14
+ mock_model.__eq__.side_effect = lambda x: x is mock_model
15
+ return mock_model
16
+
17
+ @pytest.fixture
18
+ def mock_gene_pool(self, mocker, mock_model):
19
+ """Create a mock gene pool with several models"""
20
+ model1 = mocker.MagicMock()
21
+ model2 = mocker.MagicMock()
22
+
23
+ # Configure models to be different from each other
24
+ model1.__eq__.side_effect = lambda x: x is model1
25
+ model2.__eq__.side_effect = lambda x: x is model2
26
+
27
+ return [mock_model, model1, model2]
28
+
29
+ def test_initialization(self, mock_gene_pool, mocker):
30
+ """Test proper initialization of CrossoverProposal"""
31
+ # Mock dependencies
32
+ mock_agraph_crossover = mocker.MagicMock()
33
+ mocker.patch(
34
+ f"{IMPORTMODULE}.AGraphCrossover", return_value=mock_agraph_crossover
35
+ )
36
+ mock_random = mocker.patch("numpy.random.default_rng")
37
+
38
+ # Initialize CrossoverProposal
39
+ seed = 42
40
+ crossover_proposal = CrossoverProposal(mock_gene_pool, seed=seed)
41
+
42
+ # Assertions
43
+ assert crossover_proposal._gene_pool == mock_gene_pool
44
+ mock_random.assert_called_once_with(seed)
45
+
46
+ def test_call_method_selects_different_parent(
47
+ self, mock_gene_pool, mock_model, mocker
48
+ ):
49
+ """Test that __call__ selects a parent different from the input model"""
50
+ # Mock AGraphCrossover
51
+ mock_crossover = mocker.MagicMock()
52
+ child_1 = mocker.MagicMock(name="child_1")
53
+ child_2 = mocker.MagicMock(name="child_2")
54
+ mock_crossover.return_value = (child_1, child_2)
55
+ mocker.patch(f"{IMPORTMODULE}.AGraphCrossover", return_value=mock_crossover)
56
+
57
+ # Mock random number generator
58
+ mock_rng = mocker.MagicMock()
59
+ mock_rng.integers.side_effect = [0, 1]
60
+ mock_rng.random.return_value = 0.4 # Will select first child
61
+
62
+ # Initialize CrossoverProposal with mocked RNG
63
+ crossover_proposal = CrossoverProposal(mock_gene_pool)
64
+ crossover_proposal._rng = mock_rng
65
+
66
+ # Call the method
67
+ result = crossover_proposal(mock_model)
68
+
69
+ # Assertions
70
+ # Check that crossover was called with model and a different parent
71
+ mock_crossover.assert_called_once()
72
+ args = mock_crossover.call_args[0]
73
+ assert args[0] == mock_model # First arg is the input model
74
+ assert args[1] == mock_gene_pool[1] # Second arg should be a different model
75
+ assert args[1] != mock_model # Ensure it's not the same model
76
+
77
+ # Check we got the expected child
78
+ assert result == child_1
79
+
80
+ @pytest.mark.parametrize(
81
+ "random_value, expected_child_index",
82
+ [
83
+ (0.3, 0),
84
+ (0.8, 1),
85
+ ],
86
+ )
87
+ def test_call_method_selects_child_based_on_random(
88
+ self, mock_gene_pool, mock_model, mocker, random_value, expected_child_index
89
+ ):
90
+ """Test that __call__ selects the appropriate child based on random value"""
91
+ mock_crossover = mocker.MagicMock()
92
+ child_1 = mocker.MagicMock(name="child_1")
93
+ child_2 = mocker.MagicMock(name="child_2")
94
+ children = (child_1, child_2)
95
+ mock_crossover.return_value = children
96
+ mocker.patch(f"{IMPORTMODULE}.AGraphCrossover", return_value=mock_crossover)
97
+
98
+ # Mock random number generator
99
+ mock_rng = mocker.MagicMock()
100
+ mock_rng.integers.return_value = 1
101
+ mock_rng.random.return_value = random_value
102
+
103
+ # Initialize and call
104
+ crossover_proposal = CrossoverProposal(mock_gene_pool)
105
+ crossover_proposal._rng = mock_rng
106
+ result = crossover_proposal(mock_model)
107
+
108
+ # Assertions
109
+ expected_child = children[expected_child_index]
110
+ assert result == expected_child
111
+
112
+ def test_update_method(self, mock_gene_pool, mocker):
113
+ """Test update method updates the gene pool"""
114
+ # Mock AGraphCrossover
115
+ mocker.patch(f"{IMPORTMODULE}.AGraphCrossover")
116
+
117
+ # Initialize
118
+ crossover_proposal = CrossoverProposal(mock_gene_pool)
119
+
120
+ # Create new gene pool
121
+ new_model1 = mocker.MagicMock(name="new_model1")
122
+ new_model2 = mocker.MagicMock(name="new_model2")
123
+ new_gene_pool = (new_model1, new_model2)
124
+
125
+ # Update gene pool
126
+ crossover_proposal.update(new_gene_pool)
127
+
128
+ # Assertions
129
+ assert crossover_proposal._gene_pool == list(new_gene_pool)
130
+
131
+ # @pytest.mark.parametrize("seed", [None, 0, 42, 12345])
132
+ # def test_random_seed(self, mock_gene_pool, seed, mocker):
133
+ # """Test that random seed is properly used"""
134
+ # # Mock AGraphCrossover
135
+ # mocker.patch(f"{IMPORTMODULE}.AGraphCrossover")
136
+
137
+ # # Mock numpy random generator
138
+ # mock_rng = mocker.patch("numpy.random.default_rng")
139
+
140
+ # # Initialize with different seeds
141
+ # CrossoverProposal(mock_gene_pool, seed=seed)
142
+
143
+ # # Check the RNG was initialized with the right seed
144
+ # mock_rng.assert_called_once_with(seed)
145
+
146
+ def test_empty_gene_pool_error(self, mocker):
147
+ """Test that an empty gene pool results in appropriate failure"""
148
+ # Mock AGraphCrossover
149
+ mocker.patch(f"{IMPORTMODULE}.AGraphCrossover")
150
+
151
+ # Initialize with empty pool
152
+ crossover_proposal = CrossoverProposal([])
153
+
154
+ # Call should raise exception due to empty pool
155
+ with pytest.raises(ValueError) as excinfo:
156
+ crossover_proposal(mocker.MagicMock())
@@ -0,0 +1,111 @@
1
+ import pytest
2
+ import numpy as np
3
+
4
+ from pysips.laplace_nmll import LaplaceNmll
5
+
6
+ IMPORTMODULE = LaplaceNmll.__module__
7
+
8
+
9
+ class TestLaplaceNmll:
10
+
11
+ @pytest.fixture
12
+ def sample_data(self):
13
+ """Fixture to provide sample data for tests."""
14
+ X = np.array([[1, 2], [3, 4], [5, 6]])
15
+ y = np.array([10, 20, 30])
16
+ return X, y
17
+
18
+ @pytest.fixture
19
+ def mock_model(self, mocker):
20
+ """Fixture to provide a mock bingo AGraph model."""
21
+ model = mocker.MagicMock()
22
+ model.get_local_optimization_params.return_value = np.array([1.0, 2.0])
23
+ return model
24
+
25
+ def test_negative_is_applied_to_regression_output(
26
+ self, sample_data, mock_model, mocker
27
+ ):
28
+ X, y = sample_data
29
+
30
+ mock_regression = mocker.MagicMock(return_value=-5.0)
31
+ mocker.patch(f"{IMPORTMODULE}.ExplicitRegression", return_value=mock_regression)
32
+
33
+ mock_scipy_optimizer = mocker.MagicMock()
34
+ mocker.patch(
35
+ f"{IMPORTMODULE}.ScipyOptimizer",
36
+ return_value=mock_scipy_optimizer,
37
+ )
38
+
39
+ laplace_nmll = LaplaceNmll(X, y)
40
+ result = laplace_nmll(mock_model)
41
+
42
+ assert result == 5.0
43
+
44
+ @pytest.mark.parametrize("opt_restarts", [1, 3, 5, 10])
45
+ def test_number_of_restarts(self, sample_data, mock_model, mocker, opt_restarts):
46
+ """Test that the optimizer is run the correct number of times based on opt_restarts."""
47
+ X, y = sample_data
48
+
49
+ mock_regression = mocker.MagicMock()
50
+ mock_regression.return_value = -1.0 # Constant return value
51
+ mocker.patch(f"{IMPORTMODULE}.ExplicitRegression", return_value=mock_regression)
52
+
53
+ mock_scipy_optimizer = mocker.MagicMock()
54
+ mocker.patch(
55
+ f"{IMPORTMODULE}.ScipyOptimizer",
56
+ return_value=mock_scipy_optimizer,
57
+ )
58
+
59
+ laplace_nmll = LaplaceNmll(X, y, opt_restarts=opt_restarts)
60
+ laplace_nmll(mock_model)
61
+ assert mock_scipy_optimizer.call_count == opt_restarts
62
+
63
+ def test_constants_kept_from_best_trial(self, sample_data, mock_model, mocker):
64
+ """Test that constants are kept from optimizer trial with highest nmll."""
65
+ X, y = sample_data
66
+
67
+ # Return increasingly better values (-3 > -5 > -10 when negated)
68
+ mock_regression = mocker.MagicMock()
69
+ mock_regression.side_effect = [10.0, 5.0, 3.0]
70
+ mocker.patch(f"{IMPORTMODULE}.ExplicitRegression", return_value=mock_regression)
71
+
72
+ # Create different parameter sets for different optimization runs
73
+ mock_scipy_optimizer = mocker.MagicMock()
74
+ params_run1 = np.array([1.0, 1.0])
75
+ params_run2 = np.array([2.0, 2.0])
76
+ params_run3 = np.array([3.0, 3.0]) # This should be kept as the best
77
+ mock_model.get_local_optimization_params.side_effect = [
78
+ params_run1,
79
+ params_run2,
80
+ params_run3,
81
+ ]
82
+ mocker.patch(
83
+ f"{IMPORTMODULE}.ScipyOptimizer",
84
+ return_value=mock_scipy_optimizer,
85
+ )
86
+
87
+ laplace_nmll = LaplaceNmll(X, y, opt_restarts=3)
88
+ result = laplace_nmll(mock_model)
89
+
90
+ assert result == -3.0
91
+ mock_model.set_local_optimization_params.assert_called_once_with(params_run3)
92
+
93
+ def test_optimizer_kwargs_passed_through(self, sample_data, mocker):
94
+ """Test that optimizer kwargs are passed to the bingo deterministic optimizer."""
95
+ X, y = sample_data
96
+
97
+ scipy_optimizer_spy = mocker.patch(f"{IMPORTMODULE}.ScipyOptimizer")
98
+ mocker.patch(f"{IMPORTMODULE}.ExplicitRegression")
99
+
100
+ custom_kwargs = {
101
+ "param_init_bounds": [-10, 10],
102
+ "tol": 1e-8,
103
+ "options": {"maxiter": 500},
104
+ }
105
+ LaplaceNmll(X, y, **custom_kwargs)
106
+ expected_kwargs = {"method": "lm", **custom_kwargs}
107
+ _, actual_kwargs = scipy_optimizer_spy.call_args
108
+
109
+ for key, value in expected_kwargs.items():
110
+ assert key in actual_kwargs
111
+ assert actual_kwargs[key] == value
@@ -0,0 +1,111 @@
1
+ import inspect
2
+ import numpy as np
3
+ import pytest
4
+ from pysips.sampler import Metropolis # Adjust import as needed
5
+
6
+ IMPORTMODULE = Metropolis.__module__
7
+
8
+
9
+ def dummy_likelihood(x):
10
+ return x.value * 2
11
+
12
+
13
+ @pytest.fixture
14
+ def metropolis(mocker):
15
+ mock_likelihood = dummy_likelihood
16
+ mock_proposal = mocker.Mock()
17
+ return Metropolis(
18
+ likelihood=mock_likelihood,
19
+ proposal=mock_proposal,
20
+ prior=None,
21
+ multiprocess=False,
22
+ )
23
+
24
+
25
+ class DummyGraph:
26
+ def __init__(self, value):
27
+ self.value = value
28
+ self.fitness = None
29
+
30
+
31
+ class TestMetropolis:
32
+ def test_evaluate_model_returns_none(self, metropolis):
33
+ assert metropolis.evaluate_model() is None
34
+
35
+ def test_evaluate_log_priors_returns_ones(self, metropolis):
36
+ x = np.empty((5, 1))
37
+ np.testing.assert_array_equal(
38
+ metropolis.evaluate_log_priors(x), np.ones((5, 1))
39
+ )
40
+
41
+ def test_evaluate_log_likelihood_no_multiproc(self, metropolis):
42
+ graphs = np.array(
43
+ [[DummyGraph(1)], [DummyGraph(2)], [DummyGraph(3)]], dtype=object
44
+ )
45
+ result = metropolis.evaluate_log_likelihood(graphs)
46
+ np.testing.assert_array_equal(result, np.c_[[2, 4, 6]])
47
+
48
+ def test_evaluate_log_likelihood_multiproc(self, mocker, metropolis):
49
+ metropolis._is_multiprocess = True
50
+
51
+ dummy_pool = mocker.MagicMock()
52
+ dummy_pool.__enter__.return_value = dummy_pool
53
+ dummy_pool.__exit__.return_value = None
54
+ dummy_pool.map.side_effect = lambda func, iterable: list(map(func, iterable))
55
+
56
+ mocker.patch(f"{IMPORTMODULE}.Pool", return_value=dummy_pool)
57
+
58
+ graphs = np.array(
59
+ [[DummyGraph(1)], [DummyGraph(2)], [DummyGraph(3)]], dtype=object
60
+ )
61
+ result = metropolis.evaluate_log_likelihood(graphs)
62
+
63
+ expected = np.c_[[2, 4, 6]]
64
+ np.testing.assert_array_equal(result, expected)
65
+
66
+ for g, l in zip(graphs.flatten(), expected.flatten()):
67
+ assert g.fitness == l
68
+
69
+
70
+ def test_smc_metropolis(mocker, metropolis):
71
+ inputs = np.array([[DummyGraph(1)], [DummyGraph(2)]], dtype=object)
72
+ num_samples = 2
73
+
74
+ mocker.patch.object(
75
+ metropolis,
76
+ "_initialize_probabilities",
77
+ return_value=(np.array([0, 0]), np.array([10, 10])),
78
+ )
79
+ mocker.patch.object(
80
+ metropolis,
81
+ "_perform_mcmc_step",
82
+ side_effect=lambda inputs, a, b, c: (inputs, b, None, None),
83
+ )
84
+
85
+ update_spy = mocker.spy(metropolis._equ_proposal, "update")
86
+
87
+ out_inputs, out_log_like = metropolis.smc_metropolis(inputs, num_samples)
88
+
89
+ update_spy.assert_called_once()
90
+ called_args, called_kwargs = update_spy.call_args
91
+
92
+ assert "gene_pool" in called_kwargs
93
+ np.testing.assert_array_equal(called_kwargs["gene_pool"], inputs.flatten())
94
+ assert np.array_equal(out_inputs, inputs)
95
+ assert np.array_equal(out_log_like, np.array([10, 10]))
96
+
97
+
98
+ def test_smc_metropolis_accepts_cov_kwarg(metropolis):
99
+ sig = inspect.signature(metropolis.smc_metropolis)
100
+ assert "cov" in sig.parameters
101
+
102
+
103
+ def test_proposal_applies_equ_proposal_elementwise():
104
+ equ_proposal = lambda x: x * 2
105
+ m = Metropolis(prior=None, likelihood=lambda x: 0, proposal=equ_proposal)
106
+
107
+ x = np.c_[[1.0, 2.0]]
108
+ result = m.proposal(x, None)
109
+
110
+ expected = np.c_[[2.0, 4.0]]
111
+ np.testing.assert_array_equal(result, expected)
@@ -0,0 +1,196 @@
1
+ import pytest
2
+
3
+ from pysips.mutation_proposal import MutationProposal
4
+
5
+
6
+ IMPORTMODULE = MutationProposal.__module__
7
+
8
+
9
+ class TestMutationProposal:
10
+
11
+ @pytest.fixture
12
+ def basic_setup(self):
13
+ """Basic setup for mutation proposal tests"""
14
+ X_dim = 2
15
+ operators = ["+", "-", "*"]
16
+ return X_dim, operators
17
+
18
+ @pytest.fixture
19
+ def mock_model(self, mocker):
20
+ """Create a mock model for testing"""
21
+ return mocker.MagicMock()
22
+
23
+ def test_initialization_passes_info_to_bingo(self, mocker):
24
+ """Test proper initialization of MutationProposal"""
25
+ X_dim = 3
26
+ operators = ["+", "-", "*", "sin"]
27
+
28
+ mock_component_gen = mocker.MagicMock()
29
+ mock_component_gen_cls = mocker.patch(
30
+ f"{IMPORTMODULE}.ComponentGenerator", return_value=mock_component_gen
31
+ )
32
+ mock_agraph_mutation = mocker.MagicMock()
33
+ mock_agraph_mutation_cls = mocker.patch(
34
+ f"{IMPORTMODULE}.AGraphMutation", return_value=mock_agraph_mutation
35
+ )
36
+
37
+ mutation_proposal = MutationProposal(
38
+ x_dim=X_dim,
39
+ operators=operators,
40
+ terminal_probability=0.2,
41
+ constant_probability=0.5,
42
+ command_probability=0.1,
43
+ node_probability=0.3,
44
+ parameter_probability=0.15,
45
+ prune_probability=0.25,
46
+ fork_probability=0.2,
47
+ repeat_mutation_probability=0.1,
48
+ seed=42,
49
+ )
50
+
51
+ mock_component_gen_cls.assert_called_once_with(
52
+ input_x_dimension=X_dim, terminal_probability=0.2, constant_probability=0.5
53
+ )
54
+
55
+ assert mock_component_gen.add_operator.call_count == len(operators)
56
+ for op in operators:
57
+ mock_component_gen.add_operator.assert_any_call(op)
58
+
59
+ mock_agraph_mutation_cls.assert_called_once_with(
60
+ mock_component_gen,
61
+ 0.1, # command_probability
62
+ 0.3, # node_probability
63
+ 0.15, # parameter_probability
64
+ 0.25, # prune_probability
65
+ 0.2, # fork_probability
66
+ )
67
+
68
+ def test_do_mutation_single(self, basic_setup, mock_model, mocker):
69
+ """Test _do_mutation method without repeat"""
70
+ X_dim, operators = basic_setup
71
+
72
+ # Setup mocks
73
+ mock_mutation = mocker.MagicMock()
74
+ mutated_model = mocker.MagicMock()
75
+ mock_mutation.return_value = mutated_model
76
+
77
+ # Patch random generator to ensure no repeat
78
+ mock_rng = mocker.MagicMock()
79
+ mock_rng.random.return_value = 1.0 # > repeat_mutation_prob
80
+
81
+ mocker.patch(f"{IMPORTMODULE}.ComponentGenerator")
82
+ mocker.patch(f"{IMPORTMODULE}.AGraphMutation", return_value=mock_mutation)
83
+
84
+ mutation_proposal = MutationProposal(x_dim=X_dim, operators=operators)
85
+ mutation_proposal._rng = mock_rng
86
+
87
+ result = mutation_proposal._do_mutation(mock_model)
88
+ mock_mutation.assert_called_once_with(mock_model)
89
+ assert result == mutated_model
90
+ assert mock_rng.random.call_count == 1
91
+
92
+ def test_do_mutation_with_repeat(self, basic_setup, mock_model, mocker):
93
+ """Test _do_mutation method with repeated mutations"""
94
+ X_dim, operators = basic_setup
95
+
96
+ mock_mutation = mocker.MagicMock()
97
+ mutated_model1 = mocker.MagicMock(name="mutated_model1")
98
+ mutated_model2 = mocker.MagicMock(name="mutated_model2")
99
+ mutated_model3 = mocker.MagicMock(name="mutated_model3")
100
+ mock_mutation.side_effect = [mutated_model1, mutated_model2, mutated_model3]
101
+
102
+ # Patch random generator to ensure repeat twice
103
+ mock_rng = mocker.MagicMock()
104
+ mock_rng.random.side_effect = [
105
+ 0.1,
106
+ 0.2,
107
+ 0.9,
108
+ ] # First two < repeat_mutation_prob
109
+
110
+ mocker.patch(f"{IMPORTMODULE}.ComponentGenerator")
111
+ mocker.patch(f"{IMPORTMODULE}.AGraphMutation", return_value=mock_mutation)
112
+
113
+ mutation_proposal = MutationProposal(
114
+ x_dim=X_dim, operators=operators, repeat_mutation_probability=0.5
115
+ )
116
+ mutation_proposal._rng = mock_rng
117
+
118
+ result = mutation_proposal(mock_model)
119
+
120
+ assert mock_mutation.call_count == 3
121
+ mock_mutation.assert_any_call(mock_model)
122
+ mock_mutation.assert_any_call(mutated_model1)
123
+ mock_mutation.assert_any_call(mutated_model2)
124
+ assert result == mutated_model3
125
+ assert mock_rng.random.call_count == 3
126
+
127
+ def test_call_different_model(self, basic_setup, mock_model, mocker):
128
+ """Test __call__ when mutation immediately produces a different model"""
129
+ X_dim, operators = basic_setup
130
+
131
+ mocker.patch(f"{IMPORTMODULE}.ComponentGenerator")
132
+
133
+ mutated_model = mocker.MagicMock()
134
+ mock_mutation = mocker.MagicMock(return_value=mutated_model)
135
+ mocker.patch(f"{IMPORTMODULE}.AGraphMutation", return_value=mock_mutation)
136
+
137
+ # Ensure models are different
138
+ mock_model.__eq__.return_value = False
139
+
140
+ mutation_proposal = MutationProposal(
141
+ x_dim=X_dim, operators=operators, repeat_mutation_probability=0.0
142
+ )
143
+ result = mutation_proposal(mock_model)
144
+
145
+ # Assertions
146
+ mock_mutation.assert_called_once_with(mock_model)
147
+ assert result == mutated_model
148
+
149
+ def test_call_retry_mutation(self, basic_setup, mock_model, mocker):
150
+ """Test __call__ when mutation initially produces an equivalent model"""
151
+ X_dim, operators = basic_setup
152
+
153
+ mocker.patch(f"{IMPORTMODULE}.ComponentGenerator")
154
+
155
+ equivalent_model = mock_model
156
+ different_model = mocker.MagicMock()
157
+ mock_mutation = mocker.MagicMock(
158
+ side_effect=[equivalent_model, different_model]
159
+ )
160
+ mocker.patch(f"{IMPORTMODULE}.AGraphMutation", return_value=mock_mutation)
161
+
162
+ mock_model.__eq__.side_effect = lambda other: other is equivalent_model
163
+
164
+ mutation_proposal = MutationProposal(
165
+ x_dim=X_dim, operators=operators, repeat_mutation_probability=0.0
166
+ )
167
+ result = mutation_proposal(mock_model)
168
+
169
+ assert mock_mutation.call_count == 2
170
+ assert result == different_model
171
+
172
+ def test_update_method(self, basic_setup, mocker):
173
+ """Test update method (should be a no-op)"""
174
+ X_dim, operators = basic_setup
175
+
176
+ mocker.patch(f"{IMPORTMODULE}.ComponentGenerator")
177
+ mocker.patch(f"{IMPORTMODULE}.AGraphMutation")
178
+
179
+ mutation_proposal = MutationProposal(x_dim=X_dim, operators=operators)
180
+
181
+ # This should not raise any errors
182
+ mutation_proposal.update(some_param=10)
183
+ mutation_proposal.update()
184
+
185
+ @pytest.mark.parametrize("seed", [None, 0, 42, 12345])
186
+ def test_random_seed(self, mocker, basic_setup, seed):
187
+ """Test that random seed is properly used"""
188
+ X_dim, operators = basic_setup
189
+
190
+ # Create class with different seeds
191
+ mocker.patch(f"{IMPORTMODULE}.ComponentGenerator"),
192
+ mocker.patch(f"{IMPORTMODULE}.AGraphMutation"),
193
+ mock_rng = mocker.patch("numpy.random.default_rng")
194
+
195
+ _ = MutationProposal(x_dim=X_dim, operators=operators, seed=seed)
196
+ mock_rng.assert_called_once_with(seed)