Program Theory

The panproto protocol that QVR programs are schemas over (QVR_PROGRAM_PROTOCOL) and the extractor that walks a compiled environment to emit a panproto Schema (extract_program_schema).

program_theory

Program Theory: a panproto protocol for the resolved (post-compilation) DSL.

The :data:QVR_PROGRAM_PROTOCOL protocol describes the static structure of a compiled .qvr program — every object, space, morphism, and output the program declares — as a panproto :class:~panproto.Schema. This sits one layer above the syntactic qvr protocol from panproto-grammars-all: the syntactic protocol carries the AST as parsed, this protocol carries the AST after the resolution layer (_resolve_type, _resolve_space) has run.

Why have it

Once two programs share this protocol, panproto's structural diff, auto-lens-generation, and breaking-change detection apply to compiled programs as a whole. Two .qvr files that compile to structurally equivalent programs produce equal Schemas; two that diverge surface their divergence through :func:panproto.diff_schemas.

Vertex kinds

Top-level container: program the root vertex; one per compiled module.

Discrete objects (mirrors :mod:quivers.core.objects): finset product_set coproduct_set free_monoid empty_set

Continuous spaces (mirrors :mod:quivers.continuous.spaces): euclidean simplex positive_reals product_space

Top-level declarations: object_decl space_decl morphism_decl continuous_morphism_decl stochastic_morphism_decl discretize_decl embed_decl output_decl

Edge kinds

decl program -> *_decl: each declaration is a child of the root program. binds_to object_decl -> set_object / space_decl -> space. component product_set | coproduct_set -> set_object; product_space -> space: structural recursion into composite types. domain / codomain morphism_decl | continuous_morphism_decl | stochastic_morphism_decl -> set_object | space. output program -> output_decl.

Constraint sorts carry the per-vertex scalar metadata: name, cardinality, dim, low, high, family, modality, morphism_kind, replicate, n_bins.

extract_program_schema

extract_program_schema(compiler: 'Compiler') -> Schema

Produce a :class:panproto.Schema for a compiled program.

Walks the compiler's resolved environment (objects, spaces, morphisms) and emits a graph of vertices and edges in the :data:QVR_PROGRAM_PROTOCOL protocol. The returned schema validates against that protocol and is suitable for :func:panproto.diff_schemas, :func:panproto.auto_generate_lens, and the rest of panproto's schema-level operations.

PARAMETER DESCRIPTION
compiler

A :class:~quivers.dsl.compiler.Compiler after :meth:compile_env (or :meth:compile) has populated the resolved environments.

TYPE: 'Compiler'

RETURNS DESCRIPTION
Schema

A program-level Schema in the qvr_program protocol.

Source code in src/quivers/dsl/program_theory.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
def extract_program_schema(compiler: "Compiler") -> panproto.Schema:
    """Produce a :class:`panproto.Schema` for a compiled program.

    Walks the compiler's resolved environment (objects, spaces, morphisms)
    and emits a graph of vertices and edges in the
    :data:`QVR_PROGRAM_PROTOCOL` protocol. The returned schema validates
    against that protocol and is suitable for :func:`panproto.diff_schemas`,
    :func:`panproto.auto_generate_lens`, and the rest of panproto's
    schema-level operations.

    Parameters
    ----------
    compiler
        A :class:`~quivers.dsl.compiler.Compiler` after :meth:`compile_env`
        (or :meth:`compile`) has populated the resolved environments.

    Returns
    -------
    panproto.Schema
        A program-level Schema in the ``qvr_program`` protocol.
    """
    builder = QVR_PROGRAM_PROTOCOL.schema()
    writer = _SchemaWriter(builder)

    builder.vertex("program", "program")
    if compiler._quantale is not None:
        builder.constraint("program", "quantale", type(compiler._quantale).__name__)

    # object decls
    for name, obj in compiler._objects.items():
        decl_vid = f"object_decl::{name}"
        builder.vertex(decl_vid, "object_decl")
        builder.constraint(decl_vid, "name", name)
        builder.edge("program", decl_vid, "decl")
        target_vid = writer.write_set_object(obj)
        builder.edge(decl_vid, target_vid, "binds_to")

    # space decls
    for name, space in compiler._spaces.items():
        decl_vid = f"space_decl::{name}"
        builder.vertex(decl_vid, "space_decl")
        builder.constraint(decl_vid, "name", name)
        builder.edge("program", decl_vid, "decl")
        target_vid = writer.write_space(space)
        builder.edge(decl_vid, target_vid, "binds_to")

    # morphism decls — the compiler's _morphisms env holds named primitive
    # morphisms; we record them as morphism_decl vertices with domain/codomain.
    # Composite morphisms (let-bindings, output) are derived rather than
    # recorded directly; the structural Diff-able layer is the named decls.
    for name, morphism in compiler._morphisms.items():
        kind = _classify_morphism_kind(morphism)

        decl_vid = f"{kind}::{name}"
        builder.vertex(decl_vid, kind)
        builder.constraint(decl_vid, "name", name)
        builder.edge("program", decl_vid, "decl")

        dom = getattr(morphism, "domain", None)
        cod = getattr(morphism, "codomain", None)
        if dom is not None:
            dom_vid = writer.write_any(dom)
            builder.edge(decl_vid, dom_vid, "domain")
        if cod is not None:
            cod_vid = writer.write_any(cod)
            builder.edge(decl_vid, cod_vid, "codomain")

    # output decl — the compiler's `_output_expr` holds the AST expression
    # whose compilation produces the program's root morphism. We record an
    # output_decl vertex carrying the expression's source-text form (when
    # available) as a name constraint; the structural diff cares about
    # presence/absence of an output, not its detailed shape.
    if compiler._output_expr is not None:
        out_vid = "output_decl"
        builder.vertex(out_vid, "output_decl")
        # ExprIdent / ExprIdentity carry a single name; composite expressions
        # don't have a single canonical name, so mark them as "<composite>".
        expr = compiler._output_expr
        if isinstance(expr, ExprIdent):
            label = expr.name
        elif isinstance(expr, ExprIdentity):
            label = f"identity({expr.object_name})"
        else:
            label = "<composite>"
        builder.constraint(out_vid, "name", label)
        builder.edge("program", out_vid, "output")

    return builder.build()