Replies: 9 comments 20 replies
-
I agree with the conceptual problems you highlight. Would it help to instead reframe |
Beta Was this translation helpful? Give feedback.
-
Ok lots of good stuff here but lots of points.
|
Beta Was this translation helpful? Give feedback.
-
If the extension values can be represented as JSON, they can be represented by products, sums, literals and lists. We could even add tierkreis-like structs and enums with named fields and variants at some point to make it closer to JSON. Add to that custom type constructors, and it's quite expressive. You refer to loading a constant by copying its static value into whatever runtime structure is necessary. That captures it quite nicely: the static value isn't the runtime structure, but it is a description from which we can build the runtime structure. Regarding the type of the const node: yes, dependent types would be one solution to this. Another would be to not have a single constant node, but have nodes for the different data types. For instance, an llvm extension would have a I do not see how |
Beta Was this translation helpful? Give feedback.
-
My mental model of HUGR has been converging more and more to a subset of DLNL (https://www.cl.cam.ac.uk/~nk480/dlnl-paper.pdf). DLNL as a linear language and a non-linear language, where the non-linear language has dependent types. It's not entirely precise, and we restrict the dependency more than DLNL does, but I think it is very helpful to put it in this context. For our purposes, think of the linear language as the runtime values. Operations are functions in the linear language, and a dataflow graph describes how to compose operations in the linear language (if you like categorical semantics, the dataflow graph is a string diagram for the symmetric monoidal category underlying the linear language). The non-linear language is the language of parameters/arguments/types/statically known values/terms; whatever you want to call them. A linear value may depend on non-linear values, but not the other way around. For us this means that runtime values may depend on static values, but static values may not depend on runtime values. A runtime vector whose length depends on a static parameter is fine. A static parameter that embeds a qubit is not. Now DLNL allows to take a linear value |
Beta Was this translation helpful? Give feedback.
-
Static language = exclusively non-linear, all evaluation by substitution (no "operations"), fine. Runtime language includes static language but adds linear, ok. Maybe exclusively linear, if we can convert flexibly enough, but note we need to be able to have runtime-only operations operating on non-linear values, the results are not part of the static language (I think the fact that these have incoming edges means they aren't?), but are still nonlinear.
We enforce only static edges into a FuncDefn e.g. so you can have mutually recursive functions (FuncDefn A contains call with edge from FuncDefn B).
Ok, so maybe not using naming. I think the issues that need addressing here then are how this reference is represented particularly wrt. edges (which we want for rewriting, for example). |
Beta Was this translation helpful? Give feedback.
-
Spoke briefly with @ss2165 and it seems we may not care about supporting "nested Hugrs" (as opposed to subtrees) anymore (perhaps that requirement may have originated before we had nested/scoped definitions?). That significantly frees up the design space. Still curious as to how the "translation from static values into runtime ones" would work for FuncDefn nodes into consts. Quoting from elsewhere:
Yes, having a way for opaque/extension Values to be able to refer to nodes in the Hugr does seem useful. |
Beta Was this translation helpful? Give feedback.
-
One thing I am not yet quite sure about is whether it is best to separate runtime and static types. Is the type of runtime naturals the same as the type of static naturals? DLNL has universes I have a vague feeling that the copyable/all bounds, and the split between |
Beta Was this translation helpful? Give feedback.
-
This is an idea on how to express something like const nodes without having to have runtime values. Consider the following operation: (define-operation core.const
(forall ?runtime type)
(forall ?static static)
(forall ?ext ext-set)
(param ?value ?static)
(where (core.make-const ?runtime ?static ?ext ?value))
(fn [] [?runtime] ?ext)) This operation produces a runtime value of type |
Beta Was this translation helpful? Give feedback.
-
The result of a conversation with @acl-cqc: The quote type is currently used for function definitions: A function definition does not merely define a single function, i.e. a single value of type We can use quote types for more: a static description of how to produce a runtime value, after all, is exactly what we want to do with constants! So we could define a constant operation as follows: (declare-operation core.const
(forall ?t type)
(param ?c (quote ?t))
(fn [] [?t] (ext))) But how do we produce terms of type (declare-ctr arith.u64-const (param ?v nat) (quote arith.u64))
(declare-ctr arith.u32-const (param ?v nat) (quote arith.u32))
(declare-ctr quantum.comp-basis (param ?v bool) (quote quantum.qubit))
(declare-ctr quantum.hadamard-basis (param ?v bool) (quote quantum.qubit))
(declare-ctr tierkreis.protobuf-graph (param ?protobuf bytes) (quote tierkreis.graph)) To make this work with the extension sets system, the quote type would need to be adjusted to take an additional parameter: Then |
Beta Was this translation helpful? Give feedback.
-
I'm currently trying to figure out how to deal with constants and Values. Values appear to occupy a strange conceptual place. In Tierkreis they made a lot of sense: we were specifying a single language with one particular semantics, and the Tierkreis Value is the data structure that is sent around. HUGR doesn't have one semantics. A value that flows along a hugr edge might be an int in an llvm register, or a serialised data structure to be sent over the network like in Tierkreis, or represent an Yb171 ion that at the very moment sits in some machine somewhere in Colorado. In my mind, runtime values in hugr are something that can't be accessed directly but rather only talked about indirectly, e.g. by creating a graph through which the value must flow. I don't know if that description makes sense, so I'd happily elaborate.
The significance for hugr comes from const nodes. At the moment,
Const
nodes together withLoadConst
nodes take aValue
and put it on a wire. Adopting the world view described above, what does it even mean to put a Value on a wire? An LLVM program does not have Values, and a qubit can definitely never be a Value. It makes sense when you think of the Const /LoadConst nodes taking in a description of how to construct a value; so Value is not the value itself, but a blueprint.But at that point,
Value
s and const nodes become redundant in the format, since we already have the language of terms (i.e.Type
s,TypeArg
s andTypeParam
s when suitably combined like in #1405) which can specify compile time values. To put an integer on a wire, we can use an operation that takes an integer as a parameter. Similarly, we have products, sums, custom named types, string literals, etc for statically known data.Const
nodes serve an additional purpose: a singleConst
node can be referenced by multipleConstLoad
operations throughout a graph. But we also have a mechanism for this for terms viaAliasDefn
andAliasDecl
nodes. They currently only work for types, but since the difference between types and terms is quite blurry already, we could make them work for arbitrary terms.Beta Was this translation helpful? Give feedback.
All reactions