qat.experimental.dialect.results.ir package

The Results dialect is used to model the collection and manipulation of results from quantum programs.

It provides types and attributes to represent result records, arrays, and collections. The dialect also defines operations for creating and transforming those structures, including post-selection and record reshaping operations needed by existing lowering and runtime paths.

This dialect is written heavily to be compatible with the legacy QAT runtime, but is expected to rapidly evolve to support more general and flexible results collection and manipulation in the future.

The dialect is centered around collections of results:

  • ResultsArrayType allows us to represent a list-like collection of results, grouping them together in a single structure.

  • RecordType represents the entire results of a single shot with dictionary-like semantics, allowing each piece of data to be stored by a string key.

  • ResultsCollectionType allows us to represent a collection of results. Results are stored and referenced by two identifiers; an integer index (which e.g. represents the shot) and a string key (which e.g. represents the result name). Conceptually, we can think of this as a dictionary of arrays, or as list of dictionaries. The actual data type is not specified to be index-major or key-major. This allows us to support flexible results acquisition.

The types are not specific to any particular result data type, and allow us to store arbitrary types of data (e.g. signals, bits, integers, IQ values). Along with operations to assemble these data structures, the dialect also provides operations to manipulate records. For example, an operation to group entries in a record into one single entry, with the original entries placed within a ResultsArrayType, and an operation to reduce records to selected data.

The operations to create data structures and add them to the collections allows us to store results from each shot as we go. This suits the representation of programs at a high level, such as the gate-level or pulse-level. However, this is not always the way results are represented closer to the hardware. For example, a particular control system might not support pulse post-processing operations, and might just return raw IQ values. Then we would need to retroactively apply the post-processing chain once the whole ensemble of results are returned. To support this, the dialect also provides operations to apply a post-processing chain to an entire collection of results. The MapOp allows us to specify a chain of operations that act on a RecordType, and produce a new RecordType. The MapOp then maps this chain of operations to every record in a ResultsCollectionType. This allows us to transform from a representation that applies post-processing to results as they’re collected, to a representation that applies them retroactively to the entire collection of results.

Additionally, the dialect provides a post-selection operation which can be parameterised with predicates to filter out results from a collection that do not satisfy the given predicates.

Mapping to post-processing in the runtime

The runtime operates on legacy instructions. The instructions within this dialect allow us to map those post-processing instructions:

class CreateOp(result_type, values=(), size=None)

Bases: CreateOp, IRDLOperation, Operation, _IRNode, ABC, object

Creates a value of a results type from provided values and an optional size.

The semantics are determined by the result type:

  • RecordType: Creates a record from values. The values must match the field types encoded by the record schema in the result type. No size operand.

  • ResultsArrayType:
    • Only empty arrays are supported. values must be empty.

    • If the result size is DYNAMIC_INDEX, a size operand must be provided.

    • If the result size is static or unspecified, a size operand must not be provided.

  • ResultsCollectionType:
    • With values (all of type ResultsArrayType): creates a collection from arrays. Arrays must match the result schema field types and all have the same size as the collection result type. No size operand.

    • Without values: creates an empty collection. If the result size is DYNAMIC_INDEX, a size operand must be provided.

  • TupleType: Creates a tuple from values. No size operand.

Use the factory class methods for ergonomic construction.

Variables:
  • size – Optional dynamic size operand used for dynamic-size empty arrays and collections.

  • values – Variadic operands used to populate records, collections, and tuples.

  • result – The created value. Must be one of RecordType, ResultsArrayType, ResultsCollectionType, or TupleType.

Initialises CreateOp with a pre-built result type and operands.

Parameters:
  • result_type (TypeAttribute) – The result type to create. Use the factory class methods to build this automatically from the input values.

  • values (Sequence[SSAValue | Operation]) – Values to create from (fields for records, elements for arrays and tuples, or arrays for collections).

  • size (Union[Operation, SSAValue[IntegerType], None]) – Optional dynamic size operand for empty arrays and collections.

classmethod for_array(type_, size)

Create an empty ResultsArrayType.

Parameters:
  • type_ – The element type of the array.

  • size (Union[Operation, SSAValue[IntegerType], IntAttr, int]) – The size of the array. Can be a static integer, an IntAttr, or a dynamic SSA value.

Return type:

CreateOp

