Crate hugr

Source
Expand description

Extensible, graph-based program representation with first-class support for linear types.

The name HUGR stands for “Hierarchical Unified Graph Representation”. It is designed primarily as an intermediate representation and interchange format for quantum and hybrid classical–quantum programs.

Both data-flow and control-flow graphs can be represented in the HUGR. Nodes in the graph may represent basic operations, or may themselves have “child” graphs, which inherit their inputs and outputs. Special “non-local” edges allow data to pass directly from a node to another node that is not a direct descendent (subject to causality constraints).

The specification can be found here.

This crate provides a Rust implementation of HUGR and the standard extensions defined in the specification.

It includes methods for:

  • building HUGRs from basic operations;
  • defining new extensions;
  • serializing and deserializing HUGRs;
  • performing local rewrites.

§Example

To build a HUGR for a simple quantum circuit and then serialize it to a buffer, we can define a simple quantum extension and then use the [builder::DFGBuilder] as follows:

use hugr::builder::{BuildError, DFGBuilder, Dataflow, DataflowHugr, inout_sig};
use hugr::extension::prelude::{bool_t, qb_t};
use hugr::envelope::EnvelopeConfig;
use hugr::hugr::Hugr;
use hugr::type_row;
use hugr::types::FuncValueType;

// The type of qubits, `qb_t()` is in the prelude but, by default, no gateset
// is defined. This module provides Hadamard and CX gates.
mod mini_quantum_extension {
    use hugr::{
        extension::{
            prelude::{bool_t, qb_t},
            ExtensionId, ExtensionRegistry, PRELUDE, Version,
        },
        ops::{ExtensionOp, OpName},
        type_row,
        types::{FuncValueType, PolyFuncTypeRV},
        Extension,
    };

    use std::sync::Arc;
    use lazy_static::lazy_static;

    fn one_qb_func() -> PolyFuncTypeRV {
        FuncValueType::new_endo(vec![qb_t()]).into()
    }

    fn two_qb_func() -> PolyFuncTypeRV {
        FuncValueType::new_endo(vec![qb_t(), qb_t()]).into()
    }
    /// The extension identifier.
    pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("mini.quantum");
    pub const VERSION: Version = Version::new(0, 1, 0);
    fn extension() -> Arc<Extension> {
        Extension::new_arc(EXTENSION_ID, VERSION, |ext, extension_ref| {
            ext.add_op(OpName::new_inline("H"), "Hadamard".into(), one_qb_func(), extension_ref)
                .unwrap();

            ext.add_op(OpName::new_inline("CX"), "CX".into(), two_qb_func(), extension_ref)
                .unwrap();

            ext.add_op(
                OpName::new_inline("Measure"),
                "Measure a qubit, returning the qubit and the measurement result.".into(),
                FuncValueType::new(vec![qb_t()], vec![qb_t(), bool_t()]),
                extension_ref,
            )
            .unwrap();
        })
    }

    lazy_static! {
        /// Quantum extension definition.
        pub static ref EXTENSION: Arc<Extension> = extension();
    }
    fn get_gate(gate_name: impl Into<OpName>) -> ExtensionOp {
        EXTENSION
            .instantiate_extension_op(&gate_name.into(), [])
            .unwrap()
            .into()
    }
    pub fn h_gate() -> ExtensionOp {
        get_gate("H")
    }

    pub fn cx_gate() -> ExtensionOp {
        get_gate("CX")
    }

    pub fn measure() -> ExtensionOp {
        get_gate("Measure")
    }
}

use mini_quantum_extension::{cx_gate, h_gate, measure};

//      ┌───┐
// q_0: ┤ H ├──■─────
//      ├───┤┌─┴─┐┌─┐
// q_1: ┤ H ├┤ X ├┤M├
//      └───┘└───┘└╥┘
// c:              ╚═
fn make_dfg_hugr() -> Result<Hugr, BuildError> {
    let mut dfg_builder = DFGBuilder::new(inout_sig(
        vec![qb_t(), qb_t()],
        vec![qb_t(), qb_t(), bool_t()],
    ))?;
    let [wire0, wire1] = dfg_builder.input_wires_arr();
    let h0 = dfg_builder.add_dataflow_op(h_gate(), vec![wire0])?;
    let h1 = dfg_builder.add_dataflow_op(h_gate(), vec![wire1])?;
    let cx = dfg_builder.add_dataflow_op(cx_gate(), h0.outputs().chain(h1.outputs()))?;
    let measure = dfg_builder.add_dataflow_op(measure(), cx.outputs().last())?;
    dfg_builder.finish_hugr_with_outputs(cx.outputs().take(1).chain(measure.outputs()))
}

let h: Hugr = make_dfg_hugr().unwrap();
let serialized = h.store_str(EnvelopeConfig::text()).unwrap();
println!("{}", serialized);

Modules§

algorithms
Compilation passes acting on the HUGR program representation.
builder
Utilities for building valid HUGRs.
core
Definitions for the core types used in the Hugr.
envelope
Envelope format for HUGR packages.
extension
Extensions
hugr
The Hugr data structure, and its basic component handles.
ops
The operation types for the HUGR.
package
Bundles of hugr modules along with the extension required to load them.
std_extensions
Experiments for Extension definitions.
types
General wire types used in the compiler
utils
General utilities.

Macros§

const_extension_ids
Declare ‘const’ variables holding new ExtensionIds, validating that they are well-formed as separate tests - hence, usable at the top level of a test module only. Example:
type_row
Creates a TypeRow backed by statically defined data, avoiding allocations.

Structs§

Extension
A extension is a set of capabilities required to execute a graph.
Hugr
The Hugr data structure.
IncomingPort
A port in the incoming direction.
Node
A handle to a node in the HUGR.
OutgoingPort
A port in the outgoing direction.
Port
A handle to a port for a node in the HUGR.
SimpleReplacement
Specification of a simple replacement operation.
Wire
A DataFlow wire, defined by a Value-kind output port of a node

Enums§

CircuitUnit
Enum for uniquely identifying the origin of linear wires in a circuit-like dataflow region.

Traits§

HugrView
A trait for inspecting HUGRs. For end users we intend this to be superseded by region-specific APIs.
NodeIndex
A trait for getting the index of a node.
PortIndex
A trait for getting the undirected index of a port.

Type Aliases§

Direction
The direction of a port.