diff --git a/Cargo.lock b/Cargo.lock
index a1ac9e49..58f04526 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -179,9 +179,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
 
 [[package]]
 name = "cc"
-version = "1.0.98"
+version = "1.0.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
+checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
 
 [[package]]
 name = "cesu8"
@@ -507,7 +507,18 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustc_version",
- "syn 2.0.65",
+ "syn 2.0.66",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.66",
 ]
 
 [[package]]
@@ -824,14 +835,134 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "icu_collections"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.66",
+]
+
 [[package]]
 name = "idna"
-version = "0.5.0"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed"
 dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "icu_normalizer",
+ "icu_properties",
+ "smallvec",
+ "utf8_iter",
 ]
 
 [[package]]
@@ -946,6 +1077,12 @@ version = "0.2.155"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
 
+[[package]]
+name = "litemap"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
+
 [[package]]
 name = "lock_api"
 version = "0.4.12"
@@ -1397,9 +1534,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.4"
+version = "1.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1409,9 +1546,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1420,9 +1557,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
 
 [[package]]
 name = "relative-path"
@@ -1609,6 +1746,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
 [[package]]
 name = "strsim"
 version = "0.11.1"
@@ -1659,6 +1802,17 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.66",
+]
+
 [[package]]
 name = "tap"
 version = "1.0.1"
@@ -1753,30 +1907,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "tinytemplate"
-version = "1.2.1"
+name = "tinystr"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
 dependencies = [
- "serde",
- "serde_json",
+ "displaydoc",
+ "zerovec",
 ]
 
 [[package]]
-name = "tinyvec"
-version = "1.6.0"
+name = "tinytemplate"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
 dependencies = [
- "tinyvec_macros",
+ "serde",
+ "serde_json",
 ]
 
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
 [[package]]
 name = "tket-json-rs"
 version = "0.4.1"