classmethod for_collection_from_arrays(keys, arrays)

Create a ResultsCollectionType from keyed arrays.

Parameters:
  • keys (Sequence[str | StringAttr]) – Keys for each array.

  • arrays (Sequence[Union[SSAValue[ResultsArrayType], Operation]]) – The arrays to create the collection from. Must be non-empty, of equal size, and all of type ResultsArrayType.

Return type:

CreateOp

classmethod for_empty_collection(schema, size)

Create an empty ResultsCollectionType.

Parameters:
  • schema (RecordSchemaAttr) – The schema of the collection.

  • size (Union[Operation, SSAValue[IntegerType], IntAttr, int]) – The size of the collection. Can be a static integer, an IntAttr, or a dynamic SSA value.

Return type:

CreateOp

classmethod for_record(keys, values)

Create a RecordType from keys and values.

The result schema is derived from the keys and value types.

Parameters:
  • keys (Sequence[str]) – Keys for the record fields.

  • values (Sequence[SSAValue | Operation]) – Values for the record fields.

Return type:

CreateOp

classmethod for_tuple(values)

Create a TupleType from values.

Parameters:

values (Sequence[SSAValue | Operation]) – Values to populate the tuple with.

Return type:

CreateOp

classmethod get_irdl_definition()

Get the IRDL operation definition.

irdl_options = (AttrSizedOperandSegments(as_property=False),)
name: ClassVar[str] = 'results.create'

The operation name. Should be a static member of the class

result

Access a non-variadic construct which appears before any variadic arguments.

size

Access an optional construct when there is a “segment size” attribute.

traits: ClassVar[OpTraits] = <xdsl.ir.core.OpTraits object>

Traits attached to an operation definition. This is a static field, and is made empty by default by PyRDL if not set by the operation definition.

values

Access a variadic construct when there is a “segment size” attribute.

verify_()

Verifies the CreateOp by dispatching the verification method based on the result of the operation.

class ExtractOp(container, result_type, key=None, index=None)

Bases: ExtractOp, IRDLOperation, Operation, _IRNode, ABC, object

Extracts a value from a results container.

The extraction semantics are determined by the container type and selectors:

  • A RecordType can be extracted from a ResultsCollectionType at a given index.

  • A ResultsArrayType can be extracted from a ResultsCollectionType at a given key.

  • A field value can be extracted from a ResultsCollectionType at a given key and index.

  • A field value can be extracted from a RecordType at a given key.

  • An element can be extracted from a ResultsArrayType at a given index.

Prefer the factory methods for common extraction shapes.

Variables:
  • container – The container operand to extract from.

  • index – Optional index selector used for array access and collection shot access.

  • key – Optional key selector used for record/collection field selection.

  • result – The extracted value whose type must match the selected extraction mode.

Initializes the ExtractOp with explicit selectors and result type.

Parameters:
  • container (SSAValue | Operation) – The container to extract from.

  • result_type (TypeAttribute) – Explicit result type for the extraction.

  • key (Union[str, StringAttr, None]) – Optional key selector.

  • index (Union[SSAValue, Operation, None]) – Optional index selector.

classmethod array_from_collection(collection, key)

Extract a full field array from a collection by key.

Return type:

ExtractOp

container

Access a non-variadic construct which appears before any variadic arguments.

classmethod get_irdl_definition()

Get the IRDL operation definition.

index

Access an optional construct when all variadic arguments have the same size. This occurs when the appropriate same-size option is set or there is a single variadic.

In this case either all variadics contain 1 element or no elements.

key

Accessor for an optional operation property.

name: ClassVar[str] = 'results.extract'

The operation name. Should be a static member of the class

classmethod record_from_collection(collection, index)

Extract a full record from a collection by index.

Return type:

ExtractOp

result

Access a non-variadic construct which appears before any variadic arguments.

traits: ClassVar[OpTraits] = <xdsl.ir.core.OpTraits object>

Traits attached to an operation definition. This is a static field, and is made empty by default by PyRDL if not set by the operation definition.

classmethod value_from_array(array, index)

Extract an element value from an array by index.

Return type:

ExtractOp

classmethod value_from_collection(collection, key, index)

Extract a field value from a collection by key and index.

Return type:

ExtractOp

classmethod value_from_record(record, key)

Extract a field value from a record by key.

Return type:

