guohanghui commited on
Commit
251cfa4
·
verified ·
1 Parent(s): a59aea7

Update pymc/mcp_output/mcp_plugin/mcp_service.py

Browse files
pymc/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,445 +1,226 @@
1
- import os
2
- import sys
3
- from typing import Dict, Any, List, Optional
4
-
5
- # Add the local source directory to sys.path
6
- source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
7
- if source_path not in sys.path:
8
- sys.path.insert(0, source_path)
9
-
10
  from fastmcp import FastMCP
11
- import numpy as np
12
-
13
- # Import core modules from the local source directory
14
- import pymc as pm
15
- from pymc import Model, sample
16
- from pymc.distributions import Normal, Beta, Uniform, Exponential, Gamma, HalfNormal
17
- import arviz as az
18
 
19
  # Create the FastMCP service application
20
  mcp = FastMCP("pymc_service")
21
 
22
- # Storage for models and traces
23
- _models: Dict[str, Model] = {}
24
- _traces: Dict[str, Any] = {}
25
-
26
-
27
- @mcp.tool(name="create_simple_model", description="Create a simple regression model with normal priors")
28
- def create_simple_model(
29
- model_id: str,
30
- n_predictors: int = 2
31
- ) -> Dict[str, Any]:
32
  """
33
- Create a simple Bayesian regression model.
34
 
35
- Args:
36
- model_id: Unique identifier for the model
37
- n_predictors: Number of predictor variables
38
 
39
  Returns:
40
- Dictionary with success status and model information
41
  """
42
  try:
43
- with Model() as model:
44
- # Priors
45
- alpha = Normal('alpha', mu=0, sigma=10)
46
- beta = Normal('beta', mu=0, sigma=10, shape=n_predictors)
47
- sigma = HalfNormal('sigma', sigma=1)
48
-
49
- # Store model structure info
50
- model_info = {
51
- "model_id": model_id,
52
- "type": "simple_regression",
53
- "n_predictors": n_predictors,
54
- "variables": [var.name for var in model.free_RVs]
55
- }
56
-
57
- _models[model_id] = model
58
-
59
- return {
60
- "success": True,
61
- "result": model_info,
62
- "error": None
63
- }
64
  except Exception as e:
65
- return {"success": False, "result": None, "error": str(e)}
66
 
67
-
68
- @mcp.tool(name="create_hierarchical_model", description="Create a hierarchical model")
69
- def create_hierarchical_model(
70
- model_id: str,
71
- n_groups: int = 3
72
- ) -> Dict[str, Any]:
73
  """
74
- Create a simple hierarchical model with group-level effects.
75
 
76
- Args:
77
- model_id: Unique identifier for the model
78
- n_groups: Number of groups
 
79
 
80
  Returns:
81
- Dictionary with success status and model information
82
  """
83
  try:
84
- with Model() as model:
85
- # Hyperpriors
86
- mu_alpha = Normal('mu_alpha', mu=0, sigma=10)
87
- sigma_alpha = HalfNormal('sigma_alpha', sigma=1)
88
-
89
- # Group-level parameters
90
- alpha = Normal('alpha', mu=mu_alpha, sigma=sigma_alpha, shape=n_groups)
91
- sigma = HalfNormal('sigma', sigma=1)
92
-
93
- model_info = {
94
- "model_id": model_id,
95
- "type": "hierarchical",
96
- "n_groups": n_groups,
97
- "variables": [var.name for var in model.free_RVs]
98
- }
99
-
100
- _models[model_id] = model
101
-
102
- return {
103
- "success": True,
104
- "result": model_info,
105
- "error": None
106
- }
107
- except Exception as e:
108
- return {"success": False, "result": None, "error": str(e)}
109
-
110
-
111
- @mcp.tool(name="add_observed_variable", description="Add observed data to a model")
112
- def add_observed_variable(
113
- model_id: str,
114
- var_name: str,
115
- distribution: str = "Normal",
116
- observed_data: Optional[List[float]] = None
117
- ) -> Dict[str, Any]:
118
- """
119
- Add an observed variable to an existing model.
120
-
121
- Args:
122
- model_id: ID of the model to modify
123
- var_name: Name of the observed variable
124
- distribution: Distribution type (Normal, Beta, etc.)
125
- observed_data: Optional observed data values
126
-
127
- Returns:
128
- Dictionary with success status
129
- """
130
- try:
131
- if model_id not in _models:
132
- return {"success": False, "result": None, "error": f"Model '{model_id}' not found"}
133
-
134
- model = _models[model_id]
135
-
136
  with model:
