Skip to content

Commit

Permalink
docs: Add Example for Using Rendering Module in Documentation (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
sitong1011 authored Jun 21, 2024
1 parent 0983b74 commit 966f67a
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ dmypy.json
# Cython debug symbols
cython_debug/

## ignore .DS Store file.
**/.DS_Store
*.DS Store


# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
Expand Down
205 changes: 205 additions & 0 deletions docs/examples/alias_sampling.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
program:
children:
- input_params:
- L
local_variables:
- R=ceiling(log_2(L))
name: usp
ports:
- direction: input
name: in
size: R
- direction: output
name: out
size: R
resources:
- name: T_gates
type: additive
value: 8*L/multiplicity(2,L)
- name: rotations
type: additive
value: '2'
type: null
- name: had
ports:
- direction: input
name: in
size: N
- direction: output
name: out
size: N
type: null
- input_params:
- L
- mu
local_variables:
- R=ceiling(log_2(L))
name: qrom
ports:
- direction: input
name: In_l
size: R
- direction: input
name: In_alt
size: R
- direction: input
name: In_keep
size: mu
- direction: output
name: out_l
size: R
- direction: output
name: out_alt
size: R
- direction: output
name: out_keep
size: mu
resources:
- name: T_gates
type: additive
value: 4*L-4
type: null
- input_params:
- mu
name: compare
ports:
- direction: input
name: In_sigma
size: mu
- direction: input
name: In_keep
size: mu
- direction: input
name: In_flag
size: '1'
- direction: output
name: out_sigma
size: mu
- direction: output
name: out_keep
size: mu
- direction: output
name: out_flag
size: '1'
resources:
- name: T_gates
type: additive
value: 4*mu-4
type: null
- connections:
- source: In_control
target: out_control
- source: In_target_0
target: out_target_0
- source: In_target_1
target: out_target_1
input_params:
- X
name: swap
ports:
- direction: input
name: In_control
size: '1'
- direction: input
name: In_target_0
size: X
- direction: input
name: In_target_1
size: X
- direction: output
name: out_control
size: null
- direction: output
name: out_target_0
size: null
- direction: output
name: out_target_1
size: null
resources:
- name: T_gates
type: additive
value: O(log_2(X))
type: null
connections:
- source: In_0
target: usp.in
- source: In_1
target: had.in
- source: In_2
target: qrom.In_alt
- source: In_3
target: qrom.In_keep
- source: In_4
target: compare.In_flag
- source: usp.out
target: qrom.In_l
- source: had.out
target: compare.In_sigma
- source: qrom.out_l
target: swap.In_target_0
- source: qrom.out_alt
target: swap.In_target_1
- source: qrom.out_keep
target: compare.In_keep
- source: compare.out_flag
target: swap.In_control
- source: swap.out_target_0
target: out_0
- source: compare.out_sigma
target: temp_0
- source: swap.out_target_1
target: temp_1
- source: compare.out_keep
target: temp_2
- source: swap.out_control
target: temp_3
input_params:
- mu
- L
linked_params:
- source: L
targets:
- usp.L
- qrom.L
- swap.X
- source: mu
targets:
- had.mu
- qrom.mu
- compare.mu
local_variables:
- R=ceiling(log_2(L))
name: alias_sampling
ports:
- direction: input
name: In_0
size: R
- direction: input
name: In_1
size: mu
- direction: input
name: In_2
size: R
- direction: input
name: In_3
size: mu
- direction: input
name: In_4
size: '1'
- direction: output
name: out_0
size: null
- direction: output
name: temp_0
size: null
- direction: output
name: temp_1
size: null
- direction: output
name: temp_2
size: null
- direction: output
name: temp_3
size: null
type: null
version: v1
Binary file added docs/images/as.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 46 additions & 9 deletions docs/library/userguide.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ validate(schema, data)

If you are familiar with [Pydantic](https://docs.pydantic.dev/latest/), you might find
it easier to work with QREF Pydantic models instead of interacting with JSON schema directly.
In the example below, we create an instance of [`SchemaV1`][qref.SchemaV1] model from
validated data stored in QREF format:
In the example below, we create an instance of [`SchemaV1`][qref.SchemaV1] model from validated data stored in QREF format:

```python
from qref import SchemaV1
Expand Down Expand Up @@ -76,13 +75,35 @@ if not verification_output:

```

### Rendering QREF files using `qref-render` (experimental)
### Topology validation

!!! Warning
There can be cases where a program is correct from the perspective of Pydantic validation, but has incorrect topology. This includes cases such as:

This feature is considered experimental and may occassionally produce
incorrect results.
- Disconnected ports
- Ports with multiple connections
- Cycles in the graph

In order to validate whether the topology of the program is correct you can use `verify_topology` method. Here's a short snippet showing how one can verify their program and print out the problems (if any).

```python
from qref.verification import verify_topology

program = load_some_program()

verification_output = verify_topology(program)

if not verification_output:
print("Program topology is incorrect, due to the following issues:")
for problem in verification_output.problems:
print(problem)

```

### Rendering QREF files using `qref-render` (experimental)

!!! Warning
This feature is considered experimental and may occassionally produce
incorrect results.

QREF comes with a CLI tool for rendering hierarchical graphs of quantum
algorithms. To render an algorithm stored in a file named `my_program.yaml` into a
Expand All @@ -95,8 +116,24 @@ qref-render my_program.yaml my_program_graph.svg
The `qref-render` tool supports `yaml` and `json` input formats, and all
output formats supported by [graphviz](https://graphviz.org/).

If, instead of using CLI, you'd like to invoke QREF's rendering capabilities
from Python script, you can look at [qref.experimental.rendering][qref.experimental.rendering]
module which exposes experimental API for performing the same task as `qref-render`.
If you prefer to use QREF's rendering capabilities from a Python script instead of the CLI, you can use the [`qref.experimental.rendering`](qref.experimental.rendering) module, which performs the same task as `qref-render`. Here, we demonstrate how to use the rendering module to visualize quantum circuits for preparing arbitrary quantum states in alias sampling. To learn more about the algorithm, please refer to the tutorial for [Bartiq](https://psiq.github.io/bartiq/latest/tutorials/02_alias_sampling_basic/) – our library for symbolic resource estimation.

We will use the `yaml` file `alias_sampling.yaml` as input to generate a graph representing this algorithm:

```python
import yaml
from qref import SchemaV1
from qref.experimental.rendering import to_graphviz

# Load the YAML file
with open("../examples/alias_sampling.yaml", "r") as f:
data = yaml.safe_load(f)

# Validate the schema and convert to Graphviz object
program = SchemaV1.model_validate(data)
gv_object = to_graphviz(program)

# Render the Graphviz object to a PNG file
gv_object.render("alias_sampling", format="png")
```
![alias_sampling|500](../images/as.png)
6 changes: 3 additions & 3 deletions src/qref/experimental/rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ def _format_node_name(node_name, parent):
if child.children: # Case 2: port of non-leaf child (=> port is a graphviz node)
return node_name
else: # Case 3: port of leaf child (=> port is an actual port of Mrecord, use ":")
return f"{child_name}:{port_name}"
return f"{child_name}: {port_name}"


def _add_nonleaf_ports(ports, parent_cluster, parent_path: str, group_name):
with parent_cluster.subgraph(
name=f"{parent_path}:{group_name}", graph_attr=PORT_GROUP_ATTRS
name=f"{parent_path}: {group_name}", graph_attr=PORT_GROUP_ATTRS
) as subgraph:
for port in ports:
subgraph.node(name=f"{parent_path}.{port.name}", label=port.name, **PORT_NODE_KWARGS)
Expand Down Expand Up @@ -136,7 +136,7 @@ def _ports_row(ports) -> str:

def _add_leaf(routine, dag: graphviz.Digraph, parent_path: str) -> None:
input_ports, output_ports = _split_ports(routine.ports)
label = f"{{ {_ports_row(input_ports)} | {routine.name } | {_ports_row(output_ports)} }}"
label = f"{{{_ports_row(input_ports)}|{routine.name}|{_ports_row(output_ports)}}}"
dag.node(".".join((parent_path, routine.name)), label=label, **LEAF_NODE_KWARGS)


Expand Down

0 comments on commit 966f67a

Please sign in to comment.