ExtractOp

verify_()

Verifies selectors and result type against the container semantics.

class GroupEntriesOp(record, keys, group_key)

Bases: GroupEntriesOp, IRDLOperation, Operation, _IRNode, ABC, object

Groups entries in a record into a single entry, producing a tuple of those entries, ordered by the given keys, with the tuple stored with a provided key in the record.

The operation creates a record type that replaces the provided keys with a single key, and its corresponding type is a tuple of the grouped field types.

This operation is roughly equivalent to the Assign instruction in legacy IR. This operation has a lowering path which could make use of ExtractOp to extract the values from the record, and then make a new record with the grouped entries. But currently, this operation is more useful to the current runtime.

Note

This operation exists to support legacy runtime implementations of post-processing. Going forward, it is highly encouraged to assemble records in the structure that is desired to promote proper dataflow semantics.

Warning

This operation is likely to be flagged for deprecation in the future.

Variables:
  • record – The input record to regroup.

  • keys – The set of keys to group into a tuple-valued entry.

  • group_key – The key name assigned to the new grouped tuple entry.

  • result – The transformed record type after grouping.

Initializes the GroupEntriesOp with the given record, keys, and group key.

Parameters:
  • record (Union[SSAValue[RecordType], Operation]) – The SSA value representing the existing results record.

  • keys (Sequence[str]) – A list of strings representing the keys to group in the new entry.

  • group_key (str) – A string representing the key for the new grouped entry.

classmethod get_irdl_definition()

Get the IRDL operation definition.

group_key: StringAttr

Accessor for an operation property.

keys: ArrayAttr[StringAttr]

Accessor for an operation property.

name: ClassVar[str] = 'results.group_entries'

The operation name. Should be a static member of the class

record

Access a non-variadic construct which appears before any variadic arguments.

result

Access a non-variadic construct which appears before any variadic arguments.

traits: ClassVar[OpTraits] = <xdsl.ir.core.OpTraits object>

Traits attached to an operation definition. This is a static field, and is made empty by default by PyRDL if not set by the operation definition.

verify_()
class IntegerStatePredicateAttr(key, disallowed_values)

Bases: IntegerStatePredicateAttr

Models a predicate for post selecting results based on an integer state.

This attribute is used to filter results based on a specific integer state value. It is described by a key and a list of disallowed integer values. The key refers to the entry in a record.

Variables:
  • key – The key of the entry in the record that post-selection is performed on.

  • disallowed_values – The list of values that are disallowed and will result in the record being post-selected out of the results collection.

Initializes the IntegerStatePredicateAttr with the given key and disallowed values.

Parameters:
  • key (str | StringAttr) – The key of the entry in the record that post-selection is performed on.

  • disallowed_values (Iterable[int | IntAttr] | ArrayAttr[IntAttr]) – The list of values that are disallowed and will result in the record being post-selected out of the results collection.

disallowed_values: ArrayAttr[IntAttr]
classmethod get_irdl_definition()

Get the IRDL attribute definition.

key: StringAttr
name: ClassVar[str] = 'results.integer_state_predicate'

The attribute name should be a static field in the attribute classes.

class MapOp(value, body, results_collection_type)

Bases: MapOp, IRDLOperation, Operation, _IRNode, ABC, object

Maps a transformation over a record to a collection of records, producing a new collection of records.

The operation contains a single region, which contains the operations that transform a record into a new record. The region has a block argument of type RecordType, which represents the input record, and must yield a value of type RecordType. The operation takes a ResultsCollectionType operand, which represents the collection of records to be transformed, and produces a new ResultsCollectionType result. The implication is that the transformation is applied to each record in the collection.

The operation is modelled as pure to not allow for any side effects to be introduced within the region, and is enforced to be isolated from above to ensure that the region does not have access to any values outside of the region, which could introduce side effects.

This is intended to allow for a granular post-processing chain to be implemented that acts locally to a record. It intentionally does not specify any details of how this is implemented, e.g., in parallel or sequentially, apply every operation to each record before moving onto the next or going operation-by-operation (on every record). Those details are left to the runtime implementation, or lowering if relevant.

Within this block, you might expect to see operations such as ExtractOp to extract values from the record, and then post-processing chains (such as those defined in the pulse dialect) to transform the values, and then a CreateOp create a new record. You might also expect to see operations such as GroupEntriesOp to group entries in the record, or a ReduceOp to filter out entries.

