Joel Woodfield commited on
Commit
de9ce02
·
1 Parent(s): 03e72c7

Refactor to use manager in backend

Browse files
backend/src/__pycache__/logic.cpython-312.pyc ADDED
Binary file (7.2 kB). View file
 
backend/src/__pycache__/logic.cpython-314.pyc ADDED
Binary file (9.47 kB). View file
 
backend/src/__pycache__/manager.cpython-310.pyc ADDED
Binary file (4.04 kB). View file
 
backend/src/__pycache__/manager.cpython-312.pyc ADDED
Binary file (6.35 kB). View file
 
backend/src/__pycache__/manager.cpython-314.pyc ADDED
Binary file (7.89 kB). View file
 
backend/src/{backend.py → logic.py} RENAMED
File without changes
backend/src/manager.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ from typing import Literal
3
+
4
+ import matplotlib.pyplot as plt
5
+ from sympy import sympify
6
+
7
+ from logic import (
8
+ DataGenerationOptions,
9
+ Dataset,
10
+ PlotData,
11
+ compute_plot_values,
12
+ generate_dataset,
13
+ load_dataset_from_csv,
14
+ )
15
+
16
+
17
+ class Manager:
18
+ def __init__(self) -> None:
19
+ self.dataset = Dataset(x=[], y=[])
20
+ self.plots_data: PlotData | None = None
21
+
22
+ def update_dataset(
23
+ self,
24
+ dataset_type: Literal["Generate", "CSV"],
25
+ function: str,
26
+ data_xmin: float,
27
+ data_xmax: float,
28
+ sigma: float,
29
+ nsample: int,
30
+ sample_method: Literal["Grid", "Random"],
31
+ csv_path: str | Path | None,
32
+ has_header: bool,
33
+ xcol: int,
34
+ ycol: int,
35
+ ) -> None:
36
+ if dataset_type == "Generate":
37
+ try:
38
+ parsed_function = sympify(function)
39
+ except Exception as exc:
40
+ raise ValueError(f"Invalid function: {exc}") from exc
41
+
42
+ sampling = sample_method.lower()
43
+ if sampling not in ["grid", "random"]:
44
+ raise ValueError(f"Unknown sampling method: {sample_method}")
45
+
46
+ self.dataset = generate_dataset(
47
+ parsed_function,
48
+ (data_xmin, data_xmax),
49
+ DataGenerationOptions(
50
+ method=sampling,
51
+ num_samples=nsample,
52
+ noise=sigma,
53
+ ),
54
+ )
55
+ return
56
+
57
+ normalized_path = self._normalize_csv_path(csv_path)
58
+ if normalized_path is None:
59
+ raise ValueError("Please upload a CSV file.")
60
+
61
+ self.dataset = load_dataset_from_csv(
62
+ normalized_path,
63
+ has_header,
64
+ xcol,
65
+ ycol,
66
+ )
67
+
68
+ def compute_plot_data(
69
+ self,
70
+ kernel: str,
71
+ distribution: Literal["Prior", "Posterior"],
72
+ plot_xmin: float,
73
+ plot_xmax: float,
74
+ ) -> None:
75
+ self.plots_data = compute_plot_values(
76
+ self.dataset,
77
+ kernel,
78
+ distribution,
79
+ plot_xmin,
80
+ plot_xmax,
81
+ )
82
+
83
+ def handle_generate_plots(
84
+ self,
85
+ dataset_type: Literal["Generate", "CSV"],
86
+ function: str,
87
+ data_xmin: float,
88
+ data_xmax: float,
89
+ sigma: float,
90
+ nsample: int,
91
+ sample_method: Literal["Grid", "Random"],
92
+ csv_path: str | Path | None,
93
+ has_header: bool,
94
+ xcol: int,
95
+ ycol: int,
96
+ kernel: str,
97
+ distribution: Literal["Prior", "Posterior"],
98
+ plot_xmin: float,
99
+ plot_xmax: float,
100
+ ):
101
+ self.update_dataset(
102
+ dataset_type,
103
+ function,
104
+ data_xmin,
105
+ data_xmax,
106
+ sigma,
107
+ nsample,
108
+ sample_method,
109
+ csv_path,
110
+ has_header,
111
+ xcol,
112
+ ycol,
113
+ )
114
+
115
+ true_dataset = self._build_true_dataset(
116
+ dataset_type,
117
+ function,
118
+ plot_xmin,
119
+ plot_xmax,
120
+ )
121
+
122
+ self.compute_plot_data(
123
+ kernel,
124
+ distribution,
125
+ plot_xmin,
126
+ plot_xmax,
127
+ )
128
+
129
+ return self.generate_plot(true_dataset)
130
+
131
+ def generate_plot(self, true_dataset: Dataset):
132
+ if self.plots_data is None:
133
+ raise ValueError("Plot data has not been computed.")
134
+
135
+ fig, ax = plt.subplots(figsize=(12, 9))
136
+ cmap = plt.get_cmap("tab20")
137
+
138
+ ax.scatter(self.dataset.x, self.dataset.y, color=cmap(0), label="Data Points")
139
+
140
+ if true_dataset.y is not None and len(true_dataset.y) > 0:
141
+ ax.plot(true_dataset.x, true_dataset.y, color=cmap(1), label="True Function")
142
+
143
+ ax.plot(self.plots_data.x, self.plots_data.pred_mean, color=cmap(2), label="Mean Prediction")
144
+
145
+ ax.fill_between(
146
+ self.plots_data.x,
147
+ self.plots_data.pred_mean - 1.96 * self.plots_data.pred_std,
148
+ self.plots_data.pred_mean + 1.96 * self.plots_data.pred_std,
149
+ color=cmap(3),
150
+ alpha=0.2,
151
+ label="95% Confidence Interval",
152
+ )
153
+
154
+ ax.legend()
155
+ return fig
156
+
157
+ def _build_true_dataset(
158
+ self,
159
+ dataset_type: Literal["Generate", "CSV"],
160
+ function: str,
161
+ xmin: float,
162
+ xmax: float,
163
+ ) -> Dataset:
164
+ if dataset_type == "CSV":
165
+ return Dataset(x=[], y=[])
166
+
167
+ try:
168
+ parsed_function = sympify(function)
169
+ except Exception as exc:
170
+ raise ValueError(f"Invalid function: {exc}") from exc
171
+
172
+ return generate_dataset(
173
+ parsed_function,
174
+ (xmin, xmax),
175
+ DataGenerationOptions(
176
+ method="grid",
177
+ num_samples=1000,
178
+ noise=0.0,
179
+ ),
180
+ )
181
+
182
+ def _normalize_csv_path(self, csv_path: str | Path | None) -> str | None:
183
+ if csv_path is None:
184
+ return None
185
+
186
+ if isinstance(csv_path, Path):
187
+ return str(csv_path)
188
+
189
+ if isinstance(csv_path, str):
190
+ return csv_path
191
+
192
+ name = getattr(csv_path, "name", None)
193
+ if name:
194
+ return str(name)
195
+
196
+ return None
frontends/gradio/__pycache__/main.cpython-314.pyc ADDED
Binary file (9.32 kB). View file
 
