diff --git a/docs/examples/qpe.yaml b/docs/examples/qpe.yaml
new file mode 100644
index 0000000..411f8c9
--- /dev/null
+++ b/docs/examples/qpe.yaml
@@ -0,0 +1,84 @@
+description: Program representing QPE using geometric sequence
+input:
+ program:
+ children:
+ - name: Hadamards
+ ports:
+ - direction: through
+ name: register
+ size: N
+ - name: Evolution
+ ports:
+ - direction: input
+ name: result_in
+ size: bits_of_precision
+ - direction: output
+ name: result_out
+ size: N
+ - direction: input
+ name: psi_in
+ size: None
+ - direction: output
+ name: psi_out
+ size: None
+ children:
+ - name: U
+ ports:
+ - direction: through
+ name: result
+ size: bits_of_precision
+ - direction: through
+ name: psi
+ size: N
+ resources:
+ - name: T_gates
+ type: additive
+ value: N**2
+ connections:
+ - source: result_in
+ target: U.result
+ - source: U.result
+ target: result_out
+ - source: psi_in
+ target: U.psi
+ - source: U.psi
+ target: psi_out
+ repetition:
+ count: bits_of_precision
+ sequence:
+ type: geometric
+ ratio: 1
+ - name: Inverse_QFT
+ ports:
+ - direction: through
+ name: register
+ size: N
+ connections:
+ - source: result_in
+ target: Hadamards.register
+ - source: Hadamards.register
+ target: Evolution.result_in
+ - source: Evolution.result_out
+ target: Inverse_QFT.register
+ - source: Inverse_QFT.register
+ target: result_out
+ - source: psi_in
+ target: Evolution.psi_in
+ - source: Evolution.psi_out
+ target: psi_out
+ name: QPE
+ ports:
+ - direction: input
+ name: result_in
+ size: ceil(log2(1/eps))
+ - direction: output
+ name: result_out
+ size: ceil(log2(1/eps))
+ - direction: input
+ name: psi_in
+ size: M
+ - direction: output
+ name: psi_out
+ size: M
+
+ version: v1
diff --git a/docs/format.md b/docs/format.md
index c4cd521..729edca 100644
--- a/docs/format.md
+++ b/docs/format.md
@@ -153,3 +153,45 @@ beginning looks as follows:
```yaml
--8<-- "basic_program_concise.yaml"
```
+
+
+### Repetitions
+
+On top of the basic fileds listed above, one can also write a QREF routine which contains repetitions.
+
+This can be added with `repetition` field:
+
+```yaml
+repetition:
+ count: ceil(1/eps)
+ sequence:
+ type: constant
+ multiplier: 1
+```
+
+`repetition` consists of two parts:
+
+- `count` – defines how many times the child of the this routine should be repeated.
+- `sequence` – defines how the costs for the repetition will be aggregated. Each `sequence` has a field `type` which defines the type of the sequence. Depending on the type there are extra fields, summarized in the table below.
+
+
+There are 5 different sequences that one can currently use in QREF:
+
+
+|
Sequence type
| Additional fields
| Description | Example |
+|-------|-------|-------|-------|
+| `constant`| `multiplier` | In each iteration child is repeated `multiplier` number of times. | Trotterization |
+| `arithmetic`| `difference`, `initial_term` | Iteration starts from `initial_term` repetitions of a child and then we increase the of repetitions by `difference` in every iteration. | QFT |
+| `geometric` | `ratio` | In each iteration number of repetitions is multiplied by `ratio`, starts for 1 repetition in the first iteartion. | QPE |
+| `closed_form` | `sum`, `prod`, `num_terms_symbol` | This can be used for cases, where we know the closed-form expression for the total cost of the routine given the number of repetitions is defined `num_terms_symbol`. `sum` is an expression for additive resources and `prod` is for multiplicative. | Any |
+| `custom` | `term_expression`, `iterator_symbol` | This can be used in case where we don't know the formula for closed form, but we do know the formula for each term, which is defined using `term_expression`, and we use `iterator_symbol` to denote the iterator. | Any |
+
+
+This representation abstracts out certain implementation details. Consider implementation of QPE using geometric sequence below. The child `U` of routine `Evolution` has two ports: `result` and `psi`, the with sizes `bits_of_precision` and `N`. Even though in the executable implementation each next controlled `U^2^i` only acts on one control qubit from the `result` register, there's currently no way of expressing it in QREF.
+
+=== "YAML"
+
+ ```yaml
+ --8<-- "qpe.yaml"
+ ```
+
diff --git a/src/qref/schema_v1.py b/src/qref/schema_v1.py
index b096f29..ee3d5b8 100644
--- a/src/qref/schema_v1.py
+++ b/src/qref/schema_v1.py
@@ -167,27 +167,90 @@ class ParamLinkV1(BaseModel):
model_config = ConfigDict(title="ParamLink")
+class ConstantSequenceV1(BaseModel):
+ """Description of a constant sequence in a V1 Schema.
+
+ In a constant sequence we repeat an element `multiplier` times in each iteration.
+ """
+
+ type: Literal["constant"]
+ multiplier: _Value = 1
+
+
+class ArithmeticSequenceV1(BaseModel):
+ """Description of an arithmetic sequence in a V1 Schema.
+
+ In an arithmetic sequence we start from `initial_term` repetitions of an element,
+ and in each iteration we increase it by `difference`.
+ """
+
+ type: Literal["arithmetic"]
+ initial_term: _Value = 0
+ difference: _Value
+
+
+class GeometricSequenceV1(BaseModel):
+ """Description of a geometric sequence in a V1 Schema.
+
+ In a geometric sequence we start from 1 repetition of an element,
+ and in each iteration we multiply it by `ratio`.
+ """
+
+ type: Literal["geometric"]
+ ratio: _Value
+
+
+class ClosedFormSequenceV1(BaseModel):
+ """Description of a sequence with known closed-form for a sum or product in a V1 Schema.
+
+ If `sum`/`prod` are specified, they can be used to calculate these values for a given sequence.
+ Expressions for `sum`/`prod` should use `num_terms_symbol` to represent the total number of terms.
+ """
+
+ type: Literal["closed_form"]
+ sum: _Value | None = None
+ prod: _Value | None = None
+ num_terms_symbol: str
+
+
+class CustomSequenceV1(BaseModel):
+ """Description of a custom sequence in a V1 Schema.
+
+ For sequences which do not fall into categories defined in other classes, one can use a custom representation.
+ It is an explicit representation of a sequence where `term_expression` defines the expression for each term
+ in the sequence and `iterator_symbol` is used to represent number of the iteration.
+ """
+
+ type: Literal["custom"]
+ term_expression: str
+ iterator_symbol: str = "i"
+
+
+class RepetitionV1(BaseModel):
+ """Description of a repetition of a routine in V1 schema."""
+
+ count: int | str
+ sequence: ConstantSequenceV1 | ArithmeticSequenceV1 | GeometricSequenceV1 | ClosedFormSequenceV1 | CustomSequenceV1
+
+
class RoutineV1(BaseModel):
"""Description of Routine in V1 schema.
Note:
- This is NOT a top-level object in the schema. Instead, RoutineV1 is wrapped in
- SchemaV1.
+ This is NOT a top-level object in the schema. Instead, RoutineV1 is wrapped in SchemaV1.
"""
name: _Name
- children: Annotated[NamedList[RoutineV1], _name_sorter] = Field(default_factory=list)
+ children: Annotated[NamedList[RoutineV1], _name_sorter] = Field(default_factory=NamedList)
type: str | None = None
- ports: Annotated[NamedList[PortV1], _name_sorter] = Field(default_factory=list)
- resources: Annotated[NamedList[ResourceV1], _name_sorter] = Field(default_factory=list)
- connections: Annotated[list[Annotated[ConnectionV1, _connection_parser]], _source_sorter] = Field(
- default_factory=list
- )
- input_params: list[_OptionallyMultiNamespacedName] = Field(default_factory=list)
- local_variables: dict[str, str] = Field(default_factory=dict)
- linked_params: Annotated[list[ParamLinkV1], _source_sorter] = Field(default_factory=list)
- meta: dict[str, Any] = Field(default_factory=dict)
-
+ ports: Annotated[NamedList[PortV1], _name_sorter] = Field(default_factory=NamedList)
+ resources: Annotated[NamedList[ResourceV1], _name_sorter] = Field(default_factory=NamedList)
+ connections: Annotated[list[Annotated[ConnectionV1, _connection_parser]], _source_sorter] = []
+ input_params: list[_OptionallyMultiNamespacedName] = []
+ local_variables: dict[str, str] = {}
+ linked_params: Annotated[list[ParamLinkV1], _source_sorter] = []
+ repetition: RepetitionV1 | None = None
+ meta: dict[str, Any] = {}
model_config = ConfigDict(title="Routine", validate_assignment=True)
def __init__(self, **data: Any):
diff --git a/src/qref/verification.py b/src/qref/verification.py
index 4222936..b2cdcf8 100644
--- a/src/qref/verification.py
+++ b/src/qref/verification.py
@@ -120,8 +120,6 @@ def _dfs_iteration(adjacency_list: AdjacencyList, start_node: str, ancestor_path
visited: list[str] = []
predecessors: dict[str, str] = {}
- _prefix = _make_prefixer(ancestor_path)
-
while to_visit:
node = to_visit.pop()
visited.append(node)
@@ -131,7 +129,7 @@ def _dfs_iteration(adjacency_list: AdjacencyList, start_node: str, ancestor_path
# Reconstruct the cycle
cycle = [neighbour]
while len(cycle) < 2 or cycle[-1] != start_node:
- cycle.append(_prefix(predecessors[cycle[-1]]))
+ cycle.append(predecessors[cycle[-1]])
return [f"Cycle detected: {cycle[::-1]}"]
if neighbour not in visited:
to_visit.append(neighbour)
diff --git a/tests/conftest.py b/tests/conftest.py
index f1ef0e0..33f315b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -26,7 +26,7 @@
def _load_valid_examples():
- for path in sorted(VALID_PROGRAMS_ROOT_PATH.iterdir()):
+ for path in sorted(VALID_PROGRAMS_ROOT_PATH.rglob("*.yaml")):
with open(path) as f:
data = yaml.safe_load(f)
yield pytest.param(data["input"], id=data["description"])
diff --git a/tests/qref/data/valid_programs/programs_with_repetitions/repetition_0_constant.yaml b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_0_constant.yaml
new file mode 100644
index 0000000..a79786a
--- /dev/null
+++ b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_0_constant.yaml
@@ -0,0 +1,74 @@
+description: Program with one subroutine repeated multiple times
+input:
+ program:
+ children:
+ - name: A
+ ports:
+ - direction: through
+ name: port_A
+ size: N
+ - name: B
+ input_params:
+ - eps
+ linked_params:
+ - source: eps
+ targets:
+ - core_subroutine.eps
+ ports:
+ - direction: input
+ name: port_B_in
+ size: N
+ - direction: output
+ name: port_B_out
+ size: N
+ children:
+ - name: core_subroutine
+ input_params:
+ - eps
+ ports:
+ - direction: through
+ name: my_port
+ size: N
+ resources:
+ - name: cost
+ type: additive
+ value: ceil(1/eps)/2
+ connections:
+ - source: port_B_in
+ target: core_subroutine.my_port
+ - source: core_subroutine.my_port
+ target: port_B_out
+ repetition:
+ count: ceil(1/eps)
+ sequence:
+ type: constant
+ multiplier: 1
+ - name: C
+ ports:
+ - direction: through
+ name: port_C
+ size: N
+ connections:
+ - source: in_0
+ target: A.port_A
+ - source: A.port_A
+ target: B.port_B_in
+ - source: B.port_B_out
+ target: C.port_C
+ - source: C.port_C
+ target: out_0
+ input_params:
+ - N
+ linked_params:
+ - source: N
+ targets:
+ - B.eps
+ name: root
+ ports:
+ - direction: input
+ name: in_0
+ size: N
+ - direction: output
+ name: out_0
+ size: N
+ version: v1
diff --git a/tests/qref/data/valid_programs/programs_with_repetitions/repetition_1_arithmetic.yaml b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_1_arithmetic.yaml
new file mode 100644
index 0000000..656031f
--- /dev/null
+++ b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_1_arithmetic.yaml
@@ -0,0 +1,46 @@
+description: Program representing QFT using arithmetic sequence.
+input:
+ program:
+ children:
+ - name: QFT_iterate
+ ports:
+ - direction: input
+ name: in_0
+ size: N
+ - direction: output
+ name: out_0
+ size: N
+ children:
+ - name: core_subroutine
+ ports:
+ - direction: through
+ name: my_port
+ size: N
+ resources:
+ - name: rotations
+ type: additive
+ value: N
+ connections:
+ - source: in_0
+ target: core_subroutine.my_port
+ - source: core_subroutine.my_port
+ target: out_0
+ repetition:
+ count: N
+ sequence:
+ type: arithmetic
+ difference: 1
+ connections:
+ - source: in_0
+ target: QFT_iterate.in_0
+ - source: QFT_iterate.out_0
+ target: out_0
+ name: QFT
+ ports:
+ - direction: input
+ name: in_0
+ size: N
+ - direction: output
+ name: out_0
+ size: N
+ version: v1
diff --git a/tests/qref/data/valid_programs/programs_with_repetitions/repetition_2_geometric.yaml b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_2_geometric.yaml
new file mode 100644
index 0000000..411f8c9
--- /dev/null
+++ b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_2_geometric.yaml
@@ -0,0 +1,84 @@
+description: Program representing QPE using geometric sequence
+input:
+ program:
+ children:
+ - name: Hadamards
+ ports:
+ - direction: through
+ name: register
+ size: N
+ - name: Evolution
+ ports:
+ - direction: input
+ name: result_in
+ size: bits_of_precision
+ - direction: output
+ name: result_out
+ size: N
+ - direction: input
+ name: psi_in
+ size: None
+ - direction: output
+ name: psi_out
+ size: None
+ children:
+ - name: U
+ ports:
+ - direction: through
+ name: result
+ size: bits_of_precision
+ - direction: through
+ name: psi
+ size: N
+ resources:
+ - name: T_gates
+ type: additive
+ value: N**2
+ connections:
+ - source: result_in
+ target: U.result
+ - source: U.result
+ target: result_out
+ - source: psi_in
+ target: U.psi
+ - source: U.psi
+ target: psi_out
+ repetition:
+ count: bits_of_precision
+ sequence:
+ type: geometric
+ ratio: 1
+ - name: Inverse_QFT
+ ports:
+ - direction: through
+ name: register
+ size: N
+ connections:
+ - source: result_in
+ target: Hadamards.register
+ - source: Hadamards.register
+ target: Evolution.result_in
+ - source: Evolution.result_out
+ target: Inverse_QFT.register
+ - source: Inverse_QFT.register
+ target: result_out
+ - source: psi_in
+ target: Evolution.psi_in
+ - source: Evolution.psi_out
+ target: psi_out
+ name: QPE
+ ports:
+ - direction: input
+ name: result_in
+ size: ceil(log2(1/eps))
+ - direction: output
+ name: result_out
+ size: ceil(log2(1/eps))
+ - direction: input
+ name: psi_in
+ size: M
+ - direction: output
+ name: psi_out
+ size: M
+
+ version: v1
diff --git a/tests/qref/data/valid_programs/programs_with_repetitions/repetition_3_closed_formyaml b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_3_closed_formyaml
new file mode 100644
index 0000000..7234d09
--- /dev/null
+++ b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_3_closed_formyaml
@@ -0,0 +1,85 @@
+description: Program representing QPE using closed form sequence.
+input:
+ program:
+ children:
+ - name: Hadamards
+ ports:
+ - direction: through
+ name: register
+ size: N
+ - name: Evolution
+ ports:
+ - direction: input
+ name: result_in
+ size: N
+ - direction: output
+ name: result_out
+ size: N
+ - direction: input
+ name: psi_in
+ size: M
+ - direction: output
+ name: psi_out
+ size: M
+ children:
+ - name: U
+ ports:
+ - direction: through
+ name: result
+ size: N
+ - direction: through
+ name: psi
+ size: N
+ resources:
+ - name: T_gates
+ type: additive
+ value: N**2
+ connections:
+ - source: result_in
+ target: U.result
+ - source: U.result
+ target: result_out
+ - source: psi_in
+ target: U.psi
+ - source: U.psi
+ target: psi_out
+ repetition:
+ count: N
+ sequence:
+ type: closed_form
+ sum: "(2 ** (X + 1) - 1)"
+ num_terms_symbol: X
+ - name: Inverse_QFT
+ ports:
+ - direction: through
+ name: register
+ size: N
+ connections:
+ - source: result_in
+ target: Hadamards.register
+ - source: Hadamards.register
+ target: Evolution.result_in
+ - source: Evolution.result_out
+ target: Inverse_QFT.register
+ - source: Inverse_QFT.register
+ target: result_out
+ - source: psi_in
+ target: Evolution.psi_in
+ - source: Evolution.psi_out
+ target: psi_out
+ name: QPE
+ ports:
+ - direction: input
+ name: result_in
+ size: ceil(log2(1/eps))
+ - direction: output
+ name: result_out
+ size: ceil(log2(1/eps))
+ - direction: input
+ name: psi_in
+ size: M
+ - direction: output
+ name: psi_out
+ size: M
+
+ version: v1
diff --git a/tests/qref/data/valid_programs/programs_with_repetitions/repetition_4_custom.yaml b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_4_custom.yaml
new file mode 100644
index 0000000..3fbbf50
--- /dev/null
+++ b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_4_custom.yaml
@@ -0,0 +1,85 @@
+description: Program representing QPE using custom sequence
+input:
+ program:
+ children:
+ - name: Hadamards
+ ports:
+ - direction: through
+ name: register
+ size: N
+ - name: Evolution
+ ports:
+ - direction: input
+ name: result_in
+ size: N
+ - direction: output
+ name: result_out
+ size: N
+ - direction: input
+ name: psi_in
+ size: M
+ - direction: output
+ name: psi_out
+ size: M
+ children:
+ - name: U
+ ports:
+ - direction: through
+ name: result
+ size: N
+ - direction: through
+ name: psi
+ size: N
+ resources:
+ - name: T_gates
+ type: additive
+ value: N**2
+ connections:
+ - source: result_in
+ target: U.result
+ - source: U.result
+ target: result_out
+ - source: psi_in
+ target: U.psi
+ - source: U.psi
+ target: psi_out
+ repetition:
+ count: N
+ sequence:
+ type: custom
+ term_expression: "2^i"
+ iterator_symbol: i
+ - name: Inverse_QFT
+ ports:
+ - direction: through
+ name: register
+ size: N
+ connections:
+ - source: result_in
+ target: Hadamards.register
+ - source: Hadamards.register
+ target: Evolution.result_in
+ - source: Evolution.result_out
+ target: Inverse_QFT.register
+ - source: Inverse_QFT.register
+ target: result_out
+ - source: psi_in
+ target: Evolution.psi_in
+ - source: Evolution.psi_out
+ target: psi_out
+ name: QPE
+ ports:
+ - direction: input
+ name: result_in
+ size: ceil(log2(1/eps))
+ - direction: output
+ name: result_out
+ size: ceil(log2(1/eps))
+ - direction: input
+ name: psi_in
+ size: M
+ - direction: output
+ name: psi_out
+ size: M
+
+ version: v1
diff --git a/tests/qref/data/valid_programs/programs_with_repetitions/repetition_5_nested.yaml b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_5_nested.yaml
new file mode 100644
index 0000000..65739f1
--- /dev/null
+++ b/tests/qref/data/valid_programs/programs_with_repetitions/repetition_5_nested.yaml
@@ -0,0 +1,59 @@
+description: Program representing a routine with nested repetitions
+input:
+ program:
+ children:
+ - name: a
+ children:
+ - name: b
+ ports:
+ - direction: through
+ name: thru_0
+ size: N
+ resources:
+ - name: y
+ type: additive
+ value: N
+ - name: z
+ type: multiplicative
+ value: N
+ type: null
+ ports:
+ - direction: input
+ name: in_0
+ size: N
+ - direction: output
+ name: out_0
+ size: null
+ connections:
+ - source: b.thru_0
+ target: out_0
+ - source: in_0
+ target: b.thru_0
+ repetition:
+ count: N
+ sequence:
+ type: arithmetic
+ difference: 2
+ initial_term: 1
+ type: null
+ connections:
+ - source: a.out_0
+ target: out_0
+ - source: in_0
+ target: a.in_0
+ name: root
+ ports:
+ - direction: input
+ name: in_0
+ size: N
+ - direction: output
+ name: out_0
+ size: null
+ repetition:
+ count: 10
+ sequence:
+ type: constant
+ multiplier: 1
+ type: null
+ version: v1
+
\ No newline at end of file