Variables:
  • value – The SSA value representing the existing results collection.

  • body – The region containing the operations that transform a record into a new record.

  • result – The resulting collection type, which is a ResultsCollectionType.

Initializes the MapOp with the given collection and body.

Parameters:
  • value (Union[SSAValue[ResultsCollectionType], Operation]) – The SSA value representing the existing results collection.

  • body (Block | Region | Sequence[Block]) – The region or block(s) containing the operations that transform a record into a new record.

body

Access a non-variadic construct which appears before any variadic arguments.

classmethod get_irdl_definition()

Get the IRDL operation definition.

name: ClassVar[str] = 'results.map'

The operation name. Should be a static member of the class

result

Access a non-variadic construct which appears before any variadic arguments.

traits: ClassVar[OpTraits] = <xdsl.ir.core.OpTraits object>

Traits attached to an operation definition. This is a static field, and is made empty by default by PyRDL if not set by the operation definition.

value

Access a non-variadic construct which appears before any variadic arguments.

verify_()

Verifies that the region begins with a block that has a single argument of type RecordType, and that the region yields a value of type RecordType.

It then validates that the yielded results type matches the expected results type of the MapOp.

class PostSelectOp(collection, *predicates)

Bases: PostSelectOp, IRDLOperation, Operation, _IRNode, ABC, object

Filters a results collection based on a given predicate, producing a new collection that only includes records satisfying the predicate.

The operation takes a results collection operand and a predicates property, and produces a new results collection that includes only the records that satisfy all configured predicates.

This is used to filter a results collection to records that satisfy the predicate. This is modelled around legacy runtime implementations of post-selection, which post-selects on an entire collection of records, and filters them down. Lowering paths could be implemented to allow for an on-the-fly implementation of post-selection making use of classical control flow, given hardware compatibility.

Variables:
  • collection – The operand representing the existing results collection.

  • predicates – The predicate attributes used to filter records in the collection.

  • result – The resulting collection type, which is a ResultsCollectionType.

Initializes the PostSelectOp with the given collection and predicates.

Parameters:
collection

Access a non-variadic construct which appears before any variadic arguments.

classmethod get_irdl_definition()

Get the IRDL operation definition.

name: ClassVar[str] = 'results.post_select'

The operation name. Should be a static member of the class

predicates: ArrayAttr[PostSelectPredicateAttr]

Accessor for an operation property.

result

Access a non-variadic construct which appears before any variadic arguments.

traits: ClassVar[OpTraits] = <xdsl.ir.core.OpTraits object>

Traits attached to an operation definition. This is a static field, and is made empty by default by PyRDL if not set by the operation definition.

verify_()
class PostSelectPredicateAttr(*parameters)

Bases: ParametrizedAttribute, ABC

Models a predicate for post selecting results.

Eventually, this class could be extended to provide a lowering hook.

name: ClassVar[str] = 'results.post_select_predicate'

The attribute name should be a static field in the attribute classes.

class RecordFieldAttr(key, type_)

Bases: RecordFieldAttr

Models a field in a record in the results dialect, to be used by records and collections of records.

Contains a key and a type.

Variables:
  • key – The key of the entry in the record.

  • type – The type of the entry in the record.

Initializes the RecordFieldAttr with the given key and type.

Parameters:
  • key (str | StringAttr) – The key of the entry in the record.

  • type_ – The type of the entry in the record.

classmethod get_irdl_definition()

Get the IRDL attribute definition.

key: StringAttr
name: ClassVar[str] = 'results.record_field'

The attribute name should be a static field in the attribute classes.

type: Attribute = <xdsl.irdl.attributes._ParameterDef object>
class RecordSchemaAttr(fields)

Bases: RecordSchemaAttr

Models the schema of a record in the results dialect, to be used by records and collections of records.

Variables:

fields – An array of record field attributes, which individually define the key and type of each field in the record.

Initializes the RecordSchemaAttr with the given fields.

Parameters:

fields (ArrayAttr[RecordFieldAttr] | Sequence[RecordFieldAttr]) – An array of record field attributes, which individually define the key and type of each field in the record.

as_dict()

Return the schema as a dictionary mapping keys to types.

Return type:

dict[str, TypeAttribute]