frontends/gradio/main.py CHANGED
@@ -1,24 +1,16 @@
1
  from typing import Literal
2
 
3
  import gradio as gr
4
- import matplotlib.pyplot as plt
5
- from sympy import sympify
6
 
7
  import sys
8
  from pathlib import Path
 
9
  root_dir = Path(__file__).resolve().parent.parent.parent
10
  backend_src = root_dir / "backend" / "src"
11
  if str(backend_src) not in sys.path:
12
  sys.path.append(str(backend_src))
13
 
14
- from backend import (
15
- Dataset,
16
- DataGenerationOptions,
17
- PlotData,
18
- compute_plot_values,
19
- generate_dataset,
20
- load_dataset_from_csv,
21
- )
22
 
23
 
24
  CSS = """
@@ -28,107 +20,8 @@ CSS = """
28
  """
29
 
30
 
31
- def get_dataset(
32
- dataset_type: Literal["Generate", "CSV"],
33
- function: str,
34
- xmin: float,
35
- xmax: float,
36
- sigma: float,
37
- nsample: int,
38
- sample_method: Literal["Grid", "Random"],
39
- csv_path: str,
40
- has_header: bool,
41
- xcol: int,
42
- ycol: int,
43
- ) -> tuple[Dataset, Dataset]:
44
- if dataset_type == "Generate":
45
- try:
46
- function = sympify(function)
47
- except Exception as e:
48
- raise ValueError(f"Invalid function: {e}")
49
-
50
- sample_method = sample_method.lower()
51
- if sample_method not in ['grid', 'random']:
52
- raise ValueError(f"Unknown sampling method: {sample_method}")
53
-
54
- dataset = generate_dataset(
55
- function,
56
- (xmin, xmax),
57
- DataGenerationOptions(
58
- sample_method,
59
- nsample,
60
- sigma,
61
- ),
62
- )
63
-
64
- else: # CSV
65
- dataset = load_dataset_from_csv(
66
- csv_path,
67
- has_header,
68
- xcol,
69
- ycol,
70
- )
71
-
72
- return dataset
73
-
74
-
75
- def get_true_dataset(
76
- dataset_type: Literal["Generate", "CSV"],
77
- function: str,
78
- xmin: float,
79
- xmax: float,
80
- ) -> Dataset:
81
- if dataset_type == "CSV":
82
- # No true dataset for CSV uploads
83
- return Dataset(x=[], y=[])
84
- else:
85
- try:
86
- function = sympify(function)
87
- except Exception as e:
88
- raise ValueError(f"Invalid function: {e}")
89
-
90
- true_dataset = generate_dataset(
91
- function,
92
- (xmin, xmax),
93
- DataGenerationOptions(
94
- method='grid',
95
- num_samples=1000,
96
- noise=0.,
97
- ),
98
- )
99
- return true_dataset
100
-
101
-
102
- def generate_plot(
103
- plot_data: PlotData,
104
- dataset: Dataset,
105
- true_dataset: Dataset,
106
- ):
107
- fig, ax = plt.subplots(figsize=(12, 9))
108
- cmap = plt.get_cmap("tab20")
109
-
110
- ax.scatter(dataset.x, dataset.y, color=cmap(0), label='Data Points')
111
-
112
- if true_dataset.y is not None:
113
- ax.plot(true_dataset.x, true_dataset.y, color=cmap(1), label='True Function')
114
-
115
- ax.plot(plot_data.x, plot_data.pred_mean, color=cmap(2), label='Mean Prediction')
116
-
117
- ax.fill_between(
118
- plot_data.x,
119
- plot_data.pred_mean - 1.96 * plot_data.pred_std,
120
- plot_data.pred_mean + 1.96 * plot_data.pred_std,
121
- color=cmap(3),
122
- alpha=0.2,
123
- label='95% Confidence Interval'
124
- )
125
-
126
- ax.legend()
127
- return fig
128
-
129
-
130
  def handle_dataset_type_change(
131
- dataset_type: Literal["Generate", "CSV"]
132
  ):
133
  if dataset_type == "Generate":
134
  return (
@@ -143,27 +36,65 @@ def handle_dataset_type_change(
143
  gr.update(visible=False), # xcol
144
  gr.update(visible=False), # ycol
145
  )
146
- else: # CSV
147
- return (
148
- gr.update(visible=False), # function
149
- gr.update(visible=False), # xmin
150
- gr.update(visible=False), # xmax
151
- gr.update(visible=False), # sigma
152
- gr.update(visible=False), # nsample
153
- gr.update(visible=False), # sample method
154
- gr.update(visible=True), # csv upload
155
- gr.update(visible=True), # has header
156
- gr.update(visible=True), # xcol
157
- gr.update(visible=True), # ycol
158
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
 
161
  def launch():
162
  default_dataset_type = "Generate"
163
 
164
  default_function = "sin(2 * pi * x)"
165
- default_data_xmin = -1.
166
- default_data_xmax = 1.
167
  default_sigma = 0
168
  default_nsample = 100
169
  default_sample_method = "Grid"
@@ -175,56 +106,36 @@ def launch():
175
 
176
  default_kernel = "RBF() + WhiteKernel()"
177
  default_distribution = "Posterior"
178
- default_plot_xmin = -2.
179
- default_plot_xmax = 2.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  with gr.Blocks() as demo:
182
  gr.HTML("<div style='text-align:left; font-size:40px; font-weight: bold;'>Gaussian process visualizer</div>")
183
-
184
- dataset = gr.State(
185
- get_dataset(
186
- default_dataset_type,
187
- default_function,
188
- default_data_xmin,
189
- default_data_xmax,
190
- default_sigma,
191
- default_nsample,
192
- default_sample_method,
193
- default_csv_file,
194
- default_has_header,
195
- default_xcol,
196
- default_ycol,
197
- )
198
- )
199
 
200
- true_dataset = gr.State(
201
- get_true_dataset(
202
- default_dataset_type,
203
- default_function,
204
- default_plot_xmin,
205
- default_plot_xmax,
206
- )
207
- )
208
-
209
- plot_data = gr.State(
210
- compute_plot_values(
211
- dataset.value,
212
- default_kernel,
213
- default_distribution,
214
- default_plot_xmin,
215
- default_plot_xmax,
216
- )
217
- )
218
 
219
  with gr.Row():
220
  with gr.Column(scale=2):
221
- plot = gr.Plot(
222
- value=generate_plot(
223
- plot_data.value,
224
- dataset.value,
225
- true_dataset.value,
226
- ),
227
- )
228
 
229
  with gr.Column(scale=1):
230
  with gr.Tab("Data"):
@@ -236,22 +147,21 @@ def launch():
236
  interactive=True,
237
  )
238
 
239
- # Generated data options
240
  with gr.Row():
241
  function = gr.Textbox(
242
- label="Function (in terms of x)",
243
  value=default_function,
244
  interactive=True,
245
  )
246
-
247
  with gr.Row():
248
  data_xmin = gr.Number(
249
- label="Data x min",
250
  value=default_data_xmin,
251
  interactive=True,
252
  )
253
  data_xmax = gr.Number(
254
- label="Data x max",
255
  value=default_data_xmax,
256
  interactive=True,
257
  )
@@ -269,37 +179,36 @@ def launch():
269
  )
270
 
271
  nsample = gr.Slider(
272
- label="Number of data points",
273
- minimum=0,
274
- maximum=100,
275
- step=1,
276
  value=default_nsample,
277
  interactive=True,
278
  )
279
 
280
- # CSV options
281
  with gr.Row():
282
  csv_upload = gr.File(
283
- label="Upload CSV file",
284
- file_types=['.csv'],
285
  visible=False,
286
  )
287
  with gr.Row():
288
  has_header = gr.Checkbox(
289
- label="CSV has header row",
290
  value=default_has_header,
291
  visible=False,
292
  )
293
-
294
  with gr.Row():
295
  xcol = gr.Number(
296
- label="X column index",
297
  value=default_xcol,
298
  precision=0,
299
  visible=False,
300
  )
301
  ycol = gr.Number(
302
- label="Y column index",
303
  value=default_ycol,
304
  precision=0,
305
  visible=False,
@@ -324,7 +233,7 @@ def launch():
324
 
325
  with gr.Tab("Model"):
326
  kernel = gr.Textbox(
327
- label="Kernel",
328
  value=default_kernel,
329
  interactive=True,
330
  )
@@ -336,21 +245,22 @@ def launch():
336
  )
337
  with gr.Row():
338
  plot_xmin = gr.Number(
339
- label="Plot x min",
340
  value=default_plot_xmin,
341
  interactive=True,
342
  )
343
  plot_xmax = gr.Number(
344
- label="Plot x max",
345
  value=default_plot_xmax,
346
  interactive=True,
347
  )
348
 
349
- generate_button = gr.Button("Regenerate Plot")
350
 
351
  generate_button.click(
352
- fn=get_dataset,
353
  inputs=[
 
354
  dataset_type,
355
  function,
356
  data_xmin,
@@ -362,39 +272,16 @@ def launch():
362
  has_header,
363
  xcol,
364
  ycol,
365
- ],
366
- outputs=[dataset],
367
- ).then(
368
- fn=get_true_dataset,
369
- inputs=[
370
- dataset_type,
371
- function,
372
- plot_xmin,
373
- plot_xmax,
374
- ],
375
- outputs=[true_dataset],
376
- ).then(
377
- fn=compute_plot_values,
378
- inputs=[
379
- dataset,
380
  kernel,
381
  distribution,
382
  plot_xmin,
383
  plot_xmax,
384
  ],
385
- outputs=[plot_data],
386
- ).then(
387
- fn=generate_plot,
388
- inputs=[
389
- plot_data,
390
- dataset,
391
- true_dataset,
392
- ],
393
- outputs=[plot],
394
  )
395
 
396
  demo.launch(css=CSS)
397
-
398
 
399
- if __name__ == "__main__":
 
400
  launch()
 
1
  from typing import Literal
2
 
3
  import gradio as gr
 
 
4
 
5
  import sys
6
  from pathlib import Path
7
+
8
  root_dir = Path(__file__).resolve().parent.parent.parent
9
  backend_src = root_dir / "backend" / "src"
10
  if str(backend_src) not in sys.path:
11
  sys.path.append(str(backend_src))
12
 
13
+ from manager import Manager
 
 
 
 
 
 
 
14
 
15
 
16
  CSS = """
 