137
- if distribution == "Normal":
138
- if observed_data:
139
- Normal(var_name, mu=0, sigma=1, observed=np.array(observed_data))
140
- else:
141
- Normal(var_name, mu=0, sigma=1)
142
- elif distribution == "Beta":
143
- if observed_data:
144
- Beta(var_name, alpha=1, beta=1, observed=np.array(observed_data))
145
- else:
146
- Beta(var_name, alpha=1, beta=1)
147
-
148
- return {
149
- "success": True,
150
- "result": {
151
- "model_id": model_id,
152
- "variable_added": var_name,
153
- "distribution": distribution
154
- },
155
- "error": None
156
- }
157
  except Exception as e:
158
- return {"success": False, "result": None, "error": str(e)}
159
 
160
-
161
- @mcp.tool(name="sample_model", description="Perform MCMC sampling on a model")
162
- def sample_model(
163
- model_id: str,
164
- draws: int = 1000,
165
- tune: int = 1000,
166
- chains: int = 2,
167
- cores: int = 1
168
- ) -> Dict[str, Any]:
169
  """
170
- Perform MCMC sampling on a stored model.
171
 
172
- Args:
173
- model_id: ID of the model to sample
174
- draws: Number of samples to draw
175
- tune: Number of tuning steps
176
- chains: Number of chains
177
- cores: Number of cores to use
178
 
179
  Returns:
180
- Dictionary with success status and trace ID
181
  """
182
  try:
183
- if model_id not in _models:
184
- return {"success": False, "result": None, "error": f"Model '{model_id}' not found"}
185
-
186
- model = _models[model_id]
187
-
188
  with model:
189
- trace = sample(draws=draws, tune=tune, chains=chains, cores=cores)
190
-
191
- trace_id = f"{model_id}_trace"
192
- _traces[trace_id] = trace
193
-
194
- # Get summary statistics
195
- summary = az.summary(trace)
196
-
197
- return {
198
- "success": True,
199
- "result": {
200
- "trace_id": trace_id,
201
- "model_id": model_id,
202
- "draws": draws,
203
- "chains": chains,
204
- "summary": summary.to_dict()
205
- },
206
- "error": None
207
- }
208
  except Exception as e:
209
- return {"success": False, "result": None, "error": str(e)}
210
-
211
 
212
- @mcp.tool(name="get_trace_summary", description="Get summary statistics from a trace")
213
- def get_trace_summary(trace_id: str) -> Dict[str, Any]:
214
  """
215
- Get summary statistics from a stored trace.
216
 
217
- Args:
218
- trace_id: ID of the trace
 
219
 
220
  Returns:
221
- Dictionary with summary statistics
222
  """
223
  try:
224
- if trace_id not in _traces:
225
- return {"success": False, "result": None, "error": f"Trace '{trace_id}' not found"}
226
-
227
- trace = _traces[trace_id]
228
- summary = az.summary(trace)
229
-
230
- return {
231
- "success": True,
232
- "result": {
233
- "trace_id": trace_id,
234
- "summary": summary.to_dict()
235
- },
236
- "error": None
237
- }
238
  except Exception as e:
239
- return {"success": False, "result": None, "error": str(e)}
240
 
241
-
242
- @mcp.tool(name="compute_loo", description="Compute leave-one-out cross-validation")
243
- def compute_loo(trace_id: str) -> Dict[str, Any]:
244
  """
245
- Compute LOO-CV for model comparison.
246
 
247
- Args:
248
- trace_id: ID of the trace
 
249
 
250
  Returns:
251
- Dictionary with LOO results
252
  """
253
  try:
254
- if trace_id not in _traces:
255
- return {"success": False, "result": None, "error": f"Trace '{trace_id}' not found"}
256
-
257
- trace = _traces[trace_id]
258
- loo = az.loo(trace)
259
-
260
- return {
261
- "success": True,
262
- "result": {
263
- "trace_id": trace_id,
264
- "loo": float(loo.loo),
265
- "loo_se": float(loo.loo_se),
266
- "p_loo": float(loo.p_loo)
267
- },
268
- "error": None
269
- }
270
  except Exception as e:
271
- return {"success": False, "result": None, "error": str(e)}
272
 
273
-
274
- @mcp.tool(name="compute_waic", description="Compute Watanabe-Akaike information criterion")
275
- def compute_waic(trace_id: str) -> Dict[str, Any]:
276
  """
277
- Compute WAIC for model comparison.
278
 
279
- Args:
280
- trace_id: ID of the trace
281
 
282
  Returns:
283
- Dictionary with WAIC results
284
  """
