Model Lifecycle =============== ``mixle.Model`` is a convenience facade over the library's main lifecycle: propose a model, fit it, evaluate it, inspect it, query it, distill it, and deploy it. It does not introduce a separate inference engine. It gives users one place to stand while delegating to the same distribution, inference, capability, task, and artifact systems used elsewhere. Use it when a workflow needs a durable object with consistent verbs. Use the lower-level APIs directly when you are developing a new estimator, benchmarking an inference route, or controlling each E-step and M-step explicitly. Basic Flow ---------- .. code-block:: python import mixle model = mixle.Model().fit(rows) quality = model.evaluate(holdout) samples = model.sample(5, seed=0) explanation = model.explain() print(quality["mean_log_density"]) print(explanation) ``Model(spec=None)`` means "infer the estimator from data at fit time." The ``spec`` may also be an estimator or a prototype distribution: .. code-block:: python from mixle.stats import GaussianEstimator, GaussianDistribution from_estimator = mixle.Model(GaussianEstimator()).fit(values) from_prototype = mixle.Model(GaussianDistribution(0.0, 1.0)).fit(values) Fitting delegates to ``mixle.inference.optimize``. Scoring, sampling, enumeration, and posterior calls delegate to the fitted distribution. Certified Creation Alternative ------------------------------ Use ``mixle.inference.create`` when you want a fitted artifact plus explicit post-conditions rather than a convenience lifecycle facade. .. code-block:: python from mixle.inference import create artifact = create(rows, calibrate=0.2, quantify_uq=True, seed=0) print(artifact.certificate.table()) ``CreatedModel`` carries the fitted model, an estimation certificate, optional calibration and UQ objects, and provenance. ``Model`` is still the shorter interactive facade; ``create`` is the stronger artifact boundary. Proposal Frontier ----------------- ``mixle.propose`` builds a candidate frontier and returns a ``Model`` whose ``spec`` is the best candidate: .. code-block:: python proposed = mixle.propose(rows, fit=False) for candidate in proposed.frontier: print(candidate) proposed.fit(rows) The frontier can include: * ``recommend_model`` from ``mixle.task`` for dependency-aware structural recommendation; * the plain automatic estimator from ``mixle.utils.automatic.get_estimator`` as an independence baseline; * an LLM-designed model from ``mixle.task.design_model`` when an LLM handle is provided. Each candidate is fit on a train split and scored on held-out data. Candidate failures are reported in the frontier instead of being silently ignored. The winning estimator becomes the model spec, while field-level recommendation notes and dependency hints are stored in ``Model.notes``. Automatic Restart Guard ----------------------- Latent-variable models can land on symmetric saddle points, especially mixtures whose components start identical. ``Model.fit`` uses ``restarts="auto"`` by default: .. code-block:: python model = mixle.Model(mixture_estimator).fit(rows) The fit first runs the ordinary route. If the fitted object exposes a posterior and components, Mixle checks whether every sampled observation has an almost uniform component posterior. When that pattern suggests a symmetric saddle, the model is refit with symmetry-breaking restarts and the better likelihood is kept. Notes record whether the automatic restart changed the result. Pass an integer to force a number of restarts, or ``restarts=None`` to keep the raw single fit. Query Verbs ----------- Once fitted, the lifecycle object exposes common distribution queries: .. code-block:: python logp = model(x) top = model.enumerate().top_k(10) z = model.posterior(x) forecast = model.forecast(history, horizon=7) effect = model.do({"treatment": "on"}) parts = model.explain_prediction(x) The available queries depend on the fitted model's capabilities. A continuous Gaussian will not enumerate. A mixture can expose posterior responsibilities. A Bayesian network can answer interventions when the necessary graph structure is available. Use ``mixle.describe(model.fitted)`` when you need to know what the object supports. Distillation ------------ ``Model.distill`` routes into ``mixle.task.solve``: .. code-block:: python solution = model.distill(teacher, examples, seed=0) answer = solution(new_input) If ``teacher`` is omitted, the fitted model teaches from its latent posterior: inputs are labeled by the most probable latent component. This turns a fitted mixture into a calibrated local classifier of its own clusters. The returned object is a task ``Solution``. It answers locally when calibrated and escalates to the teacher when it should not guess. See :doc:`task-distillation` and :doc:`task-serving`. Deployment ---------- ``Model.deploy`` writes a durable artifact directory: .. code-block:: python path = model.deploy("artifacts/customer-regime-model") restored = mixle.Model.load(path) The artifact contains the fitted model and a manifest with family, creation time, fit metadata, notes, and artifact schema name. This is a lightweight lifecycle artifact, not a full model registry. For production registry, provenance, drift, and serving concepts, see :doc:`production`. Reusable Skills --------------- ``mixle.inference.skill`` wraps a fitted model, ``CreatedModel``, or callable as a named capability. A skill can be registered, searched by query, indexed into a substrate, and used as a compute action by a reasoner. .. code-block:: python from mixle.inference import SkillRegistry, skill registry = SkillRegistry() sk = skill( "sample-customers", model.fitted, description="sample synthetic customer rows", registry=registry, ) print(registry.find("customer sample")) Use skills when the fitted artifact becomes an application verb. Use ``Model.deploy`` or ``Registry`` when the artifact is primarily a model version. When To Use Lower-Level APIs ---------------------------- Use the facade when: * you want a short path from raw data to a fitted, inspectable object; * you are building examples, notebooks, or application-level integrations; * you want proposal notes and held-out frontier scoring in one place; * the model lifecycle matters more than the exact optimizer steps. Use lower-level APIs when: * you need a fixed estimator tree with no recommendation step; * you are controlling initialization, streaming updates, or restarts manually; * you are testing a new distribution family, capability, or backend; * you need explicit access to encoded data, accumulators, kernels, or engines. API Reference ------------- * :doc:`api/mixle.lifecycle` * :doc:`api/mixle.inference` * :doc:`api/mixle.task.recommend` * :doc:`api/mixle.task.design` * :doc:`api/mixle.task.solve` * :doc:`api/mixle.inference.create` * :doc:`api/mixle.inference.skill`