20
  """
21
 
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  def handle_dataset_type_change(
24
+ dataset_type: Literal["Generate", "CSV"],
25
  ):
26
  if dataset_type == "Generate":
27
  return (
 
36
  gr.update(visible=False), # xcol
37
  gr.update(visible=False), # ycol
38
  )
39
+
40
+ return (
41
+ gr.update(visible=False), # function
42
+ gr.update(visible=False), # xmin
43
+ gr.update(visible=False), # xmax
44
+ gr.update(visible=False), # sigma
45
+ gr.update(visible=False), # nsample
46
+ gr.update(visible=False), # sample method
47
+ gr.update(visible=True), # csv upload
48
+ gr.update(visible=True), # has header
49
+ gr.update(visible=True), # xcol
50
+ gr.update(visible=True), # ycol
51
+ )
52
+
53
+
54
+ def handle_generate(
55
+ manager: Manager,
56
+ dataset_type: Literal["Generate", "CSV"],
57
+ function: str,
58
+ data_xmin: float,
59
+ data_xmax: float,
60
+ sigma: float,
61
+ nsample: int,
62
+ sample_method: Literal["Grid", "Random"],
63
+ csv_upload,
64
+ has_header: bool,
65
+ xcol: int,
66
+ ycol: int,
67
+ kernel: str,
68
+ distribution: Literal["Prior", "Posterior"],
69
+ plot_xmin: float,
70
+ plot_xmax: float,
71
+ ):
72
+ plot = manager.handle_generate_plots(
73
+ dataset_type,
74
+ function,
75
+ data_xmin,
76
+ data_xmax,
77
+ sigma,
78
+ nsample,
79
+ sample_method,
80
+ csv_upload,
81
+ has_header,
82
+ xcol,
83
+ ycol,
84
+ kernel,
85
+ distribution,
86
+ plot_xmin,
87
+ plot_xmax,
88
+ )
89
+ return manager, plot
90
 
91
 
92
  def launch():
93
  default_dataset_type = "Generate"
94
 
95
  default_function = "sin(2 * pi * x)"
96
+ default_data_xmin = -1.0
97
+ default_data_xmax = 1.0
98
  default_sigma = 0
99
  default_nsample = 100
100
  default_sample_method = "Grid"
 
106
 
107
  default_kernel = "RBF() + WhiteKernel()"
108
  default_distribution = "Posterior"
109
+ default_plot_xmin = -2.0
110
+ default_plot_xmax = 2.0
111
+
112
+ manager = Manager()
113
+ initial_plot = manager.handle_generate_plots(
114
+ default_dataset_type,
115
+ default_function,
116
+ default_data_xmin,
117
+ default_data_xmax,
118
+ default_sigma,
119
+ default_nsample,
120
+ default_sample_method,
121
+ default_csv_file,
122
+ default_has_header,
123
+ default_xcol,
124
+ default_ycol,
125
+ default_kernel,
126
+ default_distribution,
127
+ default_plot_xmin,
128
+ default_plot_xmax,
129
+ )
130
 
131
  with gr.Blocks() as demo:
132
  gr.HTML("<div style='text-align:left; font-size:40px; font-weight: bold;'>Gaussian process visualizer</div>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
+ manager_state = gr.State(manager)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
  with gr.Row():
137
  with gr.Column(scale=2):
138
+ plot = gr.Plot(value=initial_plot)
 
 
 
 
 
 
139
 
140
  with gr.Column(scale=1):
141
  with gr.Tab("Data"):
 
147
  interactive=True,
148
  )
149
 
 
150
  with gr.Row():
151
  function = gr.Textbox(
152
+ label="Function (in terms of x)",
153
  value=default_function,
154
  interactive=True,
155
  )
156
+
157
  with gr.Row():
158
  data_xmin = gr.Number(
159
+ label="Data x min",
160
  value=default_data_xmin,
161
  interactive=True,
162
  )
163
  data_xmax = gr.Number(
164
+ label="Data x max",
165
  value=default_data_xmax,
166
  interactive=True,
167
  )
 
179
  )
180
 
181
  nsample = gr.Slider(
182
+ label="Number of data points",
183
+ minimum=0,
184
+ maximum=100,
185
+ step=1,
186
  value=default_nsample,
187
  interactive=True,
188
  )
189
 
 
190
  with gr.Row():
191
  csv_upload = gr.File(
192
+ label="Upload CSV file",
193
+ file_types=[".csv"],
194
  visible=False,
195
  )
196
  with gr.Row():
197
  has_header = gr.Checkbox(
198
+ label="CSV has header row",
199
  value=default_has_header,
200
  visible=False,
201
  )
202
+
203
  with gr.Row():
204
  xcol = gr.Number(
205
+ label="X column index",
206
  value=default_xcol,
207
  precision=0,
208
  visible=False,
209
  )
210
  ycol = gr.Number(
211
+ label="Y column index",
212
  value=default_ycol,
213
  precision=0,
214
  visible=False,
 
233
 
234
  with gr.Tab("Model"):
235
  kernel = gr.Textbox(
236
+ label="Kernel",
237
  value=default_kernel,
238
  interactive=True,
239
  )
 
245
  )
246
  with gr.Row():
247
  plot_xmin = gr.Number(
248
+ label="Plot x min",
249
  value=default_plot_xmin,
250
  interactive=True,
251
  )
252
  plot_xmax = gr.Number(
253
+ label="Plot x max",
254
  value=default_plot_xmax,
255
  interactive=True,
256
  )
257
 
258
+ generate_button = gr.Button("Regenerate Plot")
259
 
260
  generate_button.click(
261
+ fn=handle_generate,
262
  inputs=[
263
+ manager_state,
264
  dataset_type,
265
  function,
266
  data_xmin,
 
272
  has_header,
273
  xcol,
274
  ycol,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  kernel,
276
  distribution,
277
  plot_xmin,
278
  plot_xmax,
279
  ],
280
+ outputs=[manager_state, plot],
 
 
 
 
 
 
 
 
281
  )
282
 
283
  demo.launch(css=CSS)
 
284
 
285
+
286
+ if __name__ == "__main__":
287
  launch()