285
  try:
286
- if trace_id not in _traces:
287
- return {"success": False, "result": None, "error": f"Trace '{trace_id}' not found"}
288
-
289
- trace = _traces[trace_id]
290
- waic = az.waic(trace)
291
-
292
- return {
293
- "success": True,
294
- "result": {
295
- "trace_id": trace_id,
296
- "waic": float(waic.waic),
297
- "waic_se": float(waic.waic_se),
298
- "p_waic": float(waic.p_waic)
299
- },
300
- "error": None
301
  }
 
302
  except Exception as e:
303
- return {"success": False, "result": None, "error": str(e)}
304
-
305
 
306
- @mcp.tool(name="get_posterior_samples", description="Get posterior samples for a variable")
307
- def get_posterior_samples(
308
- trace_id: str,
309
- var_name: str,
310
- n_samples: int = 100
311
- ) -> Dict[str, Any]:
312
  """
313
- Get posterior samples for a specific variable.
314
 
315
- Args:
316
- trace_id: ID of the trace
317
- var_name: Name of the variable
318
- n_samples: Number of samples to return
319
 
320
  Returns:
321
- Dictionary with posterior samples
322
  """
323
  try:
324
- if trace_id not in _traces:
325
- return {"success": False, "result": None, "error": f"Trace '{trace_id}' not found"}
326
-
327
- trace = _traces[trace_id]
328
-
329
- # Convert to InferenceData if needed
330
- if not isinstance(trace, az.InferenceData):
331
- idata = az.from_pymc3(trace)
332
- else:
333
- idata = trace
334
-
335
- # Get samples
336
- samples = idata.posterior[var_name].values.flatten()[:n_samples].tolist()
337
-
338
- return {
339
- "success": True,
340
- "result": {
341
- "trace_id": trace_id,
342
- "var_name": var_name,
343
- "samples": samples,
344
- "n_samples": len(samples)
345
- },
346
- "error": None
347
- }
348
  except Exception as e:
349
- return {"success": False, "result": None, "error": str(e)}
350
-
351
 
352
- @mcp.tool(name="list_models", description="List all stored models")
353
- def list_models() -> Dict[str, Any]:
354
  """
355
- List all stored models.
356
 
357
- Returns:
358
- Dictionary with list of model IDs
359
- """
360
- try:
361
- return {
362
- "success": True,
363
- "result": {"model_ids": list(_models.keys())},
364
- "error": None
365
- }
366
- except Exception as e:
367
- return {"success": False, "result": None, "error": str(e)}
368
-
369
-
370
- @mcp.tool(name="list_traces", description="List all stored traces")
371
- def list_traces() -> Dict[str, Any]:
372
- """
373
- List all stored traces.
374
 
375
  Returns:
376
- Dictionary with list of trace IDs
377
  """
378
  try:
379
- return {
380
- "success": True,
381
- "result": {"trace_ids": list(_traces.keys())},
382
- "error": None
383
- }
384
  except Exception as e:
385
- return {"success": False, "result": None, "error": str(e)}
386
-
387
 
388
- @mcp.tool(name="delete_model", description="Delete a stored model")
389
- def delete_model(model_id: str) -> Dict[str, Any]:
390
  """
391
- Delete a stored model.
392
 
393
- Args:
394
- model_id: ID of the model to delete
395
 
396
  Returns:
397
- Dictionary with success status
398
  """
399
  try:
400
- if model_id not in _models:
401
- return {"success": False, "result": None, "error": f"Model '{model_id}' not found"}
402
-
403
- del _models[model_id]
404
- return {
405
- "success": True,
406
- "result": {"deleted": model_id},
407
- "error": None
408
- }
409
  except Exception as e:
410
- return {"success": False, "result": None, "error": str(e)}
411
-
412
 
413
- @mcp.tool(name="delete_trace", description="Delete a stored trace")
414
- def delete_trace(trace_id: str) -> Dict[str, Any]:
415
  """
416
- Delete a stored trace.
417
 
418
- Args:
419
- trace_id: ID of the trace to delete
 
420
 
421
  Returns:
422
- Dictionary with success status
423
  """
424
  try:
425
- if trace_id not in _traces:
426
- return {"success": False, "result": None, "error": f"Trace '{trace_id}' not found"}
427
-
428
- del _traces[trace_id]
429
- return {
430
- "success": True,
431
- "result": {"deleted": trace_id},
432
- "error": None
433
- }
 
 
 
