"""Guppy structs and definitions for state factories"""fromtypingimportGeneric,no_type_checkfromguppylangimportguppyfromguppylang.std.builtinsimportarray,exit,panicfromguppylang.std.collectionsimportQueuefromguppylang.std.langimportFunction,ownedfromguppylang.std.optionimportOption,nothing,somefromguppylang.std.quantumimportMeasurement,collect_measurements,qubitfrom._logical_blockimportLogicalBlock__all__=["PreBlock","StateFactory",]BLOCK_SIZE=guppy.nat_var("BLOCK_SIZE")N_FLAGS=guppy.nat_var("N_FLAGS")BATCH_SIZE=guppy.nat_var("BATCH_SIZE")@guppy@no_type_checkdef_array_any(arr:array[bool,BLOCK_SIZE])->bool:foriinrange(BLOCK_SIZE):# noqa: SIM110 # `all` is not yet supported by Guppyifarr[i]:returnTruereturnFalse
[docs]@guppy.struct@no_type_checkclassPreBlock(Generic[BLOCK_SIZE,N_FLAGS]):# type: ignore[misc]"""Logical block that went through state preparation, but may not have succeeded. The measurement outcomes specifying if it succeeded have not been read. Hence, the runtime is not blocked by measurements, allowing multiple state preparation to occur in parallel. Args: BLOCK_SIZE: Number of physical qubits in the logical block. N_FLAGS: Size of the results array Attributes: logical_block (LogicalBlock[N]): The candidate logical block flag_outcomes (array[bool, N_FLAGS]): Array of measurement outcomes. Succeeds if all are `False`. """logical_block:LogicalBlock[BLOCK_SIZE]# type: ignore[type-arg, valid-type]flag_outcomes:array[Measurement,N_FLAGS]# type: ignore[valid-type]
[docs]@guppy@no_type_checkdefforce_check(self:"PreBlock[BLOCK_SIZE, N_FLAGS]"@owned,)->Option[LogicalBlock[BLOCK_SIZE]]:"""If preparation was successful, return the block, otherwise return `nothing`. Calling this forces the measurement of the flags to take place (if they had not already). """failed=_array_any(collect_measurements(self.flag_outcomes))iffailed:self.logical_block.discard()returnnothing()else:returnsome(self.logical_block)
@guppy@no_type_checkdef_qalloc_dirty()->LogicalBlock[BLOCK_SIZE]:"""Allocate resources for a codeblock, but the qubits are not in a valid logical state."""returnLogicalBlock(array(qubit()for_inrange(BLOCK_SIZE)))
[docs]@guppy.structclassStateFactory(Generic[BLOCK_SIZE,N_FLAGS,BATCH_SIZE]):# type: ignore[misc]"""State factory, making preparation attempts in parallel. Args: BLOCK_SIZE: Number of physical qubits in the logical block. N_FLAGS: Number of flag outcomes each `PreBlock` tracks. BATCH_SIZE: Number of states produced in the same batch. Attributes: prep_routine: Function to prepare a state. max_attempts: Maximum number of repeat-until-success attempts. batch: The Queue of elements in the batch. Provide an empty queue with `guppylang.std.collections.queue.empty_queue`. """prep_routine:Function[[],PreBlock[BLOCK_SIZE,N_FLAGS]]# type: ignore[type-arg,valid-type]max_attempts:intbatch:Queue[PreBlock[BLOCK_SIZE,N_FLAGS],BATCH_SIZE]# type: ignore[type-arg,valid-type]
[docs]@guppy@no_type_checkdefget_state(self:"StateFactory[BLOCK_SIZE, N_FLAGS, BATCH_SIZE]",)->LogicalBlock[BLOCK_SIZE]:"""Parallel RUS preparation, up to `self.max_attempts` retries. All `BATCH_SIZE` state preparations may be run in parallel. If any of them succeeds, the state is returned. Surplus states are stored and can be fetched by subsequent calls to this function. """ifBATCH_SIZE<=0:panic("StateFactory: BATCH_SIZE must be greater than zero")for_inrange(self.max_attempts):# If empty, request a new batchiflen(self.batch)==0:for_inrange(BATCH_SIZE):self.batch.push(self.prep_routine())# Pop an element from batch and check if it successfully prepared a statepre_block_prep=self.batch.pop()state=pre_block_prep.force_check()# If successful, early exitifstate.is_some():returnstate.unwrap()else:state.unwrap_nothing()exit("StateFactory ran out of attempts!")return_qalloc_dirty()# Unreachable, but required by the Guppy checker