def test_rewrite_arithmetic_mixed_mutations(): fdefn = DefFrame( frame=Frame([Qubit(0)], "rf"), center_frequency=10.0, sample_rate=20.0, ) prog = Program( fdefn, "DECLARE theta REAL", 'SET-FREQUENCY 0 "rf" theta', 'SET-PHASE 0 "rf" theta', 'SET-SCALE 0 "rf" theta', ) response = rewrite_arithmetic(prog) assert response == RewriteArithmeticResponse( original_memory_descriptors={ "theta": ParameterSpec(length=1, type="REAL") }, recalculation_table={ ParameterAref(index=0, name="__P1"): "(theta[0] - 10.0)/20.0", ParameterAref(index=1, name="__P1"): "theta[0]/(2*pi)", ParameterAref(index=2, name="__P1"): "theta[0]/8", }, quil=Program( fdefn, "DECLARE __P1 REAL[3]", "DECLARE theta REAL[1]", 'SET-FREQUENCY 0 "rf" __P1[0]', 'SET-PHASE 0 "rf" __P1[1]', 'SET-SCALE 0 "rf" __P1[2]', ).out(), )
def native_quil_to_executable( self, nq_program: Program) -> Optional[BinaryExecutableResponse]: if not self.qpu_compiler_client: raise UserMessageError( "It looks like you're trying to compile to an executable, but " "do not have access to the QPU compiler endpoint. Make sure you " "are engaged to the QPU before trying to do this.") self._connect_qpu_compiler() if nq_program.native_quil_metadata is None: warnings.warn( "It looks like you're trying to call `native_quil_to_binary` on a " "Program that hasn't been compiled via `quil_to_native_quil`. This is " "ok if you've hand-compiled your program to our native gateset, " "but be careful!") arithmetic_response = rewrite_arithmetic(nq_program) request = BinaryExecutableRequest(quil=arithmetic_response.quil, num_shots=nq_program.num_shots) response: BinaryExecutableResponse = cast( BinaryExecutableResponse, self.qpu_compiler_client.call("native_quil_to_binary", request), ) # hack! we're storing a little extra info in the executable binary that we don't want to # expose to anyone outside of our own private lives: not the user, not the Forest server, # not anyone. response.recalculation_table = arithmetic_response.recalculation_table # type: ignore response.memory_descriptors = _collect_memory_descriptors(nq_program) response.ro_sources = _collect_classical_memory_write_locations( nq_program) return response
def test_rewrite_arithmetic_simple_mref(): prog = Program("DECLARE theta REAL", "RZ(theta) 0") response = rewrite_arithmetic(prog) assert response == RewriteArithmeticResponse( original_memory_descriptors={ "theta": ParameterSpec(length=1, type="REAL") }, quil=Program("DECLARE theta REAL[1]", "RZ(theta[0]) 0").out(), recalculation_table={}, )
def test_rewrite_arithmetic_duplicate_exprs(): prog = Program( "DECLARE theta REAL", "RZ(theta*1.5) 0", "RX(theta*1.5) 0", # this is not a native gate, but it is a protoquil program ) response = rewrite_arithmetic(prog) assert response == RewriteArithmeticResponse( original_memory_descriptors={ "theta": ParameterSpec(length=1, type="REAL") }, recalculation_table={ ParameterAref(index=0, name="__P1"): "theta[0]*1.5/(2*pi)" }, quil=Program("DECLARE __P1 REAL[1]", "DECLARE theta REAL[1]", "RZ(__P1[0]) 0", "RX(__P1[0]) 0").out(), )
def native_quil_to_executable( self, nq_program: Program, *, debug: bool = False) -> Optional[QuiltBinaryExecutableResponse]: if not self.qpu_compiler_client: raise UserMessageError( "It looks like you're trying to compile to an executable, but " "do not have access to the QPU compiler endpoint. Make sure you " "are engaged to the QPU before trying to do this.") self._connect_qpu_compiler() arithmetic_response = rewrite_arithmetic(nq_program) request = QuiltBinaryExecutableRequest(quilt=arithmetic_response.quil, num_shots=nq_program.num_shots) response = cast( QuiltBinaryExecutableResponse, self.qpu_compiler_client.call("native_quilt_to_binary", request, rpc_timeout=self.timeout), ) response.recalculation_table = arithmetic_response.recalculation_table # type: ignore response.memory_descriptors = _collect_memory_descriptors(nq_program) # Convert strings to MemoryReference for downstream processing. response.ro_sources = [(parse_mref(mref), source) for mref, source in response.ro_sources] # TODO (kalzoo): this is a temporary workaround to migrate memory location parsing from # the client side (where it was pre-quilt) to the service side. In some cases, the service # won't return ro_sources, and so we can fall back to parsing the change on the client side. if response.ro_sources == []: response.ro_sources = _collect_classical_memory_write_locations( nq_program) if not debug: response.debug = {} return response
def native_quil_to_executable(self, nq_program: Program) -> QuantumExecutable: arithmetic_response = rewrite_arithmetic(nq_program) request = TranslateNativeQuilToEncryptedBinaryRequest( quil=arithmetic_response.quil, num_shots=nq_program.num_shots) with self._qcs_client() as qcs_client: # type: httpx.Client response = translate_native_quil_to_encrypted_binary( client=qcs_client, quantum_processor_id=self.quantum_processor_id, json_body=request, ).parsed ro_sources = cast( List[List[str]], [] if response.ro_sources == UNSET else response.ro_sources) def to_expression(rule: str) -> ExpressionDesignator: # We can only parse complete lines of Quil, so we wrap the arithmetic expression # in a valid Quil instruction to parse it. # TODO: This hack should be replaced after #687 return cast(ExpressionDesignator, cast(Gate, parse(f"RZ({rule}) 0")[0]).params[0]) return EncryptedProgram( program=response.program, memory_descriptors=_collect_memory_descriptors(nq_program), ro_sources={ parse_mref(mref): source for mref, source in ro_sources }, recalculation_table={ mref: to_expression(rule) for mref, rule in arithmetic_response.recalculation_table.items() }, _memory=nq_program._memory.copy(), )
def test_rewrite_arithmetic_set_scale(): prog = Program( "DECLARE theta REAL", 'SET-SCALE 0 "rf" 1.0', 'SET-SCALE 0 "rf" theta', ) response = rewrite_arithmetic(prog) assert response == RewriteArithmeticResponse( original_memory_descriptors={ "theta": ParameterSpec(length=1, type="REAL") }, recalculation_table={ ParameterAref(index=0, name="__P1"): "theta[0]/8" }, quil=Program( "DECLARE __P1 REAL[1]", "DECLARE theta REAL[1]", 'SET-SCALE 0 "rf" 1.0', 'SET-SCALE 0 "rf" __P1[0]', ).out(), )
def test_rewrite_arithmetic_mixed(): prog = Program( "DECLARE theta REAL", "DECLARE beta REAL", "RZ(3 * theta) 0", "RZ(beta+theta) 0", ) response = rewrite_arithmetic(prog) assert response.original_memory_descriptors == { "theta": ParameterSpec(length=1, type="REAL"), "beta": ParameterSpec(length=1, type="REAL"), } assert response.recalculation_table == { ParameterAref(index=0, name="__P2"): "3*theta[0]/(2*pi)", ParameterAref(index=1, name="__P2"): "(beta[0] + theta[0])/(2*pi)", } assert (response.quil == Program( "DECLARE __P2 REAL[2]", "DECLARE theta REAL[1]", "DECLARE beta REAL[1]", "RZ(__P2[0]) 0", "RZ(__P2[1]) 0", ).out())
def test_rewrite_arithmetic_no_params(): prog = Program("X 0") response = rewrite_arithmetic(prog) assert response == RewriteArithmeticResponse(quil=Program("X 0").out())