434
  except Exception as e:
435
- return {"success": False, "result": None, "error": str(e)}
436
-
437
 
438
  def create_app() -> FastMCP:
439
  """
440
  Create and return the FastMCP application instance.
441
 
442
  Returns:
443
- FastMCP: The FastMCP application instance.
444
  """
445
  return mcp
 
 
 
 
 
 
 
 
 
 
1
  from fastmcp import FastMCP
 
 
 
 
 
 
 
2
 
3
  # Create the FastMCP service application
4
  mcp = FastMCP("pymc_service")
5
 
6
+ @mcp.tool(name="model_specification", description="Define a probabilistic model using PyMC's syntax")
7
+ def model_specification(model_code: str) -> dict:
 
 
 
 
 
 
 
 
8
  """
9
+ Define a probabilistic model using PyMC's syntax.
10
 
11
+ Parameters:
12
+ - model_code (str): The PyMC model definition code.
 
13
 
14
  Returns:
15
+ - dict: A dictionary with success status and model object.
16
  """
17
  try:
18
+ # Execute the model code
19
+ local_vars = {}
20
+ exec(model_code, {}, local_vars)
21
+ model = local_vars.get("model")
22
+ if model is None:
23
+ raise ValueError("Model definition is invalid or missing.")
24
+ return {"success": True, "model": model}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  except Exception as e:
26
+ return {"success": False, "error": str(e)}
27
 
28
+ @mcp.tool(name="mcmc_sampling", description="Perform Markov Chain Monte Carlo (MCMC) sampling")
29
+ def mcmc_sampling(model: object, num_samples: int, tune: int) -> dict:
 
 
 
 
30
  """
31
+ Perform Markov Chain Monte Carlo (MCMC) sampling.
32
 
33
+ Parameters:
34
+ - model (object): The PyMC model object.
35
+ - num_samples (int): Number of samples to draw.
36
+ - tune (int): Number of tuning steps.
37
 
38
  Returns:
39
+ - dict: A dictionary with success status and sampling results.
40
  """
41
  try:
42
+ import pymc as pm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  with model:
44
+ trace = pm.sample(draws=num_samples, tune=tune)
45
+ return {"success": True, "trace": trace}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  except Exception as e:
47
+ return {"success": False, "error": str(e)}
48
 
49
+ @mcp.tool(name="variational_inference", description="Perform Variational Inference (VI)")
50
+ def variational_inference(model: object, num_samples: int) -> dict:
 
 
 
 
 
 
 
51
  """
52
+ Perform Variational Inference (VI).
53
 
54
+ Parameters:
55
+ - model (object): The PyMC model object.
56
+ - num_samples (int): Number of samples to draw.
 
 
 
57
 
58
  Returns:
59
+ - dict: A dictionary with success status and VI results.
60
  """
61
  try:
62
+ import pymc as pm
 
 
 
 
63
  with model:
64
+ approx = pm.fit(n=num_samples)
65
+ return {"success": True, "approximation": approx}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  except Exception as e:
67
+ return {"success": False, "error": str(e)}
 
68
 
69
+ @mcp.tool(name="posterior_predictive_sampling", description="Generate posterior predictive samples")
70
+ def posterior_predictive_sampling(model: object, trace: object) -> dict:
71
  """
72
+ Generate posterior predictive samples.
73
 
74
+ Parameters:
75
+ - model (object): The PyMC model object.
76
+ - trace (object): The trace object from MCMC or VI.
77
 
78
  Returns:
79
+ - dict: A dictionary with success status and posterior predictive samples.
80
  """
81
  try:
82
+ import pymc as pm
83
+ with model:
84
+ ppc = pm.sample_posterior_predictive(trace)
85
+ return {"success": True, "posterior_predictive": ppc}
 
 
 
 
 
 
 
 
 
 
86
  except Exception as e:
87
+ return {"success": False, "error": str(e)}
88
 
89
+ @mcp.tool(name="model_visualization", description="Visualize the model using Graphviz or Mermaid")
90
+ def model_visualization(model: object, format: str) -> dict:
 
91
  """
92
+ Visualize the model using Graphviz or Mermaid.
93
 
94
+ Parameters:
95
+ - model (object): The PyMC model object.
96
+ - format (str): Visualization format (e.g., "graphviz", "mermaid").
97
 
98
  Returns:
99
+ - dict: A dictionary with success status and visualization data.
100
  """
101
  try:
