Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include Extern specification #73

Merged
merged 4 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion grammars/Quil.g4
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ instr : gate
| include
| pragma
| memoryDescriptor
| extern
| call
;

// C. Static and Parametric Gates
Expand Down Expand Up @@ -118,6 +120,13 @@ include : INCLUDE STRING ;
pragma : PRAGMA IDENTIFIER pragma_name* STRING? ;
pragma_name : IDENTIFIER | INT ;


// N. Declaring and Calling External Functions

extern : EXTERN IDENTIFIER ;
call : CALL IDENTIFIER call_arg+ ;
call_arg : addr | number ;

// Expressions (in order of precedence)

expression : LPAREN expression RPAREN #parenthesisExp
Expand All @@ -131,7 +140,7 @@ expression : LPAREN expression RPAREN #parenthesisExp
| addr #addrExp
;

function : SIN | COS | SQRT | EXP | CIS ;
function : SIN | COS | SQRT | EXP | CIS | IDENTIFIER;
sign : PLUS | MINUS ;

// Numbers
Expand Down
2 changes: 1 addition & 1 deletion specgen/quil.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
(let ((doc (make-instance 'document
:title "Quil Specification"
:author "Robert S. Smith; Rigetti & Co. Inc.; and contributors"
:version "2021.1 (DRAFT)"
:version "2024.1 (DRAFT)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we take out of draft given that it's in production use? It may (does) still have errors, but that's the nature of it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll update this in a later commit, and yes remove the draft

:body (append
(include (spec/ "sec-intro.s"))
(include (spec/ "sec-opsem.s"))
Expand Down
109 changes: 109 additions & 0 deletions specgen/spec/sec-other.s
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,120 @@ semantics.}
PRAGMA @ms{Identifier} @rep{@group{@ms{Identifier} @alt @ms{Integer}}} @rep[:max 1]{@ms{String}}
}


@subsection[:title "Extern Functions"]

@p{Programmers and researchers may wish to avail themselves of a rich
vocabulary of analytic and stochastic functions in the designs of
their experiments and algorithms. Quantum control systems or quantum
simulators that consume Quil code may support a variety of functions
that operate on classical data. Quil addresses these cases by
supporting the declaration of extern functions.}

@subsubsection[:title "Declaring Externs"]

@p{Declaring an identifier to be an extern indicates that the
identifier can appear in call instructions.}

@syntax[:name "Extern Function Declaration"]{
EXTERN @ms{Identifier}
}

@subsubsection[:title "Extern Signature Pragmas"]

@p{Under some circumstances it may be desirable to specify the
function signature of an external function. Signature declarations are
supplied by way of a special pragma.}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps from earlier discussions, but I've missed why we're making the signature optional.

It's required when used in expressions but not when used with CALL, and I'm unclear on why we would treat those differently since they seem to share concerns

Copy link
Contributor Author

@macrologist macrologist Apr 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A CALL has an effect on the abstract machine. The type signature of a function passed to a CALL does not alter the meaning of the CALL as pertains to the abstract machine in terms of which Quil's semantics are defined. (the QAM has no way of knowing what an extern function does.) So type signatures are rightly specified in PRAGMAs - that is, their presence does not have an effect on the semantics of the program.

However, in the interests of keeping all our toes firmly affixed to the fronts of our feet, the footgun of value-mutating externs appearing inside expressions has been explicitly disallowed.

In some compilation steps expressions can be copied (in say, gate parameter positions). If an expression mutates the memory on which it operates, then copies of that expression are no longer guaranteed to have equivalent evaluations, which would break compilation.


@syntax[:name "Extern Signature Pragma"]{
PRAGMA EXTERN @ms{Identifier} "@ms{Extern Signature}"
}

@syntax[:name "Extern Signature"]{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nailed it.

@rep[:min 0 :max 1]{@ms{Base Type}} ( @ms{Extern Parameter} @rep[:min 0]{@group{ , @ms{Extern Parameter} }} )
}

@syntax[:name "Extern Parameter"]{
@ms{Identifier} : @rep[:min 0 :max 1]{mut} @ms{Type}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just calling out this requires parameter names. I don't view that as a hard requirement for this spec change, but neither am I opposed to it.

I think it does deserve a bit of exposition, such as:

Quil requires the type signature to name all extern function parameters. This adds in program readability and debugging.

}

@subsubsection[:title "Call Instructions"]

@p{Declared externs may be appear in call instructions. The precise
meaning of an extern call is left up to the implementor. From the
perspective of the abstract machine, a call to an external function
increments the program counter and has some effect on classical
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest rephrasing this:

a call to an extern function will result in advancement of the program counter...

This is a bit nit-picky, but my concern is that the original wording implies the extern function itself controls and modifies the program counter, whereas from the POV of a hardware control system, the extern function just executes, something else at a higher level may be controlling the program counter.

memory.}

@syntax[:name "Extern Call Instruction"]{
CALL @ms{Identifier} @rep[:min 1]{@group{@ms{Identifier} @alt @ms{Memory Reference} @alt @ms{Complex}}}
}

@p{When a supplied function type signature specifies a return type,
then calls to the associated extern are assumed to write their return
value to a memory reference passed in at the first argument position.
E.g. the following are equivalent:}

@clist{
PRAMGA EXTERN rng "INTEGER (seed : INTEGER)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PRAMGA EXTERN rng "INTEGER (seed : INTEGER)"
PRAGMA EXTERN rng "INTEGER (seed : INTEGER)"

EXTERN rng;
DECLARE num INTEGER
... snip ...

CALL rng num 10
}

@p{is roughly the same as}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@p{is the same as}

or

@p{is equivalent to}


@clist{
PRAMGA EXTERN rng "(out : mut INTEGER, seed : INTEGER)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PRAMGA EXTERN rng "(out : mut INTEGER, seed : INTEGER)"
PRAGMA EXTERN rng "(out : mut INTEGER, seed : INTEGER)"

EXTERN rng;
DECLARE num INTEGER
... snip ...

CALL rng num 10
}


@subsubsection[:title "Externs in Arithmetic Expressions"]
Copy link
Contributor

@kalzoo kalzoo Mar 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use this opportunity to remove the baked-in-but-not-explicitly-enumerated functions from the spec and state that all functions in expressions must be declared?

Since that could make for a hard cutover, we could state that Quil implicitly declares that list of functions, and then state their PRAGMA EXTERN and EXTERNs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm more or less on board but it should be addressed more widely than in a comment to a PR before we make it explicit. I think we want to publish the "2024.1" spec version in the next month or so.


@p{Extern calls may appear in arithmetic expressions with some
restrictions: the extern MUST have a known function signature, which
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
restrictions: the extern MUST have a known function signature, which
restrictions: the extern MUST have a declared function signature, which

MUST include a return type, and which MUST NOT include any mutable
parameters.}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It must specifically return a single real value, right? I mention "real" here in light of the upcoming discussion on custom types

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why must it return a real value? Expressions are also used in matrix lines, which may be used in DEFWAVEFORM, so it could be useful to have a complex return type, right?

I'd maybe even go a step further - regardless as to current utility, design intent is clearer if the extern functions can return any type; it's the support for expressions that is the current / practical constraint here.


@p{For example, this is OK:}

@clist{
PRAMGA EXTERN prng "REAL (seed : REAL)"
EXTERN prng;
RX(prng(pi) / 4)
}

@p{But this is not:}

@clist{

PRAGMA EXTERN irng "INTEGER (seed : mut INTEGER)"
EXTERN irng;
EXTERN prng;
DECLARE num INTEGER;

... snip ...

RX(irng(num)) # WRONG: the seed parameter is declared mutable

RZ(prng(33)) # WRONG: we don't know the signature of prng

}


@subsection[:title "File Inclusion"]

@p{One can include a valid Quil file in another valid Quil file by
inclusion.}


@syntax[:name "File Include"]{
INCLUDE @ms{String}
}
Expand Down
4 changes: 3 additions & 1 deletion specgen/spec/sec-structure.s
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ i pi
ADD AND AS CONTROLLED CONVERT DAGGER DECLARE DEFCIRCUIT DEFGATE DIV EQ
EXCHANGE FORKED GE GT HALT INCLUDE IOR JUMP JUMP-UNLESS JUMP-WHEN
LABEL LE LOAD LT MATRIX MEASURE MOVE MUL NEG NOP NOT OFFSET PAULI-SUM
PERMUTATION PRAGMA RESET SHARING STORE SUB WAIT XOR
PERMUTATION PRAGMA RESET SHARING STORE SUB WAIT XOR EXTERN CALL
Copy link
Contributor

@kalzoo kalzoo Mar 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're all on board, but just for the record, this makes this a breaking change, something which is not expressed anywhere in the spec or the nature of quil (e.g. the spec version number does not communicate that, and the program itself does not carry its version)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be in favor of a Quil version declaration. It could default to the last version that didn't support it. For example:

VERSION '2024.1' # otherwise assumed to be `2021.1`
# ... etc

}
}

Expand Down Expand Up @@ -252,6 +252,7 @@ object, like classical memory registers.}
@ms{Gate Definition}
@alt @ms{Circuit Definition}
@alt @ms{Classical Memory Declaration}
@alt @ms{Extern Function Declaration} @ms{Terminator}
}

@p{A @emph{directive} specifies information to software processing
Expand All @@ -270,6 +271,7 @@ Quil, such as the @quil{INCLUDE} directive for including files.}
@alt @ms{Measurement Instruction}
@alt @ms{Circuit Application}
@alt @ms{Classical Memory Instruction}
@alt @ms{Extern Call Instruction}
@alt @ms{Reset Instruction}
@alt @ms{Wait Instruction}
@alt @ms{Branch Instruction}
Expand Down