@@ -1807,6 +1956,7 @@ dependencies = [
  "downcast-rs",
  "fxhash",
  "hugr",
+ "hugr-core",
  "itertools 0.13.0",
  "lazy_static",
  "num-complex",
@@ -1961,27 +2111,12 @@ dependencies = [
  "syn 2.0.66",
 ]
 
-[[package]]
-name = "unicode-bidi"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
-
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
-[[package]]
-name = "unicode-normalization"
-version = "0.1.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
-dependencies = [
- "tinyvec",
-]
-
 [[package]]
 name = "unindent"
 version = "0.2.3"
@@ -1996,9 +2131,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
 
 [[package]]
 name = "url"
-version = "2.5.0"
+version = "2.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -2011,17 +2146,29 @@ version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
 
+[[package]]
+name = "utf16_iter"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
 [[package]]
 name = "utf8-width"
 version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
 
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
 [[package]]
 name = "utf8parse"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "uuid"
@@ -2309,6 +2456,18 @@ version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
 
+[[package]]
+name = "write16"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+
 [[package]]
 name = "wyz"
 version = "0.5.1"
@@ -2317,3 +2476,70 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
 dependencies = [
  "tap",
 ]
+
+[[package]]
+name = "yoke"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.66",
+ "synstructure",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.66",
+ "synstructure",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.66",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 0b625af8..b30947e3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,13 @@ lto = "thin"
 
 [workspace]
 resolver = "2"
-members = ["tket2", "tket2-py", "compile-rewriter", "badger-optimiser", "tket2-hseries"]
+members = [
+    "tket2",
+    "tket2-py",
+    "compile-rewriter",
+    "badger-optimiser",
+    "tket2-hseries",
+]
 default-members = ["tket2", "tket2-hseries"]
 
 [workspace.package]
@@ -21,6 +27,7 @@ missing_docs = "warn"
 tket2 = { path = "./tket2" }
 hugr = "0.5.1"
 hugr-cli = "0.1.1"
+hugr-core = "0.2.0"
 portgraph = "0.12"
 pyo3 = "0.21.2"
 itertools = "0.13.0"
diff --git a/tket2/Cargo.toml b/tket2/Cargo.toml
index cff8cea0..2eba9e4a 100644
--- a/tket2/Cargo.toml
+++ b/tket2/Cargo.toml
@@ -50,6 +50,7 @@ serde_yaml = { workspace = true }
 portmatching = { workspace = true, optional = true, features = ["serde"] }
 derive_more = { workspace = true }
 hugr = { workspace = true }
+hugr-core = { workspace = true }
 portgraph = { workspace = true, features = ["serde"] }
 strum_macros = { workspace = true }
 strum = { workspace = true }
diff --git a/tket2/src/circuit.rs b/tket2/src/circuit.rs
index ef09402d..ed27d58c 100644
--- a/tket2/src/circuit.rs
+++ b/tket2/src/circuit.rs
@@ -9,15 +9,17 @@ use std::iter::Sum;
 
 pub use command::{Command, CommandIterator};
 pub use hash::CircuitHash;
+use hugr::hugr::views::{DescendantsGraph, ExtractHugr, HierarchyView};
 use itertools::Either::{Left, Right};
 
 use hugr::hugr::hugrmut::HugrMut;
 use hugr::hugr::NodeType;
 use hugr::ops::dataflow::IOTrait;
-use hugr::ops::{Input, NamedOp, OpParent, OpTag, OpTrait, Output};
+use hugr::ops::{Input, NamedOp, OpParent, OpTag, OpTrait, Output, DFG};
 use hugr::types::{FunctionType, PolyFuncType};
 use hugr::{Hugr, PortIndex};
 use hugr::{HugrView, OutgoingPort};
+use hugr_core::hugr::internal::HugrMutInternals;
 use itertools::Itertools;
 use thiserror::Error;
 
@@ -299,6 +301,37 @@ impl<T: HugrView> Circuit<T> {
         // TODO: See comment in `dot_string`.
         self.hugr.mermaid_string()
     }
+
+    /// Extracts the circuit into a new owned HUGR containing the circuit at the root.
+    /// Replaces the circuit container operation with an [`OpType::Dfg`].
+    ///
+    /// Regions that are not descendants of the parent node are not included in the new HUGR.
+    /// This may invalidate calls to functions defined elsewhere. Make sure to inline any
+    /// external functions before calling this method.
+    pub fn extract_dfg(self) -> Result<Circuit<Hugr>, CircuitMutError>
+    where
+        T: ExtractHugr,
+    {
+        let mut circ = if self.parent == self.hugr.root() {
+            self.to_owned()
+        } else {
+            let view: DescendantsGraph = DescendantsGraph::try_new(&self.hugr, self.parent)
+                .expect("Circuit parent was not a dataflow container.");
+            view.extract_hugr().into()
+        };
+
+        // Replace the parent node with a DFG node, if necessary.
+        let nodetype = circ.hugr.get_nodetype(circ.parent());
+        if !matches!(nodetype.op(), OpType::DFG(_)) {
+            let dfg = DFG {
+                signature: circ.circuit_signature(),
+            };
+            let input_extensions = nodetype.input_extensions().cloned();
+            let nodetype = NodeType::new(OpType::DFG(dfg), input_extensions);
+            circ.hugr.replace_op(circ.parent(), nodetype)?;
+        }
+        Ok(circ)
+    }
 }
 
 impl<T: HugrView> From<T> for Circuit<T> {
@@ -648,12 +681,9 @@ mod tests {
         #[case] circ: Circuit,
         #[case] qubits: usize,
         #[case] bits: usize,
-        #[case] _name: Option<&str>,
+        #[case] name: Option<&str>,
     ) {
-        // TODO: The decoder discards the circuit name.
-        // This requires decoding circuits into `FuncDefn` nodes instead of `Dfg`,
-        // but currently that causes errors with the replacement methods.
-        //assert_eq!(circ.name(), name);
+        assert_eq!(circ.name(), name);
         assert_eq!(circ.circuit_signature().input_count(), qubits + bits);
         assert_eq!(circ.circuit_signature().output_count(), qubits + bits);
         assert_eq!(circ.qubit_count(), qubits);
diff --git a/tket2/src/portmatching/matcher.rs b/tket2/src/portmatching/matcher.rs
index f949e5ea..0a3e4afa 100644
--- a/tket2/src/portmatching/matcher.rs
+++ b/tket2/src/portmatching/matcher.rs
@@ -412,7 +412,7 @@ impl From<InvalidSubgraph> for InvalidPatternMatch {
                 InvalidSubgraphBoundary::DisconnectedBoundaryPort(_, _),
             ) => InvalidPatternMatch::NotConvex,
             InvalidSubgraph::EmptySubgraph => InvalidPatternMatch::EmptyMatch,
-            InvalidSubgraph::NoSharedParent | InvalidSubgraph::InvalidBoundary(_) => {
+            InvalidSubgraph::NoSharedParent { .. } | InvalidSubgraph::InvalidBoundary(_) => {
                 InvalidPatternMatch::InvalidSubcircuit
             }
             other => InvalidPatternMatch::Other(other),
diff --git a/tket2/src/rewrite.rs b/tket2/src/rewrite.rs
index a2fb220d..08c6d61e 100644
--- a/tket2/src/rewrite.rs
+++ b/tket2/src/rewrite.rs
@@ -12,6 +12,7 @@ pub use ecc_rewriter::ECCRewriter;
 use derive_more::{From, Into};
 use hugr::hugr::hugrmut::HugrMut;
 use hugr::hugr::views::sibling_subgraph::{InvalidReplacement, InvalidSubgraph};
+use hugr::hugr::views::ExtractHugr;
 use hugr::{
     hugr::{views::SiblingSubgraph, Rewrite, SimpleReplacementError},
     SimpleReplacement,
@@ -33,7 +34,7 @@ impl Subcircuit {
     /// Create a new subcircuit induced from a set of nodes.
     pub fn try_from_nodes(
         nodes: impl Into<Vec<Node>>,
-        circ: &Circuit,
+        circ: &Circuit<impl HugrView>,
     ) -> Result<Self, InvalidSubgraph> {
         let subgraph = SiblingSubgraph::try_from_nodes(nodes, circ.hugr())?;
         Ok(Self { subgraph })
@@ -49,16 +50,25 @@ impl Subcircuit {
         self.subgraph.node_count()
     }
 
-    /// Create a rewrite rule to replace the subcircuit.
+    /// Create a rewrite rule to replace the subcircuit with a new circuit.
+    ///
+    /// # Parameters
+    /// * `circuit` - The base circuit that contains the subcircuit.
+    /// * `replacement` - The new circuit to replace the subcircuit with.
     pub fn create_rewrite(
         &self,
-        source: &Circuit,
-        target: Circuit,
+        circuit: &Circuit<impl HugrView>,
+        replacement: Circuit<impl ExtractHugr>,
     ) -> Result<CircuitRewrite, InvalidReplacement> {
-        Ok(CircuitRewrite(self.subgraph.create_simple_replacement(
-            source.hugr(),
-            target.into_hugr(),
-        )?))
+        // The replacement must be a Dfg rooted hugr.
+        let replacement = replacement
+            .extract_dfg()
+            .unwrap_or_else(|e| panic!("{}", e))
+            .into_hugr();
+        Ok(CircuitRewrite(
+            self.subgraph
+                .create_simple_replacement(circuit.hugr(), replacement)?,
+        ))
     }
 }
 
@@ -69,13 +79,17 @@ pub struct CircuitRewrite(SimpleReplacement);
 impl CircuitRewrite {
     /// Create a new rewrite rule.
     pub fn try_new(
-        source_position: &Subcircuit,
-        source: &Circuit<impl HugrView>,
-        target: Circuit,
+        circuit_position: &Subcircuit,
+        circuit: &Circuit<impl HugrView>,
+        replacement: Circuit<impl ExtractHugr>,
     ) -> Result<Self, InvalidReplacement> {
-        source_position
+        let replacement = replacement
+            .extract_dfg()
+            .unwrap_or_else(|e| panic!("{}", e))
+            .into_hugr();
+        circuit_position
             .subgraph
-            .create_simple_replacement(source.hugr(), target.into_hugr())
+            .create_simple_replacement(circuit.hugr(), replacement)
             .map(Self)
     }
 
diff --git a/tket2/src/serialize/pytket/decoder.rs b/tket2/src/serialize/pytket/decoder.rs
index 4b4cd218..3b589774 100644
--- a/tket2/src/serialize/pytket/decoder.rs
+++ b/tket2/src/serialize/pytket/decoder.rs
@@ -5,7 +5,7 @@ use std::collections::HashMap;
 use std::hash::{Hash, Hasher};
 use std::mem;
 
-use hugr::builder::{CircuitBuilder, Container, DFGBuilder, Dataflow, DataflowHugr};
+use hugr::builder::{CircuitBuilder, Container, Dataflow, DataflowHugr, FunctionBuilder};
 use hugr::extension::prelude::QB_T;
 
 use hugr::types::FunctionType;
@@ -22,13 +22,13 @@ use super::{METADATA_B_REGISTERS, METADATA_Q_REGISTERS};
 use crate::extension::{LINEAR_BIT, REGISTRY};
 use crate::symbolic_constant_op;
 
-/// The state of an in-progress [`DFGBuilder`] being built from a [`SerialCircuit`].
+/// The state of an in-progress [`FunctionBuilder`] being built from a [`SerialCircuit`].
 ///
 /// Mostly used to define helper internal methods.
 #[derive(Debug, PartialEq)]
 pub(super) struct JsonDecoder {
     /// The Hugr being built.
-    pub hugr: DFGBuilder<Hugr>,
+    pub hugr: FunctionBuilder<Hugr>,
     /// The dangling wires of the builder.
     /// Used to generate [`CircuitBuilder`]s.
     dangling_wires: Vec<Wire>,
@@ -66,8 +66,8 @@ impl JsonDecoder {
         );
         // .with_extension_delta(&ExtensionSet::singleton(&TKET1_EXTENSION_ID));
 
-        // TODO: Use a FunctionBuilder and store the circuit name there.
-        let mut dfg = DFGBuilder::new(sig).unwrap();
+        let name = serialcirc.name.clone().unwrap_or_default();
+        let mut dfg = FunctionBuilder::new(name, sig.into()).unwrap();
 
         // Metadata. The circuit requires "name", and we store other things that
         // should pass through the serialization roundtrip.
@@ -128,7 +128,7 @@ impl JsonDecoder {
     }
 
     /// Apply a function to the internal hugr builder viewed as a [`CircuitBuilder`].
-    fn with_circ_builder(&mut self, f: impl FnOnce(&mut CircuitBuilder<DFGBuilder<Hugr>>)) {
+    fn with_circ_builder(&mut self, f: impl FnOnce(&mut CircuitBuilder<FunctionBuilder<Hugr>>)) {
         let mut circ = self.hugr.as_circuit(mem::take(&mut self.dangling_wires));
         f(&mut circ);
         self.dangling_wires = circ.finish();
diff --git a/tket2/src/utils.rs b/tket2/src/utils.rs
index 727a2f09..e6578c47 100644
--- a/tket2/src/utils.rs
+++ b/tket2/src/utils.rs
@@ -3,10 +3,12 @@
 use hugr::builder::{Container, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder};
 use hugr::extension::PRELUDE_REGISTRY;
 use hugr::ops::handle::NodeHandle;
+use hugr::std_extensions::arithmetic::float_ops::FLOAT_OPS_REGISTRY;
+use hugr::std_extensions::arithmetic::float_types;
 use hugr::types::{Type, TypeBound};
 use hugr::Hugr;
 use hugr::{
-    builder::{BuildError, CircuitBuilder, DFGBuilder, Dataflow, DataflowHugr},
+    builder::{BuildError, CircuitBuilder, Dataflow, DataflowHugr},
     extension::prelude::QB_T,
     types::FunctionType,
 };
@@ -21,10 +23,12 @@ pub(crate) fn type_is_linear(typ: &Type) -> bool {
 #[allow(unused)]
 pub(crate) fn build_simple_circuit<F>(num_qubits: usize, f: F) -> Result<Circuit, BuildError>
 where
-    F: FnOnce(&mut CircuitBuilder<DFGBuilder<Hugr>>) -> Result<(), BuildError>,
+    F: FnOnce(&mut CircuitBuilder<FunctionBuilder<Hugr>>) -> Result<(), BuildError>,
 {
     let qb_row = vec![QB_T; num_qubits];
-    let mut h = DFGBuilder::new(FunctionType::new(qb_row.clone(), qb_row))?;
+    let signature =
+        FunctionType::new(qb_row.clone(), qb_row).with_extension_delta(float_types::EXTENSION_ID);
+    let mut h = FunctionBuilder::new("main", signature.into())?;
 
     let qbs = h.input_wires();
 
@@ -33,7 +37,9 @@ where
     f(&mut circ)?;
 
     let qbs = circ.finish();
-    let hugr = h.finish_hugr_with_outputs(qbs, &PRELUDE_REGISTRY)?;
+
+    // The float ops registry is required to define constant float values.
+    let hugr = h.finish_hugr_with_outputs(qbs, &FLOAT_OPS_REGISTRY)?;
     Ok(hugr.into())
 }