102
+ from pymc.model_graph import model_to_graphviz, model_to_mermaid
103
+ if format == "graphviz":
104
+ visualization = model_to_graphviz(model)
105
+ elif format == "mermaid":
106
+ visualization = model_to_mermaid(model)
107
+ else:
108
+ raise ValueError("Unsupported visualization format.")
109
+ return {"success": True, "visualization": visualization}
 
 
 
 
 
 
 
 
110
  except Exception as e:
111
+ return {"success": False, "error": str(e)}
112
 
113
+ @mcp.tool(name="model_diagnostics", description="Perform diagnostics on the model")
114
+ def model_diagnostics(trace: object) -> dict:
 
115
  """
116
+ Perform diagnostics on the model.
117
 
118
+ Parameters:
119
+ - trace (object): The trace object from MCMC or VI.
120
 
121
  Returns:
122
+ - dict: A dictionary with diagnostic results (e.g., Rhat, ESS).
123
  """
124
  try:
125
+ import arviz as az
126
+ diagnostics = {
127
+ "rhat": az.rhat(trace).to_dict(),
128
+ "ess": az.ess(trace).to_dict()
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
+ return {"success": True, "diagnostics": diagnostics}
131
  except Exception as e:
132
+ return {"success": False, "error": str(e)}
 
133
 
134
+ @mcp.tool(name="load_data", description="Load data for modeling")
135
+ def load_data(data_path: str) -> dict:
 
 
 
 
136
  """
137
+ Load data for modeling.
138
 
139
+ Parameters:
140
+ - data_path (str): Path to the data file.
 
 
141
 
142
  Returns:
143
+ - dict: A dictionary with success status and loaded data.
144
  """
145
  try:
146
+ import pandas as pd
147
+ data = pd.read_csv(data_path)
148
+ return {"success": True, "data": data}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  except Exception as e:
150
+ return {"success": False, "error": str(e)}
 
151
 
152
+ @mcp.tool(name="save_model", description="Save a PyMC model to a file")
153
+ def save_model(model: object, file_path: str) -> dict:
154
  """
155
+ Save a PyMC model to a file.
156
 
157
+ Parameters:
158
+ - model (object): The PyMC model object.
159
+ - file_path (str): Path to save the model.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  Returns:
162
+ - dict: A dictionary with success status.
163
  """
164
  try:
165
+ import pickle
166
+ with open(file_path, "wb") as f:
167
+ pickle.dump(model, f)
168
+ return {"success": True}
 
169
  except Exception as e:
170
+ return {"success": False, "error": str(e)}
 
171
 
172
+ @mcp.tool(name="load_model", description="Load a PyMC model from a file")
173
+ def load_model(file_path: str) -> dict:
174
  """
175
+ Load a PyMC model from a file.
176
 
177
+ Parameters:
178
+ - file_path (str): Path to the model file.
179
 
180
  Returns:
181
+ - dict: A dictionary with success status and loaded model.
182
  """
183
  try:
184
+ import pickle
185
+ with open(file_path, "rb") as f:
186
+ model = pickle.load(f)
187
+ return {"success": True, "model": model}
 
 
 
 
 
188
  except Exception as e:
189
+ return {"success": False, "error": str(e)}
 
190
 
191
+ @mcp.tool(name="export_results", description="Export results to a specified format")
192
+ def export_results(results: object, output_format: str) -> dict:
193
  """
194
+ Export results to a specified format.
195
 
196
+ Parameters:
197
+ - results (object): The results object (e.g., trace, posterior predictive samples).
198
+ - output_format (str): Desired output format (e.g., "csv", "json").
199
 
200
  Returns:
201
+ - dict: A dictionary with success status and export details.
202
  """
203
  try:
204
+ if output_format == "csv":
205
+ import pandas as pd
206
+ df = pd.DataFrame(results)
207
+ df.to_csv("results.csv", index=False)
208
+ return {"success": True, "file": "results.csv"}
209
+ elif output_format == "json":
210
+ import json
211
+ with open("results.json", "w") as f:
212
+ json.dump(results, f)
213
+ return {"success": True, "file": "results.json"}
214
+ else:
215
+ raise ValueError("Unsupported output format.")
216
  except Exception as e:
217
+ return {"success": False, "error": str(e)}
 
218
 
219
  def create_app() -> FastMCP:
220
  """
221
  Create and return the FastMCP application instance.
222
 
223
  Returns:
224
+ - FastMCP: The FastMCP application instance.
225
  """
226
  return mcp