fields: ArrayAttr[RecordFieldAttr]
classmethod get_irdl_definition()

Get the IRDL attribute definition.

name: ClassVar[str] = 'results.record_schema'

The attribute name should be a static field in the attribute classes.

verify()

Verify that there are no duplicate keys in the schema.

class RecordType(schema)

Bases: RecordType

A type that carries a results record that can be added to a results collection.

This is used to represent a single record of results, e.g., from a single shot. It has the semantics of a dictionary, where the entries are accessed by string keys, and it can store arbitrary data types.

RecordType is modelled to be immutable, and must be complete when constructed.

Variables:

schema – The schema of the record, which defines the keys and types of the entries in the record.

Initializes a record type with a schema.

classmethod get_irdl_definition()

Get the IRDL attribute definition.

name: ClassVar[str] = 'results.record'

The attribute name should be a static field in the attribute classes.

schema: RecordSchemaAttr
class ReduceOp(record, keys)

Bases: ReduceOp, IRDLOperation, Operation, _IRNode, ABC, object

Reduces a record, down to a subset of the entries in the record, producing a new record.

We often want to gather a number of measurements to use in post-processing for use cases such as post-selection, and more general error mitigation methods. But not each of these measurements are the measurements that are requested in the original circuit. After the post-processing has completed and we have no need for these measurements, we can reduce records down to only the entries we would like to return.

This is a high-level operation that is roughly equivalent to the Return instruction in legacy IR. This has a lowering path to extract the entries from the record, and then create a new record with only the entries that are requested. But currently, this operation is more useful to the current runtime.

Note

This operation exists to support legacy runtime implementations of post-processing. Going forward, it is highly encouraged to assemble records in the structure that is desired to promote proper dataflow semantics.

Warning

This operation is likely to be flagged for deprecation in the future.

Variables:
  • record – The input record to reduce.

  • keys – The keys to retain in the reduced record.

  • result – The reduced record type containing only retained keys.

Initializes the ReduceOp with the given record and keys.

Parameters:
  • record (Union[SSAValue[RecordType], Operation]) – The SSA value representing the existing results record.

  • keys (Sequence[str]) – A list of strings representing the keys to retain in the reduced record.

classmethod get_irdl_definition()

Get the IRDL operation definition.

keys: ArrayAttr[StringAttr]

Accessor for an operation property.

name: ClassVar[str] = 'results.reduce'

The operation name. Should be a static member of the class

record

Access a non-variadic construct which appears before any variadic arguments.

result

Access a non-variadic construct which appears before any variadic arguments.

traits: ClassVar[OpTraits] = <xdsl.ir.core.OpTraits object>

Traits attached to an operation definition. This is a static field, and is made empty by default by PyRDL if not set by the operation definition.

verify_()
class ResultsArrayType(type_, size)

Bases: ResultsArrayType

A type that represents an array of results, which can be added to and filtered with given operations.

This is used to represent a collection of results that can be indexed into, e.g., a register of classical bits measured from a quantum circuit. It takes standard array semantics, holding an ordered list of results.

ResultsArrayType is modelled to be immutable, and must be complete when constructed.

Variables:
  • type – The type of the entries in the array.

  • size – The size of the array. The special value DYNAMIC_INDEX indicates runtime-dynamic size.

Initializes an array type with element type and size.

classmethod dynamic_size(type_)

Construct a ResultsArrayType with a dynamic size.

Return type:

ResultsArrayType

classmethod get_irdl_definition()

Get the IRDL attribute definition.

name: ClassVar[str] = 'results.array'

The attribute name should be a static field in the attribute classes.

size: IntAttr
type: TypeAttribute = <xdsl.irdl.attributes._ParameterDef object>
class ResultsCollectionType(schema, size)

Bases: ResultsCollectionType

A type that represents a collection of results, which can be added to and filtered with given operations.

The collection is modelled as a collection of data which are referenced by two identifiers:

  • An integer index, which for example, might refer to the shot number.

  • A string key, which for example, might refer to the name of the result.

The actual data type is not specified to be index-major or key-major. This allows us to support flexible results acquisition. For example, you could treat this as a dictionary of arrays, and append a result to each array for each shot. Or you could treat this as a list of records, and append a new record for each shot.

