quil-lang / quilc

The optimizing Quil compiler.
Apache License 2.0
448 stars 73 forks source link

A Collection of Curious Quil Compilations #245

Open appleby opened 5 years ago

appleby commented 5 years ago

While poking quilc with my stick, I came across a handful of (to me) surprising compilations. Most of these are "wacky" / useless programs and all involve multiple WAIT instructions, which I am not even sure is supported.

I am posting these here for comments in case any of these are user error. I will spin off separate issues for any that I am able to determine are actual bugs.

These all came about after I noticed that HALT, WAIT, RESET, and NOP instructions are represented as singleton classes, which made me wonder if there was someway to exploit that to trip quilc up.

Curio No. 1: WAIT, WAIT, BOOM!

This is the only one I'm fairly certain is a bug or should at least report a better error message.

You can stick most other instructions between the WAITs and get the same result.

./quilc-unsafe <<EOF
> WAIT
> WAIT
> EOF
Unhandled TYPE-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING
                                  {10006185B3}>:
  The value
    NIL
  is not of type
    NUMBER
  when binding SB-KERNEL::X

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10006185B3}>
0: (SB-KERNEL:TWO-ARG-+ NIL 1) [external]
1: ((LAMBDA (CL-QUIL::INSTR CL-QUIL::VALUE) :IN "/home/ma/src/repos/rigetti/quilc/src/addresser/logical-schedule.lisp") #<unused argument> NIL)
2: (CL-QUIL::LSCHEDULER-WALK-GRAPH #<CL-QUIL::LOGICAL-SCHEDULER {10058BCD53}> :BASE-VALUE 0 :BUMP-VALUE NIL :TEST-VALUES NIL)
3: (CL-QUIL::LSCHEDULER-ALL-INSTRUCTIONS #<CL-QUIL::LOGICAL-SCHEDULER {10058BCD53}>)
4: (CL-QUIL::CHIP-SCHEDULE-TO-STRAIGHT-QUIL #<CL-QUIL::CHIP-SCHEDULE {10058BC8F3}>)
5: (CL-QUIL::DO-GREEDY-TEMPORAL-ADDRESSING (#1=#<WAIT> #1#) #<CL-QUIL::CHIP-SPECIFICATION of 8:8 objects> :ENVIRONS #<CL-QUIL:PARSED-PROGRAM {1004D3D753}> :INITIAL-REWIRING #S(CL-QUIL::REWIRING :L2P #(0 1 2 3 4 5 6 7) :P2L #(0 1 2 3 4 5 6 7)) :USE-FREE-SWAPS T)
6: (CL-QUIL:COMPILER-HOOK #<CL-QUIL:PARSED-PROGRAM {1004D3D753}> #<CL-QUIL::CHIP-SPECIFICATION of 8:8 objects> :PROTOQUIL NIL :REWIRING-TYPE NIL)
7: (QUILC::PROCESS-PROGRAM #<CL-QUIL:PARSED-PROGRAM {1004D3D753}> #<CL-QUIL::CHIP-SPECIFICATION of 8:8 objects>)
8: (QUILC::PROCESS-OPTIONS :PREFER-GATE-LADDERS NIL :COMPUTE-GATE-DEPTH NIL :COMPUTE-GATE-VOLUME NIL :COMPUTE-RUNTIME NIL :COMPUTE-FIDELITY NIL :COMPUTE-MATRIX-REPS NIL :COMPUTE-2Q-GATE-DEPTH NIL :COMPUTE-UNUSED-QUBITS NIL :SHOW-TOPOLOGICAL-OVERHEAD NIL :GATE-BLACKLIST NIL :GATE-WHITELIST NIL :WITHOUT-PRETTY-PRINTING NIL :PRINT-LOGICAL-SCHEDULE NIL :VERBOSE NIL :JSON-SERIALIZE NIL :ISA "8Q" :ENABLE-STATE-PREP-REDUCTIONS NIL :PROTOQUIL NIL :VERSION NIL :CHECK-LIBRARIES NIL :BENCHMARK NIL :SERVER-MODE-HTTP NIL :SERVER-MODE-RPC NIL :HOST "*" :PORT 5555 :TIME-LIMIT 0 :HELP NIL :LOG-LEVEL "info" :QUIET NIL :CHECK-SDK-VERSION NIL :PROXY NIL)
9: (QUILC::%ENTRY-POINT NIL)
10: ((LAMBDA NIL :IN MAKE-TOPLEVEL-FUNCTION))
11: ((FLET SB-UNIX::BODY :IN SAVE-LISP-AND-DIE))
12: ((FLET "WITHOUT-INTERRUPTS-BODY-14" :IN SAVE-LISP-AND-DIE))
13: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))

unhandled condition in --disable-debugger mode, quitting

Curio No. 2: Comment sharing

This is now #270

./quilc <<EOF
> WAIT
> RESET
> WAIT
> EOF
WAIT                                    # Entering rewiring: #(0 1 2 3 4 5 6 7)
WAIT                                    # Entering rewiring: #(0 1 2 3 4 5 6 7)
HALT                                    # Exiting rewiring: #(0 1 2 3 4 5 6 7)

Notice that both WAIT instructions have the same "entering rewiring" with no intervening "exiting rewiring" comment. Expected?

I think this can happen with any quil program that contains multiple NOP, RESET, WAIT, HALT, etc instructions, since rewiring comments are stored in a hash-table keyed on the instructions and all those instructions are singletons. In other words, every instance of those singleton instructions in a program will have the same rewiring comment attached.

Consider also the following REPL session, where the intervening call to compiler-hook affects the printed representation of the parsed-program. I do not claim this a bug per-se, but it demonstrates the comment sharing, which could explain the above quilc output.

CL-USER> (progn
       (remhash (make-instance 'quil::wait) quil::**comments**)
       (quil::print-parsed-program (quil:parse-quil "WAIT"))
       (quil:compiler-hook (quil:parse-quil "WAIT") (quilc::lookup-isa-descriptor-for-name "8Q"))
       (quil::print-parsed-program (quil:parse-quil "WAIT")))
WAIT
WAIT                                    # Entering rewiring: #(0 1 2 3 4 5 6 7)
NIL
CL-USER> (progn
       (remhash (make-instance 'quil::halt) quil::**comments**)
       (quil::print-parsed-program (quil:parse-quil "HALT"))
       (quil:compiler-hook (quil:parse-quil "HALT") (quilc::lookup-isa-descriptor-for-name "8Q"))
       (quil::print-parsed-program (quil:parse-quil "HALT")))
HALT
HALT                                    # Entering/exiting rewiring: (#(0 1 2 3 4 5 6 7) . #(0 1 2 3 4 5 6 7))
NIL

Curio No. 3: The Case of the Disappearing MEASURE

This next one does what I'd expect.

./quilc <<EOF
> DECLARE ro BIT[2]
> MEASURE 0 ro[0]
> WAIT
> MEASURE 1 ro[1]
> EOF
DECLARE ro BIT[2]

MEASURE 0 ro[0]                         # Entering rewiring: #(0 1 2 3 4 5 6 7)
WAIT
MEASURE 1 ro[1]
HALT                                    # Exiting rewiring: #(0 1 2 3 4 5 6 7)

But adding a second WAIT after the final MEASURE causes both WAITS and the final MEASURE to be dropped.

./quilc <<EOF
> DECLARE ro BIT[2]
> MEASURE 0 ro[0]
> WAIT
> MEASURE 1 ro[1]
> WAIT
> EOF
DECLARE ro BIT[2]

MEASURE 0 ro[0]                         # Entering rewiring: #(0 1 2 3 4 5 6 7)
HALT                                    # Exiting rewiring: #(0 1 2 3 4 5 6 7)

You can make this one more interesting by actually doing something with the qubits between the measurements.

./quilc <<EOF
> DECLARE ro BIT[2]
> RESET
> H 0
> MEASURE 0 ro[0]
> WAIT
> H 1
> MEASURE 1 ro[1]
> WAIT
> EOF
DECLARE ro BIT[2]

RZ(pi/2) 0                              # Entering rewiring: #(0 1 2 3 4 5 6 7)
RX(pi/2) 0
RZ(pi/2) 0
MEASURE 0 ro[0]
HALT                                    # Exiting rewiring: #(0 1 2 3 4 5 6 7)
notmgsk commented 5 years ago

Concur and commiserate. Quite cool.

stylewarning commented 5 years ago

All of these look like bugs.

appleby commented 5 years ago

Curio No. 2 now has it's own issue: #270.