[Question] Use-cases for computations, other than running them?

In im­per­a­tive pro­gram­ming lan­guages, the main pur­pose of a pro­gram is to spec­ify a com­pu­ta­tion, which we then run. But it seems a rather… uni­mag­i­na­tive use of a com­pu­ta­tion, sim­ply to run it.

Hav­ing speci­fied a com­pu­ta­tion, what else might one want to do with it?

Some ex­am­ples:

  • Differ­en­ti­ate nu­mer­i­cal com­pu­ta­tions (i.e. back­prop)

  • Ask whether any pos­si­ble in­puts could yield a par­tic­u­lar out­put (i.e. NP prob­lems)

  • Search for data within the com­pu­ta­tion’s out­put space (i.e. grep)

  • Given out­put from the com­pu­ta­tion, find a data struc­ture which traces its ex­e­cu­tion (i.e. pars­ing with con­text-free gram­mars)

  • Par­allelize the computation

  • In­ter­ven­tions & coun­ter­fac­tu­als: ask what will hap­pen/​what would have hap­pened if some in­ter­nal vari­able at some step of the com­pu­ta­tion were as­signed a differ­ent value, keep­ing the rest of the com­pu­ta­tion the same

  • Gen­er­ate a new com­pu­ta­tion which op­ti­mizes the out­put of the origi­nal com­pu­ta­tion via dy­namic programming

  • Find sum­mary statis­tics de­scribing the com­pu­ta­tion (i.e. pro­filer, max­i­mum forces en­coun­tered in a phys­i­cal simu­la­tion, …?)

  • Embed one com­pu­ta­tion within an­other (i.e. a com­piler em­beds com­pu­ta­tion speci­fied in Python into the com­pu­ta­tion performed by a CPU)

    • Use knowl­edge of the in­ter­nal struc­ture of the two com­pu­ta­tions to op­ti­mize the em­bed­ding (i.e. op­ti­miz­ing com­piler)

  • Ask what pa­ram­e­ters/​out­puts of the com­pu­ta­tion need to be ob­served in or­der to re­li­ably de­duce other pa­ram­e­ters/​out­puts (i.e. in­fer­ence, iden­ti­fi­a­bil­ity, & ex­per­i­ment de­sign in causal mod­els)

  • Ask whether ob­served data is con­sis­tent with the com­pu­ta­tion (i.e. regexes & CFGs; causal model com­par­i­son)

  • Depen­dency: is the out­put (or some set of in­ter­me­di­ates) in­de­pen­dent of some in­ter­nal value, or in­de­pen­dent of the dis­tinc­tion be­tween two (or more) pos­si­ble val­ues at some point in the com­pu­ta­tion?

  • Can we keep around a small amount of data suffi­cient to quickly re­con­struct any­thing we might want to know about the full com­pu­ta­tion (i.e. in­ter­me­di­ate val­ues and ex­e­cu­tion path)?

  • Was any com­pu­ta­tion re­peated (i.e. some­thing we could op­ti­mize out via mem­o­iza­tion)?

  • Are there any un­used pat­terns/​sym­me­tries in the com­pu­ta­tional DAG (i.e. po­ten­tial for re­fac­tor­ing the code)?

  • Big-O run­time analysis

I’m also in­ter­ested in more gen­eral meta-use-cases for com­pu­ta­tions, which gen­er­ate use-cases that aren’t just “run the com­pu­ta­tion”. For in­stance, some pat­terns in the ex­am­ples above:

  • When con­sid­er­ing phys­i­cal pro­cesses as com­pu­ta­tions (i.e. simu­la­tions), we of­ten want to query/​vi­su­al­ize in­ter­me­di­ates in the com­pu­ta­tion in var­i­ous ways

  • When treat­ing com­pu­ta­tions alge­braically—i.e. solv­ing com­pu­ta­tion(in­put) = out­put given the com­pu­ta­tion and the out­put—we usu­ally want to rea­son about the in­ter­nal struc­ture of the com­pu­ta­tion.

  • More gen­er­ally, given only par­tial in­for­ma­tion from the com­pu­ta­tion, de­duc­ing other in­for­ma­tion usu­ally in­volves op­er­a­tions other than just run­ning the com­pu­ta­tion. Again, this is es­pe­cially rele­vant when the com­pu­ta­tion simu­lates some phys­i­cal pro­cess about which we have par­tial in­for­ma­tion.

  • Any­thing in­volv­ing run­time, pro­filing, op­ti­miza­tion, de­bug­ging, etc will typ­i­cally in­volve look­ing at the in­ter­nal struc­ture of the com­pu­ta­tion.