Variables:
  • schema – The schema of the records in the collection, which defines the keys and types.

  • size – The size of the collection. The special value DYNAMIC_INDEX indicates runtime-dynamic size.

Initializes a collection type with schema and size.

classmethod dynamic_size(schema)

Construct a ResultsCollectionType with a dynamic size.

Return type:

ResultsCollectionType

classmethod get_irdl_definition()

Get the IRDL attribute definition.

name: ClassVar[str] = 'results.collection'

The attribute name should be a static field in the attribute classes.

schema: RecordSchemaAttr
size: IntAttr
class StoreOp(container, index, value, key=None)

Bases: StoreOp, IRDLOperation, Operation, _IRNode, ABC, object

Stores a value into a results container at a given index.

The semantics are determined by the container and value types:

  • container is ResultsCollectionType, value is RecordType: stores the record at index. The record schema must match the collection schema. No key property.

  • container is ResultsCollectionType, value is any other type: stores the value at key and index. A key property must be provided and must exist in the collection schema with the correct type.

  • container is ResultsArrayType: stores the value at index. The value type must match the array element type. No key property.

Variables:
  • container – The container operand to write into.

  • index – The integer-or-index operand identifying the element/shot to overwrite.

  • value – The value operand to write.

  • key – Optional key selector used only for keyed collection value stores.

  • result – The updated container value. Must match container type.

Initialises the StoreOp.

Parameters:
  • container (SSAValue | Operation) – The container to store into (collection or array).

  • index (SSAValue | Operation) – The index at which to store the value.

  • value (SSAValue | Operation) – The value to store.

  • key (Union[str, StringAttr, None]) – Optional key for storing a value by key in a collection.

container

Access a non-variadic construct which appears before any variadic arguments.

classmethod get_irdl_definition()

Get the IRDL operation definition.

index

Access a non-variadic construct which appears before any variadic arguments.

key

Accessor for an optional operation property.

name: ClassVar[str] = 'results.store'

The operation name. Should be a static member of the class

classmethod record_in_collection(collection, index, record)

Store a full record into a collection at index.

Parameters:
  • collection (Union[SSAValue[ResultsCollectionType], Operation]) – The collection to store into.

  • index (SSAValue | Operation) – The shot index to write.

  • record (Union[SSAValue[RecordType], Operation]) – The record to store.

Return type:

StoreOp

result

Access a non-variadic construct which appears before any variadic arguments.

traits: ClassVar[OpTraits] = <xdsl.ir.core.OpTraits object>

Traits attached to an operation definition. This is a static field, and is made empty by default by PyRDL if not set by the operation definition.

value

Access a non-variadic construct which appears before any variadic arguments.

classmethod value_in_array(array, index, value)

Store a value into an array at index.

Parameters:
  • array (Union[SSAValue[ResultsArrayType], Operation]) – The results array to store into.

  • index (SSAValue | Operation) – The array index to write.

  • value (SSAValue | Operation) – The value to store.

Return type:

StoreOp

classmethod value_in_collection(collection, index, key, value)

Store a field value into a collection at key and index.

Parameters:
  • collection (Union[SSAValue[ResultsCollectionType], Operation]) – The collection to store into.

  • index (SSAValue | Operation) – The shot index to write.

  • key (str | StringAttr) – The field key in the collection schema.

  • value (SSAValue | Operation) – The value to store for key.

Return type:

StoreOp

verify_()

Verifies the StoreOp based on the container type.

class YieldOp(record)

Bases: YieldOp, IRDLOperation, Operation, _IRNode, ABC, object

Yields a record from a region, which can be used to produce a new collection of records.

This operation is used to yield a record from a region, which can be used to produce a new collection of records. The yielded record must be of type RecordType.

Variables:

record – The SSA value representing the record to be yielded.

Initializes the YieldOp with the given record.

Parameters:

record (Union[SSAValue[RecordType], Operation]) – The SSA value representing the record to be yielded.

classmethod get_irdl_definition()

Get the IRDL operation definition.

name: ClassVar[str] = 'results.yield'

The operation name. Should be a static member of the class

record

Access a non-variadic construct which appears before any variadic arguments.

traits: ClassVar[OpTraits] = <xdsl.ir.core.OpTraits object>

Traits attached to an operation definition. This is a static field, and is made empty by default by PyRDL if not set by the operation definition.

verify_()

Submodules