Repository: HQSquantumsimulations/qoqo (language: python)

Issue:
activating github actions



Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 334520b8..7be885aa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,9 +13,9 @@ dependencies = [
 
 [[package]]
 name = "async-trait"
-version = "0.1.57"
+version = "0.1.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
+checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -45,9 +45,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bytemuck"
-version = "1.12.1"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da"
+checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835"
 
 [[package]]
 name = "cfg-if"
@@ -57,9 +57,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "ctor"
-version = "0.1.23"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb"
+checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
 dependencies = [
  "quote",
  "syn",
@@ -67,15 +67,15 @@ dependencies = [
 
 [[package]]
 name = "dyn-clone"
-version = "1.0.9"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
+checksum = "9d07a982d1fb29db01e5a59b1918e03da4df7297eaeee7686ac45542fd4e59c8"
 
 [[package]]
 name = "erased-serde"
-version = "0.3.22"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "003000e712ad0f95857bd4d2ef8d1890069e06554101697d12050668b2f6f020"
+checksum = "81d013529d5574a60caeda29e179e695125448e5de52e3874f7b4c1d7360e18e"
 dependencies = [
  "serde",
 ]
@@ -88,9 +88,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
 
 [[package]]
 name = "futures"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa"
+checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -103,9 +103,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -113,15 +113,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528"
+checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -130,15 +130,15 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5"
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d"
+checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -147,21 +147,21 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765"
+checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
 
 [[package]]
 name = "futures-task"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306"
+checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
 
 [[package]]
 name = "futures-util"
-version = "0.3.23"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577"
+checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -188,9 +188,9 @@ dependencies = [
 
 [[package]]
 name = "ghost"
-version = "0.1.6"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb19fe8de3ea0920d282f7b77dd4227aea6b8b999b42cdf0ca41b2472b14443a"
+checksum = "b93490550b1782c589a350f2211fff2e34682e25fed17ef53fc4fa8fe184975e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -215,15 +215,15 @@ dependencies = [
 
 [[package]]
 name = "indoc"
-version = "1.0.7"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3"
+checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e"
 
 [[package]]
 name = "inventory"
-version = "0.3.1"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30a61b8101d87996f82d725ba701b1987b7afc72f481c13513a30b855b9c9133"
+checksum = "e0257e268c91daba296499206db5dd5a058875936bec3d8429b5f3e20ec9dc9a"
 dependencies = [
  "ctor",
  "ghost",
@@ -231,21 +231,21 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.3"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
 
 [[package]]
 name = "libc"
-version = "0.2.132"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
+checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
 
 [[package]]
 name = "libm"
-version = "0.2.5"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565"
+checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"
 
 [[package]]
 name = "lock_api"
@@ -274,9 +274,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "nalgebra"
-version = "0.31.1"
+version = "0.31.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9e0a04ce089f9401aac565c740ed30c46291260f27d4911fdbaa6ca65fa3044"
+checksum = "18a89248335f688e4bd994e6d030fd7e185eb41769b8c435395075425e100ac6"
 dependencies = [
  "approx",
  "matrixmultiply",
@@ -301,9 +301,9 @@ dependencies = [
 
 [[package]]
 name = "ndarray"
-version = "0.15.6"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
+checksum = "dec23e6762830658d2b3d385a75aa212af2f67a4586d4442907144f3bb6a1ca8"
 dependencies = [
  "matrixmultiply",
  "num-complex",
@@ -369,9 +369,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.13.1"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
+checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
 
 [[package]]
 name = "parking_lot"
@@ -398,9 +398,9 @@ dependencies = [
 
 [[package]]
 name = "paste"
-version = "1.0.8"
+version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
+checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
 
 [[package]]
 name = "petgraph"
@@ -410,6 +410,8 @@ checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143"
 dependencies = [
  "fixedbitset",
  "indexmap",
+ "serde",
+ "serde_derive",
 ]
 
 [[package]]
@@ -456,9 +458,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.43"
+version = "1.0.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
+checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
 dependencies = [
  "unicode-ident",
 ]
@@ -583,9 +585,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.21"
+version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
 dependencies = [
  "proc-macro2",
 ]
@@ -638,9 +640,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.16"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+checksum = "534cfe58d6a18cc17120fbf4635d53d14691c1fe4d951064df9bd326178d7d5a"
 dependencies = [
  "bitflags",
 ]
@@ -697,9 +699,9 @@ dependencies = [
 
 [[package]]
 name = "ryu"
-version = "1.0.11"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
 
 [[package]]
 name = "safe_arch"
@@ -718,18 +720,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "serde"
-version = "1.0.143"
+version = "1.0.140"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
+checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.143"
+version = "1.0.140"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
+checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -738,9 +740,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.83"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
+checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
 dependencies = [
  "itoa",
  "ryu",
@@ -749,18 +751,18 @@ dependencies = [
 
 [[package]]
 name = "serde_test"
-version = "1.0.143"
+version = "1.0.140"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4b5b007c1bccdc70f8d8b5804da305a64d4279f990f1ae9e3b06eaae46f42e0"
+checksum = "38b0651a2f427e4a4d74d458947aa5ca36c62c1b503344d143763ec06216a975"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "simba"
-version = "0.7.2"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c48e45e5961033db030b56ad67aef22e9c908c493a6e8348c0a0f6b93433cd77"
+checksum = "13a2609e876d4f77f6ab7ff5254fc39b4f1927ba8e6db3d18be7c32534d3725e"
 dependencies = [
  "approx",
  "num-complex",
@@ -786,9 +788,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
 
 [[package]]
 name = "syn"
-version = "1.0.99"
+version = "1.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
+checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -825,18 +827,18 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.32"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.32"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -851,9 +853,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
 
 [[package]]
 name = "typetag"
-version = "0.2.3"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23c8909f61359cac318f10dc9a3f9a7ee2155b6511f2a0c973460222ae5a00bb"
+checksum = "62a3f3e32f68a8a611e8b98a14e058954f1b81d6eb6af50e2349bf322ae4062b"
 dependencies = [
  "erased-serde",
  "inventory",
@@ -864,9 +866,9 @@ dependencies = [
 
 [[package]]
 name = "typetag-impl"
-version = "0.2.3"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd4d8ff674190866d3437d2f725355d49e99e3b1a513bf7ee43dcfe7effc36f4"
+checksum = "762400b16d1815702b6a22e71764f5936ef5d2c4dcff95497f874c53594d59fd"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -875,15 +877,15 @@ dependencies = [
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.3"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
+checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
 
 [[package]]
 name = "unindent"
-version = "0.1.10"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58ee9362deb4a96cef4d437d1ad49cffc9b9e92d202b6995674e928ce684f112"
+checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44"
 
 [[package]]
 name = "version_check"
diff --git a/qoqo/Cargo.toml b/qoqo/Cargo.toml
index aea79f46..deb91c32 100644
--- a/qoqo/Cargo.toml
+++ b/qoqo/Cargo.toml
@@ -54,6 +54,7 @@ no-default-features = true
 [features]
 extension-module = ["pyo3/extension-module"]#, "qoqo_calculator_pyo3/extension-module"]
 default = ["extension-module"]
+circuitdag = ["roqoqo/circuitdag"]
 
 [package.metadata.maturin]
 requires-dist = ["numpy", "qoqo_calculator_pyo3>=0.8.3"]
diff --git a/qoqo/src/circuitdag.rs b/qoqo/src/circuitdag.rs
new file mode 100644
index 00000000..bf14ee4b
--- /dev/null
+++ b/qoqo/src/circuitdag.rs
@@ -0,0 +1,465 @@
+// Copyright © 2021 HQS Quantum Simulations GmbH. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software distributed under the
+// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the License for the specific language governing permissions and
+// limitations under the License.
+
+///! Module containing the CircuitDag class that represents the Directed Acyclic Graph (DAG)
+///! of a quantum circuit in qoqo.
+///!
+use std::collections::HashSet;
+
+use crate::{QoqoError, QOQO_VERSION};
+use bincode::{deserialize, serialize};
+use pyo3::exceptions::{PyIndexError, PyTypeError, PyValueError};
+use pyo3::prelude::*;
+use pyo3::types::PyByteArray;
+use roqoqo::{Circuit, CircuitDag, ROQOQO_VERSION};
+
+use crate::operations::{convert_operation_to_pyobject, convert_pyany_to_operation};
+use crate::CircuitWrapper;
+
+/// Module containing the CircuitDag class that represents the Directed Acyclic Graph (DAG)
+/// of a quantum circuit in qoqo.
+///
+#[pymodule]
+fn circuitdag(_py: Python, module: &PyModule) -> PyResult<()> {
+    module.add_class::<CircuitDagWrapper>()?;
+    Ok(())
+}
+
+/// Represents the Direct Acyclic Graph (DAG) of a Circuit.
+///
+#[pyclass(name = "CircuitDag", module = "qoqo")]
+#[derive(Clone, Debug, PartialEq)]
+#[pyo3(text_signature = "(node_number, edge_number, /)")]
+pub struct CircuitDagWrapper {
+    /// Internal storage of [roqoqo:CircuitDag]
+    pub internal: CircuitDag,
+}
+
+impl Default for CircuitDagWrapper {
+    fn default() -> Self {
+        Self::new(100, 300)
+    }
+}
+
+#[pymethods]
+impl CircuitDagWrapper {
+    /// Create an empty CircuitDag.
+    ///
+    /// Args:
+    ///     node_number (int): The node max capacity of the new CircuitDag.
+    ///     edge_number (int): The edge max capacity of the new CircuitDag.
+    ///
+    /// Returns:
+    ///     self: The new, empty CircuitDag.
+    #[new]
+    pub fn new(node_number: usize, edge_number: usize) -> Self {
+        Self {
+            internal: CircuitDag::with_capacity(node_number, edge_number),
+        }
+    }
+
+    /// Create a CircuitDag from a given Circuit;
+    ///
+    /// Args:
+    ///     circuit (Circuit): The Circuit to build the new CircuitDag from.
+    ///
+    /// Returns:
+    ///     self: The new CircuitDag.
+    #[pyo3(text_signature = "(circuit)")]
+    pub fn from_circuit(&self, circuit: Py<PyAny>) -> PyResult<Self> {
+        let circuit = Python::with_gil(|py| -> Result<Circuit, QoqoError> {
+            let circ_ref = circuit.as_ref(py);
+            crate::convert_into_circuit(circ_ref)
+        })
+        .unwrap();
+
+        Ok(Self {
+            internal: CircuitDag::from(circuit),
+        })
+    }
+
+    /// Transforms the CircuitDag into a Circuit.
+    ///
+    #[pyo3(text_signature = "($self)")]
+    pub fn to_circuit(&self) -> PyResult<CircuitWrapper> {
+        Ok(CircuitWrapper {
+            internal: Circuit::from(self.internal.clone()),
+        })
+    }
+
+    /// Add an Operation to the back of the CircuitDag, if necessary.
+    ///
+    /// Args:
+    ///     op (Operation): The Operation to add to the back of the CircuitDag.
+    ///
+    /// Raises:
+    ///     TypeError: The Python Object cannot be converted to Operation.
+    #[pyo3(text_signature = "($self, op)")]
+    pub fn add_to_back(&mut self, op: &PyAny) -> PyResult<Option<usize>> {
+        let operation = convert_pyany_to_operation(op).map_err(|x| {
+            PyTypeError::new_err(format!("Cannot convert python object to Operation {:?}", x))
+        })?;
+        Ok(self.internal.add_to_back(operation))
+    }
+
+    /// Add an Operation to the front of the CircuitDag, if necessary.
+    ///
+    /// Args:
+    ///     op (Operation): The Operation to add to the front of the CircuitDag.
+    ///
+    /// Raises:
+    ///     TypeError: The Python Object cannot be converted to Operation.
+    #[pyo3(text_signature = "($self, op)")]
+    pub fn add_to_front(&mut self, op: &PyAny) -> PyResult<Option<usize>> {
+        let operation = convert_pyany_to_operation(op).map_err(|x| {
+            PyTypeError::new_err(format!("Cannot convert python object to Operation {:?}", x))
+        })?;
+        Ok(self.internal.add_to_front(operation))
+    }
+
+    /// Checks if executing an operation is blocked by any not-yet executed operation.
+    ///
+    /// Args:
+    ///     already_executed (list[int]): List of NodeIndices of Nodes that have already been executed in the Circuit.
+    ///     to_be_executed (int): NodeIndex of the operation that should be executed next.
+    ///
+    /// Returns:
+    ///     list[int]: List containing the sorted blocking elements.
+    #[pyo3(text_signature = "($self, already_executed, to_be_executed)")]
+    pub fn execution_blocked(
+        &self,
+        already_executed: Vec<usize>,
+        to_be_executed: usize,
+    ) -> Vec<usize> {
+        self.internal
+            .execution_blocked(already_executed.as_slice(), &to_be_executed)
+    }
+
+    /// Checks which of the direct predecessors of an Operation in the CircuitDag blocks the execution.
+    ///
+    /// Warning:
+    /// This method can only be used to determine if an operation can be executed when `already_executed` is consistent.
+    /// When the list  `already_executed` is inconsistent (a n operation is reported as executed that could not have been executed yet)
+    /// this method returning an empty vector does not imply that the `to_be_executed` operation can be executed.
+    ///
+    /// Args:
+    ///     already_executed (list[int]): List of NodeIndices of Nodes that have already been executed in the Circuit.
+    ///     to_be_executed(int): NodeIndex of the Operation that should be executed next.
+    ///
+    /// Returns:
+    ///     list[int]: List containing the sorted blocking elements.
+    #[pyo3(text_signature = "($self, already_executed, to_be_executed)")]
+    pub fn blocking_predecessors(
+        &self,
+        already_executed: Vec<usize>,
+        to_be_executed: usize,
+    ) -> Vec<usize> {
+        self.internal
+            .blocking_predecessors(already_executed.as_slice(), &to_be_executed)
+    }
+
+    /// Returns a new front-layer after executing an operation from the current front layer.
+    ///
+    /// Returns an error if operation to be executed is not in the current front layer.
+    ///
+    /// Args:
+    ///     already_executed (list[int]): List of NodeIndices of Nodes that have already been executed in the Circuit.
+    ///     current_front_layer (list[int]): List of NodeIndices in the current front layer ready to be executed if physically possible.
+    ///     to_be_executed (int): NodeIndex of the operation that should be executed next.
+    #[pyo3(text_signature = "($self, already_executed, current_front_layer, to_be_executed)")]
+    pub fn new_front_layer(
+        &self,
+        already_executed: Vec<usize>,
+        current_front_layer: Vec<usize>,
+        to_be_executed: usize,
+    ) -> PyResult<Vec<usize>> {
+        Ok(self
+            .internal
+            .new_front_layer(
+                already_executed.as_slice(),
+                current_front_layer.as_slice(),
+                &to_be_executed,
+            )
+            .map_err(|_| {
+                PyValueError::new_err(format!(
+                    "The Operation to be executed is not in the current front layer."
+                ))
+            })?)
+    }
+
+    /// Returns an iterator over the possible parallel blocks in circuit that can be executed simultaneously
+    ///
+    /// Returns an Iterator over Vectors of references to the NodeIndices in the parallel block as well
+    /// as references to the Operation in the blocks
+    #[pyo3(text_signature = "($self)")]
+    pub fn parallel_blocks(&self) -> Vec<Vec<usize>> {
+        let mut par_bl_vec: Vec<Vec<usize>> = Vec::new();
+
+        for block in self.internal.parallel_blocks() {
+            par_bl_vec.push(block.clone());
+        }
+
+        par_bl_vec
+    }
+
+    /// Given a NodeIndex, returns the Operation contained in the node of
+    /// the CircuitDag.
+    ///
+    /// Args:
+    ///     index (int): The index of the node to get from the CircuitDag.
+    ///
+    /// Returns:
+    ///     Operation: The Operation at the given index (if it exists).
+    ///
+    /// Raises:
+    ///     IndexError: Index out of range.
+    #[pyo3(text_signature = "($self, index)")]
+    pub fn get(&self, index: usize) -> PyResult<PyObject> {
+        let operation = self
+            .internal
+            .get(index)
+            .ok_or_else(|| PyIndexError::new_err(format!("Index {} out of range", index)))?
+            .clone();
+        convert_operation_to_pyobject(operation)
+    }
+
+    /// Returns a copy of the CircuitDag (produces a deepcopy).
+    ///
+    /// Returns:
+    ///     CircuitDag: A copy of self.
+    #[pyo3(text_signature = "($self)")]
+    pub fn __copy__(&self) -> CircuitDagWrapper {
+        self.clone()
+    }
+
+    /// Return the __richcmp__ magic method to perform rich comparison operations on Circuit.
+    ///
+    /// Args:
+    ///     self: The CircuitDag object.
+    ///     other: The object to compare self to.
+    ///     op: Type of comparison.
+    ///
+    /// Returns:
+    ///     Whether the two operations compared evaluated to True or False.
+    ///
+    /// Raises:
+    ///     NotImplementedError: Other comparison not implemented.
+    fn __richcmp__(&self, other: Py<PyAny>, op: pyo3::class::basic::CompareOp) -> PyResult<bool> {
+        let other = Python::with_gil(|py| -> Result<CircuitDag, QoqoError> {
+            let other_ref = other.as_ref(py);
+            crate::convert_into_circuitdag(other_ref)
+        });
+        match op {
+            pyo3::class::basic::CompareOp::Eq => match other {
+                Ok(dag) => Ok(self.internal == dag),
+                _ => Ok(false),
+            },
+            pyo3::class::basic::CompareOp::Ne => match other {
+                Ok(dag) => Ok(self.internal != dag),
+                _ => Ok(true),
+            },
+            _ => Err(pyo3::exceptions::PyNotImplementedError::new_err(
+                "Other comparison not implemented",
+            )),
+        }
+    }
+
+    /// Return the roqoqo and qoqo versions from when the code was compiled.
+    ///
+    /// Returns:
+    ///     tuple[str, str]: The roqoqo and qoqo versions.
+    #[pyo3(text_signature = "($self)")]
+    fn _qoqo_versions(&self) -> (String, String) {
+        let mut rsplit = ROQOQO_VERSION.split('.').take(2);
+        let mut qsplit = QOQO_VERSION.split('.').take(2);
+        let rver = format!(
+            "{}.{}",
+            rsplit.next().expect("ROQOQO_VERSION badly formatted"),
+            rsplit.next().expect("ROQOQO_VERSION badly formatted")
+        );
+        let qver = format!(
+            "{}.{}",
+            qsplit.next().expect("QOQO_VERSION badly formatted"),
+            qsplit.next().expect("QOQO_VERSION badly formatted")
+        );
+        (rver, qver)
+    }
+
+    /// Return the bincode representation of the CircuitDag using the [bincode] crate.
+    ///
+    /// Returns:
+    ///     ByteArray: The serialized CircuitDag (in [bincode] form).
+    ///
+    /// Raises:
+    ///     ValueError: Cannot serialize CircuitDag to bytes.
+    #[pyo3(text_signature = "($self)")]
+    pub fn to_bincode(&self) -> PyResult<Py<PyByteArray>> {
+        let serialized = serialize(&self.internal)
+            .map_err(|_| PyValueError::new_err("Cannot serialize CircuitDag to bytes"))?;
+        let b: Py<PyByteArray> = Python::with_gil(|py| -> Py<PyByteArray> {
+            PyByteArray::new(py, &serialized[..]).into()
+        });
+        Ok(b)
+    }
+
+    /// Convert the bincode representation of the CircuitDag to a CircuitDag using the [bincode] crate.
+    ///
+    /// Args:
+    ///     input (ByteArray): The serialized CircuitDag (in [bincode] form).
+    ///
+    /// Returns:
+    ///     CircuitDag: The deserialized CircuitDag.
+    ///
+    /// Raises:
+    ///     TypeError: Input cannot be converted to byte array.
+    ///     ValueError: Input cannot be deserialized to CircuitDag.
+    #[staticmethod]
+    #[pyo3(text_signature = "(input)")]
+    pub fn from_bincode(input: &PyAny) -> PyResult<Self> {
+        let bytes = input
+            .extract::<Vec<u8>>()
+            .map_err(|_| PyTypeError::new_err("Input cannot be converted to byte array"))?;
+
+        Ok(Self {
+            internal: deserialize(&bytes[..])
+                .map_err(|_| PyValueError::new_err("Input cannot be deserialized to CircuitDag"))?,
+        })
+    }
+
+    /// Returns the list of nodes of commuting operations in CircuitDag.
+    ///
+    /// Returns:
+    ///     list[int]: The list of nodes of commuting operations.
+    #[pyo3(text_signature = "($self)")]
+    pub fn commuting_operations(&self) -> Vec<usize> {
+        self.internal.commuting_operations().to_vec()
+    }
+
+    /// Returns a set containing the nodes in the first parallel block.
+    ///
+    /// Returns:
+    ///     set[int]: The set of nodes in the first parallel block.
+    #[pyo3(text_signature = "($self)")]
+    pub fn first_parallel_block(&self) -> HashSet<usize> {
+        self.internal.first_parallel_block().clone()
+    }
+
+    /// Returns a set containing the nodes in the last parallel block.
+    ///
+    /// Returns:
+    ///     set[int]: The set of nodes in the last parallel block.
+    #[pyo3(text_signature = "($self)")]
+    pub fn last_parallel_block(&self) -> HashSet<usize> {
+        self.internal.last_parallel_block().clone()
+    }
+
+    /// Returns a dictionary where a key represents a qubit and its value represents
+    /// the first node that involves that qubit.
+    ///
+    /// Returns:
+    ///     dict[int, int]: The dictionary of {qubit: node} elements.
+    #[pyo3(text_signature = "($self)")]
+    pub fn first_operation_involving_qubit(&self) -> PyObject {
+        pyo3::prepare_freethreaded_python();
+        Python::with_gil(|py| -> PyObject {
+            self.internal
+                .first_operation_involving_qubit()
+                .to_object(py)
+        })
+    }
+
+    /// Returns a dictionary where a key represents a qubit and its value represents
+    /// the last node that involves that qubit.
+    ///
+    /// Returns:
+    ///     dict[int, int]: The dictionary of {qubit: node} elements.
+    #[pyo3(text_signature = "($self)")]
+    pub fn last_operation_involving_qubit(&self) -> PyObject {
+        pyo3::prepare_freethreaded_python();
+        Python::with_gil(|py| -> PyObject {
+            self.internal.last_operation_involving_qubit().to_object(py)
+        })
+    }
+
+    /// Returns a dictionary where a key is composed by the name and the size
+    /// of the classical register and its value represents the first node that involves that
+    /// register.
+    ///
+    /// Returns:
+    ///     dict[(str, int), int]: The dictionary of {(str, int), int} elements.
+    #[pyo3(text_signature = "($self)")]
+    pub fn first_operation_involving_classical(&self) -> PyObject {
+        pyo3::prepare_freethreaded_python();
+        Python::with_gil(|py| -> PyObject {
+            self.internal
+                .first_operation_involving_classical()
+                .to_object(py)
+        })
+    }
+
+    /// Returns a dictionary where a key is composed by the name and the size
+    /// of the classical register and its value represents the last node that involves that
+    /// register.
+    ///
+    /// Returns:
+    ///     dict[(str, int), int]: The dictionary of {(str, int), int} elements.
+    #[pyo3(text_signature = "($self)")]
+    pub fn last_operation_involving_classical(&self) -> PyObject {
+        pyo3::prepare_freethreaded_python();
+        Python::with_gil(|py| -> PyObject {
+            self.internal
+                .last_operation_involving_classical()
+                .to_object(py)
+        })
+    }
+}
+
+/// Convert generic python object to [roqoqo::CircuitDag].
+///
+/// Fallible conversion of generic python object to [roqoqo::CircuitDag].
+pub fn convert_into_circuitdag(input: &PyAny) -> Result<CircuitDag, QoqoError> {
+    if let Ok(try_downcast) = input.extract::<CircuitDagWrapper>() {
+        return Ok(try_downcast.internal);
+    }
+    // Everything that follows tries to extract the circuitdag when two separately
+    // compiled python packages are involved
+    let get_version = input
+        .call_method0("_qoqo_versions")
+        .map_err(|_| QoqoError::CannotExtractObject)?;
+    let version = get_version
+        .extract::<(&str, &str)>()
+        .map_err(|_| QoqoError::CannotExtractObject)?;
+    let mut rsplit = ROQOQO_VERSION.split('.').take(2);
+    let mut qsplit = QOQO_VERSION.split('.').take(2);
+    let rver = format!(
+        "{}.{}",
+        rsplit.next().expect("ROQOQO_VERSION badly formatted"),
+        rsplit.next().expect("ROQOQO_VERSION badly formatted")
+    );
+    let qver = format!(
+        "{}.{}",
+        qsplit.next().expect("QOQO_VERSION badly formatted"),
+        qsplit.next().expect("QOQO_VERSION badly formatted")
+    );
+    let test_version: (&str, &str) = (rver.as_str(), qver.as_str());
+    if version == test_version {
+        let get_bytes = input
+            .call_method0("to_bincode")
+            .map_err(|_| QoqoError::CannotExtractObject)?;
+        let bytes = get_bytes
+            .extract::<Vec<u8>>()
+            .map_err(|_| QoqoError::CannotExtractObject)?;
+        deserialize(&bytes[..]).map_err(|_| QoqoError::CannotExtractObject)
+    } else {
+        Err(QoqoError::VersionMismatch)
+    }
+}
diff --git a/qoqo/src/lib.rs b/qoqo/src/lib.rs
index 5c8935dc..518f6cf6 100644
--- a/qoqo/src/lib.rs
+++ b/qoqo/src/lib.rs
@@ -41,6 +41,11 @@ pub use circuit::{convert_into_circuit, CircuitWrapper, OperationIteratorWrapper
 mod quantum_program;
 pub use quantum_program::{convert_into_quantum_program, QuantumProgramWrapper};
 
+#[cfg(feature = "circuitdag")]
+mod circuitdag;
+#[cfg(feature = "circuitdag")]
+pub use circuitdag::{convert_into_circuitdag, CircuitDagWrapper};
+
 /// qoqo version information, used for qoqo import/export checks
 pub const QOQO_VERSION: &str = env!("CARGO_PKG_VERSION");
 
@@ -96,6 +101,7 @@ pub enum QoqoBackendError {
 ///     :toctree: generated/
 ///
 ///     Circuit
+///     CircuitDag
 ///     QuantumProgram
 ///     operations
 ///     measurements
@@ -104,6 +110,8 @@ pub enum QoqoBackendError {
 fn qoqo(_py: Python, module: &PyModule) -> PyResult<()> {
     module.add_class::<CircuitWrapper>()?;
     module.add_class::<QuantumProgramWrapper>()?;
+    #[cfg(feature = "circuitdag")]
+    module.add_class::<CircuitDagWrapper>()?;
     // module.add_class::<GenericChainWrapper>()?;
     // module.add_class::<GenericDeviceWrapper>()?;
     // module.add_class::<AllToAllDeviceWrapper>()?;
diff --git a/roqoqo/Cargo.toml b/roqoqo/Cargo.toml
index d82fe3c4..028eb3b4 100644
--- a/roqoqo/Cargo.toml
+++ b/roqoqo/Cargo.toml
@@ -37,7 +37,7 @@ rand_distr = {version="0.4", optional=true}
 rand = { version = "0.8.4"}
 async-trait = {version = "0.1", optional = true}
 futures ={version = "0.3", optional=true}
-petgraph = "0.6.2"
+petgraph = {version = "0.6.2", optional=true}
 
 [dev-dependencies]
 serde_test = "1.0"
@@ -56,7 +56,8 @@ rustdoc-args = ["--document-private-items" ]
 [features]
 default = ["serialize"]
 dynamic = ["typetag", "dyn-clone"]
-serialize = ["serde", "ndarray/serde", "num-complex/serde", "bincode"]
+serialize = ["serde", "ndarray/serde", "num-complex/serde", "bincode", "petgraph/serde-1"]
 overrotate = [ "rand_distr", "roqoqo-derive/overrotate"]
 async = ["async-trait", "futures"]
 # json_schema=["schemars", "serialize", "qoqo_calculator/json_schema"]
+circuitdag = ["petgraph"]
diff --git a/roqoqo/src/circuitdag.rs b/roqoqo/src/circuitdag.rs
index 937e33ae..d10e1ba5 100644
--- a/roqoqo/src/circuitdag.rs
+++ b/roqoqo/src/circuitdag.rs
@@ -14,13 +14,20 @@ use std::collections::{HashMap, HashSet};
 
 use crate::operations::*;
 use crate::Circuit;
+use crate::{RoqoqoError, RoqoqoVersion};
 
 use petgraph::adj::NodeIndex;
+use petgraph::algo;
+use petgraph::algo::toposort;
 use petgraph::graph::Graph;
+use petgraph::visit::Dfs;
 use petgraph::Directed;
+use petgraph::Direction::{Incoming, Outgoing};
 
 /// Represents the Direct Acyclic Graph (DAG) of a Circuit.
-#[derive(Debug)]
+///
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
 pub struct CircuitDag {
     pub(crate) graph: Graph<Operation, (), Directed, usize>,
     pub(crate) commuting_operations: Vec<NodeIndex<usize>>,
@@ -32,6 +39,28 @@ pub struct CircuitDag {
     pub(crate) last_operation_involving_qubit: HashMap<usize, NodeIndex<usize>>,
     pub(crate) first_operation_involving_classical: HashMap<(String, usize), NodeIndex<usize>>,
     pub(crate) last_operation_involving_classical: HashMap<(String, usize), NodeIndex<usize>>,
+    _roqoqo_version: RoqoqoVersion,
+}
+
+/// Iterator over all possible parallel executable blocks of a Circuit.
+///
+#[derive(Debug)]
+pub struct ParallelBlocks<'a> {
+    dag: &'a CircuitDag,
+    parallel_block: Vec<NodeIndex<usize>>,
+    already_executed: Vec<NodeIndex<usize>>,
+}
+
+impl PartialEq for CircuitDag {
+    fn eq(&self, other: &Self) -> bool {
+        let nodes = |a: &Operation, b: &Operation| a.eq(b);
+        let edges = |_: &(), _: &()| true;
+        algo::is_isomorphic_matching(&self.graph, &other.graph, nodes, edges)
+    }
+
+    fn ne(&self, other: &Self) -> bool {
+        return !self.eq(other);
+    }
 }
 
 impl CircuitDag {
@@ -50,14 +79,15 @@ impl CircuitDag {
             first_operation_involving_classical: HashMap::<(String, usize), NodeIndex<usize>>::new(
             ),
             last_operation_involving_classical: HashMap::<(String, usize), NodeIndex<usize>>::new(),
+            _roqoqo_version: RoqoqoVersion,
         }
     }
 
-    /// Adds an operation to the back of the CircuitDag if necessary.
+    /// Adds an operation to the back of the CircuitDag, if necessary.
     ///
     /// # Arguments
     ///
-    /// * 'operation' - The Operation to add to the end of the CircuitDag.
+    /// * 'operation' - The Operation to add to the back of the CircuitDag.
     ///
     /// # Returns
     ///
@@ -101,6 +131,14 @@ impl CircuitDag {
             for qubit in x {
                 self.update_from_qubit_back(node, qubit);
             }
+            if self
+                .graph
+                .neighbors_directed(node.into(), Incoming)
+                .next()
+                .is_none()
+            {
+                self.first_parallel_block.insert(node);
+            }
         } else if let InvolvedQubits::All = node_involved_qubits {
             self.update_from_all_operation_back(node);
         }
@@ -117,23 +155,25 @@ impl CircuitDag {
         // Update last_operation_involving qubit and last_parallel_block
         //  depending on current structure
         if let Some(&i) = self.last_operation_involving_qubit.get(&qubit) {
-            self.graph.add_edge(i.into(), node.into(), ());
+            self.graph.update_edge(i.into(), node.into(), ());
             self.last_parallel_block.remove(&i);
+        } else if self.last_all.is_some() {
+            self.graph
+                .update_edge(self.last_all.unwrap().into(), node.into(), ());
+            self.last_parallel_block
+                .remove(&self.last_all.unwrap().into());
         }
         let qubit_presence = self.last_operation_involving_qubit.insert(qubit, node);
         self.last_parallel_block.insert(node);
 
         // Update the first layer in case the qubit has never been seen before
         if qubit_presence.is_none() {
-            // Update first_operation_involving_qubit and first_parallel_block
-            //  depending on last_all
+            // Update first_operation_involving_qubit depending on last_all
             if self.last_all.is_none() {
                 self.first_operation_involving_qubit.insert(qubit, node);
-                self.first_parallel_block.insert(node);
             } else {
                 self.first_operation_involving_qubit
                     .insert(qubit, self.last_all.unwrap());
-                self.first_parallel_block.insert(self.last_all.unwrap());
             }
         }
     }
@@ -226,6 +266,14 @@ impl CircuitDag {
             for qubit in x {
                 self.update_from_qubit_front(node, qubit);
             }
+            if self
+                .graph
+                .neighbors_directed(node.into(), Outgoing)
+                .next()
+                .is_none()
+            {
+                self.last_parallel_block.insert(node);
+            }
         } else if let InvolvedQubits::All = node_involved_qubits {
             self.update_from_all_operation_front(node);
         }
@@ -242,23 +290,25 @@ impl CircuitDag {
         // Update first_operation_involving qubit and first_parallel_block
         //  depending on current structure
         if let Some(&i) = self.first_operation_involving_qubit.get(&qubit) {
-            self.graph.add_edge(node.into(), i.into(), ());
+            self.graph.update_edge(node.into(), i.into(), ());
             self.first_parallel_block.remove(&i);
+        } else if self.first_all.is_some() {
+            self.graph
+                .update_edge(node.into(), self.first_all.unwrap().into(), ());
+            self.first_parallel_block
+                .remove(&self.first_all.unwrap().into());
         }
         let qubit_presence = self.first_operation_involving_qubit.insert(qubit, node);
         self.first_parallel_block.insert(node);
 
         // Update the last layer in case the qubit has never been seen before
         if qubit_presence.is_none() {
-            // Update last_operation_involving_qubit and last_parallel_block
-            //  depending on first_all
+            // Update last_operation_involving_qubit depending on first_all
             if self.first_all.is_none() {
                 self.last_operation_involving_qubit.insert(qubit, node);
-                self.last_parallel_block.insert(node);
             } else {
                 self.last_operation_involving_qubit
                     .insert(qubit, self.first_all.unwrap());
-                self.last_parallel_block.insert(self.first_all.unwrap());
             }
         }
     }
@@ -430,9 +480,9 @@ impl CircuitDag {
                 }
             }
             InvolvedClassical::All(x) | InvolvedClassical::AllQubits(x) => {
-                // Cycle first_operation_involving_classical
                 let mut temp_map: HashMap<(String, usize), NodeIndex<usize>> =
                     HashMap::with_capacity(self.first_operation_involving_classical.capacity());
+                // Cycle first_operation_involving_classical
                 for (name, readout) in self.first_operation_involving_classical.keys() {
                     // If the classical register's name in InvolvedClassical::All or ::AllQubits
                     //  is present in first_operation_involving_classical, insert the node
@@ -448,13 +498,156 @@ impl CircuitDag {
         }
     }
 
+    /// Checks if executing an Operation is blocked by any previous not-yet executed Operation.
+    /// The scope of the check is the whole graph.
+    ///
+    /// # Arguments:
+    ///
+    /// * `already_executed` - Slice of NodeIndices of Nodes that have already been executed in the Circuit.
+    /// * `to_be_executed` - NodeIndex of the Operation that should be executed next.
+    ///
+    /// # Returns:
+    ///
+    /// * `Vec<NodeIndex<usize>>` - Vector containing the sorted blocking elements.
+    pub fn execution_blocked(
+        &self,
+        already_executed: &[NodeIndex<usize>],
+        to_be_executed: &NodeIndex<usize>,
+    ) -> Vec<NodeIndex<usize>> {
+        let mut blocking_elements: Vec<NodeIndex<usize>> = vec![];
+        let mut rev_graph: Graph<Operation, (), Directed, usize> = self.graph.clone();
+        rev_graph.reverse();
+        let mut dfs = Dfs::new(&rev_graph, (*to_be_executed).into());
+        dfs.next(&rev_graph);
+
+        // Perform a DFS on the reversed graph starting from to_be_executed,
+        //  pushing on blocking_elements the nodes not contained in already_executed
+        while let Some(nxt) = dfs.next(&rev_graph) {
+            if !already_executed.contains(&nxt.index()) {
+                blocking_elements.push(nxt.index());
+            }
+        }
+
+        blocking_elements.sort_unstable();
+        blocking_elements
+    }
+
+    /// Checks which of the direct predecessors of an Operation in the CircuitDag blocks the execution.
+    ///
+    /// # Warning:
+    ///
+    /// This method can only be used to determine if an operation can be executed when `already_executed` is consistent.
+    /// When the list `already_executed` is inconsistent (an operation is reported as executed that could not have been executed yet)
+    /// this method returning an empty vector does not imply that the `to_be_executed` operation can be executed.
+    ///
+    /// # Arguments:
+    ///
+    /// * `already_executed` - Slice of NodeIndices of Nodes that have already been executed in the Circuit.
+    /// * `to_be_executed` - NodeIndex of the Operation that should be executed next.
+    pub fn blocking_predecessors(
+        &self,
+        already_executed: &[NodeIndex<usize>],
+        to_be_executed: &NodeIndex<usize>,
+    ) -> Vec<NodeIndex<usize>> {
+        let mut blocking_elements: Vec<NodeIndex<usize>> = vec![];
+        let mut neighbor_iter = self
+            .graph
+            .neighbors_directed((*to_be_executed).into(), Incoming);
+
+        while let Some(nxt) = neighbor_iter.next() {
+            if !already_executed.contains(&nxt.index()) {
+                blocking_elements.push(nxt.index());
+            }
+        }
+
+        blocking_elements.sort_unstable();
+        blocking_elements
+    }
+
+    /// Returns a new front layer after executing an operation from the current front layer.
+    /// A front layer is a set of Operations that are ready to be executed, because all required
+    /// predecessors in the graph have already been executed.
+    ///
+    /// Returns an error if operation to be executed is not in the current front layer.
+    ///
+    /// # Arguments:
+    ///
+    /// * `already_executed` - Slice of NodeIndices of Nodes that have already been executed in the Circuit.
+    /// * `current_front_layer` - Slice of NodeIndices in the current front layer ready to be executed if physically possible.
+    /// * `to_be_executed` - NodeIndex of the operation that should be executed next.
+    pub fn new_front_layer(
+        &self,
+        already_executed: &[NodeIndex<usize>],
+        current_front_layer: &[NodeIndex<usize>],
+        to_be_executed: &NodeIndex<usize>,
+    ) -> Result<Vec<NodeIndex<usize>>, RoqoqoError> {
+        // Raise an error if the node to be executed is not in the current
+        //  front layer
+        if !current_front_layer.contains(to_be_executed) {
+            Err(RoqoqoError::GenericError {
+                msg: "The Operation to be executed is not in the current front layer.".to_string(),
+            })
+        } else {
+            let mut current_front_layer = current_front_layer.to_vec();
+            let mut added: bool = false;
+            let mut neighbor_iter = self
+                .graph
+                .neighbors_directed((*to_be_executed).into(), Outgoing);
+
+            // Boolean needed for end of graph case
+            let mut check_emptiness = neighbor_iter.clone();
+            let mut empty: bool = false;
+            if let None = check_emptiness.next() {
+                empty = true;
+            }
+
+            // Already_executed vector extension for execution_blocked() compatibility
+            let mut extended_a_e: Vec<NodeIndex<usize>> = Vec::from(already_executed);
+            extended_a_e.push(*to_be_executed);
+            // Cycle through each neighbor of to_be_executed
+            while let Some(nxt) = neighbor_iter.next() {
+                // Push the neighbor into the current front layer if it is not execution blocked
+                //  by any other node
+                if self
+                    .execution_blocked(&extended_a_e, &nxt.index())
+                    .is_empty()
+                {
+                    current_front_layer.push(nxt.index());
+                    added = true;
+                }
+            }
+            // Remove to_be_executed from the current front layer
+            if added || empty {
+                current_front_layer.remove(
+                    current_front_layer
+                        .iter()
+                        .position(|&x| x == *to_be_executed)
+                        .unwrap(),
+                );
+            }
+            Ok(current_front_layer)
+        }
+    }
+
+    /// Returns an iterator over the possible parallel blocks in circuit that can be executed simultaneously
+    ///
+    /// Returns an Iterator over Vectors of references to the NodeIndices in the parallel block as well
+    /// as references to the Operation in the blocks
+    pub fn parallel_blocks<'a>(&'a self) -> ParallelBlocks<'a> {
+        ParallelBlocks {
+            dag: self,
+            parallel_block: Vec::<NodeIndex<usize>>::new(),
+            already_executed: Vec::<NodeIndex<usize>>::new(),
+        }
+    }
+
     /// Returns a reference to the vector of commuting operations in CircuitDag.
     ///
     pub fn commuting_operations(&self) -> &Vec<usize> {
         &self.commuting_operations
     }
 
-    /// Returns a reference to the HasSet containing the nodes in the first parallel block.
+    /// Returns a reference to the HashSet containing the nodes in the first parallel block.
     ///
     pub fn first_parallel_block(&self) -> &HashSet<usize> {
         &self.first_parallel_block
@@ -495,6 +688,13 @@ impl CircuitDag {
     pub fn last_operation_involving_classical(&self) -> &HashMap<(String, usize), usize> {
         &self.last_operation_involving_classical
     }
+
+    /// Given a NodeIndex, returns a reference to the Operation contained in the node of
+    /// the CircuitDag.
+    ///
+    pub fn get(&self, node: NodeIndex<usize>) -> Option<&Operation> {
+        self.graph.node_weight(node.into())
+    }
 }
 
 /// Creates a new CircuitDag from a given Circuit.
@@ -516,6 +716,7 @@ impl From<Circuit> for CircuitDag {
             first_operation_involving_classical: HashMap::<(String, usize), NodeIndex<usize>>::new(
             ),
             last_operation_involving_classical: HashMap::<(String, usize), NodeIndex<usize>>::new(),
+            _roqoqo_version: RoqoqoVersion,
         };
 
         for operation in circuit.operations() {
@@ -526,6 +727,76 @@ impl From<Circuit> for CircuitDag {
     }
 }
 
+/// Creates a new Circuit from a given CircuitDag.
+///
+impl From<CircuitDag> for Circuit {
+    fn from(dag: CircuitDag) -> Circuit {
+        let mut circuit: Circuit = Circuit::new();
+
+        for com_op in dag.commuting_operations() {
+            let op = dag.get(*com_op).unwrap();
+            circuit.add_operation(op.clone());
+        }
+
+        match toposort(&dag.graph, None) {
+            Ok(order) => {
+                for i in order {
+                    circuit.add_operation(dag.graph.node_weight(i).unwrap().clone());
+                }
+            }
+            Err(_) => {
+                println!("Error: graph not acyclic");
+            }
+        }
+
+        circuit
+    }
+}
+
+impl<'a> Iterator for ParallelBlocks<'a> {
+    type Item = Vec<NodeIndex<usize>>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        // First case
+        if self.parallel_block.is_empty() && self.already_executed.is_empty() {
+            for node in &self.dag.first_parallel_block {
+                self.parallel_block.push(*node);
+            }
+            return Some(self.parallel_block.clone());
+        }
+
+        // Populate already_executed with current parallel_block
+        for node in &self.parallel_block {
+            self.already_executed.push(*node);
+        }
+
+        let mut new_parallel_block: Vec<NodeIndex<usize>> = Vec::new();
+        // Cycle through the current parallel block
+        for node in &self.parallel_block {
+            let mut neighbor_iter = self.dag.graph.neighbors_directed((*node).into(), Outgoing);
+            // Cycle through its neighbors
+            while let Some(nxt) = neighbor_iter.next() {
+                // Add the neighbor to the new parallel block if ready to be executed and has not been pushed before
+                if self
+                    .dag
+                    .execution_blocked(self.already_executed.as_slice(), &nxt.index())
+                    .is_empty()
+                    && !new_parallel_block.iter().any(|id| *id == nxt.index())
+                {
+                    new_parallel_block.push(nxt.index());
+                }
+            }
+        }
+
+        // Update parallel_block and return it
+        self.parallel_block = new_parallel_block.clone();
+        if new_parallel_block.is_empty() {
+            return None;
+        }
+        Some(new_parallel_block)
+    }
+}
+
 /// The following module contains tests that need access to internal data structures.
 ///
 #[cfg(test)]
@@ -693,4 +964,27 @@ mod tests {
 
         assert_eq!(dag.graph.node_count(), op_vec.len());
     }
+
+    #[test_case(vec![Operation::from(CNOT::new(0,1)), Operation::from(PauliX::new(0)), Operation::from(PauliY::new(0)), Operation::from(PauliZ::new(0))])]
+    #[test_case(vec![Operation::from(PauliZ::new(0)), Operation::from(ControlledPauliZ::new(0,1))])]
+    fn test_from_circuitdag(op_vec: Vec<Operation>) {
+        let mut dag: CircuitDag =
+            CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+
+        for op in &op_vec {
+            dag.add_to_back(op.clone());
+        }
+
+        let circuit = Circuit::from(dag.clone());
+
+        assert_eq!(circuit.len(), dag.graph.node_count());
+
+        circuit
+            .iter()
+            .enumerate()
+            .into_iter()
+            .for_each(|(ind, oper)| {
+                assert_eq!(*oper, *dag.graph.node_weight(ind.into()).unwrap());
+            });
+    }
 }
diff --git a/roqoqo/src/lib.rs b/roqoqo/src/lib.rs
index da18dd1e..46c8afc6 100644
--- a/roqoqo/src/lib.rs
+++ b/roqoqo/src/lib.rs
@@ -303,7 +303,9 @@ mod circuit;
 pub use circuit::Circuit;
 #[doc(hidden)]
 pub use circuit::*;
+#[cfg(feature = "circuitdag")]
 mod circuitdag;
+#[cfg(feature = "circuitdag")]
 pub use circuitdag::CircuitDag;
 pub mod backends;
 pub mod devices;


Test patch:
diff --git a/qoqo/tests/integration/circuitdag.rs b/qoqo/tests/integration/circuitdag.rs
new file mode 100644
index 00000000..5b96947a
--- /dev/null
+++ b/qoqo/tests/integration/circuitdag.rs
@@ -0,0 +1,649 @@
+// Copyright © 2021 HQS Quantum Simulations GmbH. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software distributed under the
+// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the License for the specific language governing permissions and
+// limitations under the License.
+
+use pyo3::prelude::*;
+
+use qoqo::operations::convert_operation_to_pyobject;
+use qoqo::{CircuitDagWrapper, CircuitWrapper, QOQO_VERSION};
+
+use roqoqo::operations::*;
+use roqoqo::ROQOQO_VERSION;
+
+// Helper functions
+fn new_circuitdag(py: Python) -> &PyCell<CircuitDagWrapper> {
+    let circuitdag_type = py.get_type::<CircuitDagWrapper>();
+    circuitdag_type
+        .call1((100, 300))
+        .unwrap()
+        .cast_as::<PyCell<CircuitDagWrapper>>()
+        .unwrap()
+}
+
+fn new_circuit(py: Python) -> &PyCell<CircuitWrapper> {
+    let circuit_type = py.get_type::<CircuitWrapper>();
+    circuit_type
+        .call0()
+        .unwrap()
+        .cast_as::<PyCell<CircuitWrapper>>()
+        .unwrap()
+}
+
+/// Test default
+#[test]
+fn test_default() {
+    pyo3::prepare_freethreaded_python();
+    let operation = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+        dag.call_method1("add_to_back", (operation.clone(),))
+            .unwrap();
+        let circuitdag_wrapper = dag.extract::<CircuitDagWrapper>();
+
+        let helper_ne: bool = CircuitDagWrapper::default() != circuitdag_wrapper.unwrap();
+        assert!(helper_ne);
+        let helper_eq: bool = CircuitDagWrapper::default() == CircuitDagWrapper::new(100, 300);
+        assert!(helper_eq);
+    })
+}
+
+/// Test add_to_back and add_to_front
+#[test]
+fn test_add_to() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliY::new(0))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_front", (cnot_01.clone(),))
+            .unwrap();
+        let _circuit_wrapper = dag.extract::<CircuitDagWrapper>();
+    })
+}
+
+/// Test get
+#[test]
+fn test_get() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let pauliy_0 = convert_operation_to_pyobject(Operation::from(PauliY::new(0))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_back", (pauliy_0.clone(),))
+            .unwrap();
+
+        let comp_op = dag.call_method1("get", (0,)).unwrap();
+        let operation = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+
+        let helper1 = bool::extract(comp_op.call_method1("__eq__", (operation,)).unwrap()).unwrap();
+        assert!(helper1);
+
+        let comp_op = dag.call_method1("get", (1,)).unwrap();
+        let operation = convert_operation_to_pyobject(Operation::from(PauliY::new(0))).unwrap();
+
+        let helper2 = bool::extract(comp_op.call_method1("__eq__", (operation,)).unwrap()).unwrap();
+        assert!(helper2);
+    })
+}
+
+/// Test copy
+#[test]
+fn test_copy() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let pauliy_0 = convert_operation_to_pyobject(Operation::from(PauliY::new(0))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_back", (pauliy_0.clone(),))
+            .unwrap();
+        let empty_dag = new_circuitdag(py);
+
+        let dag_copy = dag.call_method0("__copy__").unwrap();
+        let empty_dag_copy = empty_dag.call_method0("__copy__").unwrap();
+
+        let full_dag_comparison =
+            bool::extract(dag_copy.call_method1("__eq__", (&(*dag),)).unwrap()).unwrap();
+        assert!(full_dag_comparison);
+
+        let empty_dag_comparison = bool::extract(
+            empty_dag_copy
+                .call_method1("__eq__", (&(*empty_dag),))
+                .unwrap(),
+        )
+        .unwrap();
+        assert!(empty_dag_comparison);
+    })
+}
+
+/// Test __richcmp__
+#[test]
+fn test_richcmp() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let pauliy_0 = convert_operation_to_pyobject(Operation::from(PauliY::new(0))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    Python::with_gil(|py| {
+        let dag1 = new_circuitdag(py);
+        dag1.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        dag1.call_method1("add_to_back", (pauliy_0.clone(),))
+            .unwrap();
+        let dag2 = new_circuitdag(py);
+        dag2.call_method1("add_to_back", (cnot_01.clone(),))
+            .unwrap();
+        dag2.call_method1("add_to_front", (paulix_0.clone(),))
+            .unwrap();
+        dag2.call_method1("add_to_back", (pauliy_0.clone(),))
+            .unwrap();
+
+        let comparison = bool::extract(dag1.call_method1("__eq__", (dag2,)).unwrap()).unwrap();
+        assert!(!comparison);
+        let comparison = bool::extract(dag1.call_method1("__ne__", (dag2,)).unwrap()).unwrap();
+        assert!(comparison);
+        let comparison = dag1.call_method1("__ge__", (dag2,));
+        assert!(comparison.is_err());
+    })
+}
+
+/// Test qoqo_versions function of Circuit
+#[test]
+fn test_qoqo_versions() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        let mut rsplit = ROQOQO_VERSION.split('.').take(2);
+        let mut qsplit = QOQO_VERSION.split('.').take(2);
+        let rver = format!(
+            "{}.{}",
+            rsplit.next().expect("ROQOQO_VERSION badly formatted"),
+            rsplit.next().expect("ROQOQO_VERSION badly formatted")
+        );
+        let qver = format!(
+            "{}.{}",
+            qsplit.next().expect("QOQO_VERSION badly formatted"),
+            qsplit.next().expect("QOQO_VERSION badly formatted")
+        );
+
+        let comparison_copy: Vec<&str> =
+            Vec::extract(dag.call_method0("_qoqo_versions").unwrap()).unwrap();
+        assert_eq!(comparison_copy, vec![rver.as_str(), qver.as_str()]);
+    })
+}
+
+/// Test to_ and from_bincode functions of CircuitDag
+#[test]
+fn test_to_from_bincode() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+
+        // testing 'to_bincode' and 'from_bincode' functions
+        let serialised = dag.call_method0("to_bincode").unwrap();
+        let new = new_circuitdag(py);
+        let deserialised = new.call_method1("from_bincode", (serialised,)).unwrap();
+        let comparison =
+            bool::extract(deserialised.call_method1("__eq__", (dag,)).unwrap()).unwrap();
+        assert!(comparison);
+
+        let deserialised_error =
+            new.call_method1("from_bincode", (bincode::serialize("fails").unwrap(),));
+        assert!(deserialised_error.is_err());
+
+        let deserialised_error =
+            new.call_method1("from_bincode", (bincode::serialize(&vec![0]).unwrap(),));
+        assert!(deserialised_error.is_err());
+
+        let deserialised_error = deserialised.call_method0("from_bincode");
+        assert!(deserialised_error.is_err());
+
+        let serialised_error = serialised.call_method0("to_bincode");
+        assert!(serialised_error.is_err());
+
+        // testing that 'from_bincode' can be called directly on a circuitdag (python classmethod)
+        let circuitdag_type = py.get_type::<CircuitDagWrapper>();
+        let deserialised_py = circuitdag_type
+            .call_method1("from_bincode", (serialised,))
+            .unwrap();
+
+        let comparison =
+            bool::extract(deserialised_py.call_method1("__eq__", (dag,)).unwrap()).unwrap();
+        assert!(comparison);
+    })
+}
+
+/// Test from_circuit
+#[test]
+fn test_from_circuit() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let pauliy_0 = convert_operation_to_pyobject(Operation::from(PauliY::new(0))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    Python::with_gil(|py| {
+        let circuit = new_circuit(py);
+        circuit.call_method1("add", (paulix_0.clone(),)).unwrap();
+        circuit.call_method1("add", (pauliy_0.clone(),)).unwrap();
+        circuit.call_method1("add", (cnot_01.clone(),)).unwrap();
+
+        let dag = new_circuitdag(py);
+        let dag = dag
+            .call_method1("from_circuit", (circuit,))
+            .unwrap()
+            .cast_as::<PyCell<CircuitDagWrapper>>()
+            .unwrap();
+
+        let comp_op = dag.call_method1("get", (0,)).unwrap();
+        let helper1 = bool::extract(comp_op.call_method1("__eq__", (paulix_0,)).unwrap()).unwrap();
+        assert!(helper1);
+
+        let comp_op = dag.call_method1("get", (1,)).unwrap();
+        let helper2 = bool::extract(comp_op.call_method1("__eq__", (pauliy_0,)).unwrap()).unwrap();
+        assert!(helper2);
+
+        let comp_op = dag.call_method1("get", (2,)).unwrap();
+        let helper3 = bool::extract(comp_op.call_method1("__eq__", (cnot_01,)).unwrap()).unwrap();
+        assert!(helper3);
+    })
+}
+
+#[test]
+fn test_to_circuit() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let pauliy_0 = convert_operation_to_pyobject(Operation::from(PauliY::new(0))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_back", (pauliy_0.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_back", (cnot_01.clone(),)).unwrap();
+
+        let circuit = new_circuit(py);
+        circuit.call_method1("add", (paulix_0.clone(),)).unwrap();
+        circuit.call_method1("add", (pauliy_0.clone(),)).unwrap();
+        circuit.call_method1("add", (cnot_01.clone(),)).unwrap();
+
+        let new_circuit = dag.call_method0("to_circuit").unwrap();
+
+        let comp_op = new_circuit.call_method1("get", (0,)).unwrap();
+        let helper1 = bool::extract(comp_op.call_method1("__eq__", (paulix_0,)).unwrap()).unwrap();
+        assert!(helper1);
+
+        let comp_op = new_circuit.call_method1("get", (1,)).unwrap();
+        let helper2 = bool::extract(comp_op.call_method1("__eq__", (pauliy_0,)).unwrap()).unwrap();
+        assert!(helper2);
+
+        let comp_op = new_circuit.call_method1("get", (2,)).unwrap();
+        let helper3 = bool::extract(comp_op.call_method1("__eq__", (cnot_01,)).unwrap()).unwrap();
+        assert!(helper3);
+    })
+}
+
+#[test]
+fn test_execution_blocked() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let pauliy_1 = convert_operation_to_pyobject(Operation::from(PauliY::new(1))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    let pauliz_0 = convert_operation_to_pyobject(Operation::from(PauliZ::new(0))).unwrap();
+    let cpauliz_12 =
+        convert_operation_to_pyobject(Operation::from(ControlledPauliZ::new(1, 2))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+
+        let a = dag
+            .call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        let b = dag
+            .call_method1("add_to_back", (pauliz_0.clone(),))
+            .unwrap();
+        let c = dag
+            .call_method1("add_to_back", (pauliy_1.clone(),))
+            .unwrap();
+        let d = dag.call_method1("add_to_back", (cnot_01.clone(),)).unwrap();
+        let e = dag
+            .call_method1("add_to_back", (cpauliz_12.clone(),))
+            .unwrap();
+
+        let comp = dag
+            .call_method1("execution_blocked", (vec![a, b, c], d))
+            .unwrap();
+        assert_eq!(comp.len().unwrap(), 0);
+
+        let comp = dag
+            .call_method1("execution_blocked", (vec![a, b, c], e))
+            .unwrap();
+        let helper = bool::extract(comp.call_method1("__eq__", (vec![d],)).unwrap()).unwrap();
+        assert!(helper);
+
+        let comp = dag
+            .call_method1("execution_blocked", (vec![a, b, c, d], e))
+            .unwrap();
+        assert_eq!(comp.len().unwrap(), 0);
+
+        let comp = dag
+            .call_method1("execution_blocked", (vec![d, e], a))
+            .unwrap();
+        assert_eq!(comp.len().unwrap(), 0);
+
+        let comp = dag
+            .call_method1("execution_blocked", (vec![d, e], b))
+            .unwrap();
+        let helper = bool::extract(comp.call_method1("__eq__", (vec![a],)).unwrap()).unwrap();
+        assert!(helper);
+
+        let comp = dag
+            .call_method1("execution_blocked", (Vec::<usize>::new(), e))
+            .unwrap();
+        let helper =
+            bool::extract(comp.call_method1("__eq__", (vec![a, b, c, d],)).unwrap()).unwrap();
+        assert!(helper);
+    })
+}
+
+#[test]
+fn test_blocking_predecessors() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let pauliy_1 = convert_operation_to_pyobject(Operation::from(PauliY::new(1))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    let pauliz_0 = convert_operation_to_pyobject(Operation::from(PauliZ::new(0))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+
+        let a = dag
+            .call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        let b = dag
+            .call_method1("add_to_back", (pauliz_0.clone(),))
+            .unwrap();
+        let c = dag
+            .call_method1("add_to_back", (pauliy_1.clone(),))
+            .unwrap();
+        let d = dag.call_method1("add_to_back", (cnot_01.clone(),)).unwrap();
+
+        let comp = dag
+            .call_method1("blocking_predecessors", (vec![a, b, c], d))
+            .unwrap();
+        assert_eq!(comp.len().unwrap(), 0);
+
+        let comp = dag
+            .call_method1("blocking_predecessors", (vec![a], b))
+            .unwrap();
+        assert_eq!(comp.len().unwrap(), 0);
+
+        let comp = dag
+            .call_method1("blocking_predecessors", (Vec::<usize>::new(), c))
+            .unwrap();
+        assert_eq!(comp.len().unwrap(), 0);
+
+        let comp = dag
+            .call_method1("blocking_predecessors", (vec![a, b], d))
+            .unwrap();
+        let helper = bool::extract(comp.call_method1("__eq__", (vec![c],)).unwrap()).unwrap();
+        assert!(helper);
+    })
+}
+
+#[test]
+fn test_new_front_layer() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let pauliy_1 = convert_operation_to_pyobject(Operation::from(PauliY::new(1))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    let pauliz_0 = convert_operation_to_pyobject(Operation::from(PauliZ::new(0))).unwrap();
+    let cpauliz_12 =
+        convert_operation_to_pyobject(Operation::from(ControlledPauliZ::new(1, 2))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+
+        let a = dag
+            .call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        let b = dag
+            .call_method1("add_to_back", (pauliz_0.clone(),))
+            .unwrap();
+        let c = dag
+            .call_method1("add_to_back", (pauliy_1.clone(),))
+            .unwrap();
+        let d = dag.call_method1("add_to_back", (cnot_01.clone(),)).unwrap();
+        let e = dag
+            .call_method1("add_to_back", (cpauliz_12.clone(),))
+            .unwrap();
+
+        assert!(dag
+            .call_method1("new_front_layer", (vec![a, b, c], vec![d], e))
+            .is_err());
+
+        let comp = dag
+            .call_method1("new_front_layer", (vec![a, c], vec![b], b))
+            .unwrap();
+        let helper = bool::extract(comp.call_method1("__eq__", (vec![d],)).unwrap()).unwrap();
+        assert!(helper);
+
+        let comp = dag
+            .call_method1("new_front_layer", (vec![a], vec![b], b))
+            .unwrap();
+        let helper = bool::extract(comp.call_method1("__eq__", (vec![b],)).unwrap()).unwrap();
+        assert!(helper);
+
+        let comp = dag
+            .call_method1("new_front_layer", (vec![a, b, c], vec![d], d))
+            .unwrap();
+        let helper = bool::extract(comp.call_method1("__eq__", (vec![e],)).unwrap()).unwrap();
+        assert!(helper);
+
+        let comp = dag
+            .call_method1("new_front_layer", (vec![a, b, c, d], vec![e], e))
+            .unwrap();
+        let helper =
+            bool::extract(comp.call_method1("__eq__", (Vec::<usize>::new(),)).unwrap()).unwrap();
+        assert!(helper);
+    })
+}
+
+#[test]
+fn test_parallel_blocks() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let paulix_1 = convert_operation_to_pyobject(Operation::from(PauliX::new(1))).unwrap();
+    let pauliy_1 = convert_operation_to_pyobject(Operation::from(PauliY::new(1))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    let pauliz_0 = convert_operation_to_pyobject(Operation::from(PauliZ::new(0))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_back", (pauliz_0.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_back", (pauliy_1.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_back", (paulix_1.clone(),))
+            .unwrap();
+        dag.call_method1("add_to_back", (cnot_01.clone(),)).unwrap();
+
+        let par_bl = dag.call_method0("parallel_blocks").unwrap();
+
+        let vec0 = par_bl.get_item(0).unwrap();
+        for el in vec0.call0() {
+            let helper1 = bool::extract(
+                el.get_item(0)
+                    .unwrap()
+                    .call_method1("__eq__", (0,))
+                    .unwrap(),
+            )
+            .unwrap();
+            let helper2 = bool::extract(
+                el.get_item(0)
+                    .unwrap()
+                    .call_method1("__eq__", (2,))
+                    .unwrap(),
+            )
+            .unwrap();
+            assert!(helper1 || helper2);
+        }
+
+        let vec1 = par_bl.get_item(1).unwrap();
+        for el in vec1.call0() {
+            let helper1 = bool::extract(
+                el.get_item(0)
+                    .unwrap()
+                    .call_method1("__eq__", (1,))
+                    .unwrap(),
+            )
+            .unwrap();
+            let helper2 = bool::extract(
+                el.get_item(0)
+                    .unwrap()
+                    .call_method1("__eq__", (3,))
+                    .unwrap(),
+            )
+            .unwrap();
+            assert!(helper1 || helper2);
+        }
+
+        let vec2 = par_bl.get_item(2).unwrap();
+        for el in vec2.call0() {
+            let helper1 = bool::extract(
+                el.get_item(0)
+                    .unwrap()
+                    .call_method1("__eq__", (4,))
+                    .unwrap(),
+            )
+            .unwrap();
+            assert!(helper1);
+        }
+    })
+}
+
+#[test]
+fn test_getters_commuting_operations() {
+    pyo3::prepare_freethreaded_python();
+    let commut_op = convert_operation_to_pyobject(Operation::from(
+        PragmaSetNumberOfMeasurements::new(3, "ro".to_string()),
+    ))
+    .unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+
+        let commut_vec = dag.call_method0("commuting_operations").unwrap();
+        assert_eq!(commut_vec.len().unwrap(), 0);
+
+        dag.call_method1("add_to_back", (commut_op.clone(),))
+            .unwrap();
+
+        let commut_vec = dag.call_method0("commuting_operations").unwrap();
+        assert_eq!(commut_vec.len().unwrap(), 1);
+    })
+}
+
+#[test]
+fn test_getters_parallel_blocks() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    let paulix_1 = convert_operation_to_pyobject(Operation::from(PauliX::new(1))).unwrap();
+    let cnot_01 = convert_operation_to_pyobject(Operation::from(CNOT::new(0, 1))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+
+        let fpb = dag.call_method0("first_parallel_block").unwrap();
+        let lpb = dag.call_method0("last_parallel_block").unwrap();
+        assert_eq!(fpb.len().unwrap(), 0);
+        assert_eq!(lpb.len().unwrap(), 0);
+
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+
+        let fpb = dag.call_method0("first_parallel_block").unwrap();
+        let lpb = dag.call_method0("last_parallel_block").unwrap();
+        assert_eq!(fpb.len().unwrap(), 1);
+        assert_eq!(lpb.len().unwrap(), 1);
+
+        dag.call_method1("add_to_back", (cnot_01.clone(),)).unwrap();
+        dag.call_method1("add_to_front", (paulix_1,)).unwrap();
+
+        let fpb = dag.call_method0("first_parallel_block").unwrap();
+        let lpb = dag.call_method0("last_parallel_block").unwrap();
+        assert_eq!(fpb.len().unwrap(), 2);
+        assert_eq!(lpb.len().unwrap(), 1);
+    })
+}
+
+#[test]
+fn test_getters_operation_involving_qubit() {
+    pyo3::prepare_freethreaded_python();
+    let paulix_0 = convert_operation_to_pyobject(Operation::from(PauliX::new(0))).unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+
+        let foiq = dag.call_method0("first_operation_involving_qubit").unwrap();
+        let loiq = dag.call_method0("last_operation_involving_qubit").unwrap();
+        assert_eq!(foiq.len().unwrap(), 0);
+        assert_eq!(loiq.len().unwrap(), 0);
+
+        dag.call_method1("add_to_back", (paulix_0.clone(),))
+            .unwrap();
+
+        let foiq = dag.call_method0("first_operation_involving_qubit").unwrap();
+        let loiq = dag.call_method0("last_operation_involving_qubit").unwrap();
+        assert_eq!(foiq.len().unwrap(), 1);
+        assert_eq!(loiq.len().unwrap(), 1);
+    })
+}
+
+#[test]
+fn test_getters_operations_involving_classical() {
+    pyo3::prepare_freethreaded_python();
+    let meas =
+        convert_operation_to_pyobject(Operation::from(MeasureQubit::new(0, "ro".to_string(), 0)))
+            .unwrap();
+    Python::with_gil(|py| {
+        let dag = new_circuitdag(py);
+
+        dag.call_method1("add_to_back", (meas,)).unwrap();
+
+        let foic = dag
+            .call_method0("first_operation_involving_classical")
+            .unwrap();
+        let loic = dag
+            .call_method0("last_operation_involving_classical")
+            .unwrap();
+        assert_eq!(foic.len().unwrap(), 1);
+        assert_eq!(loic.len().unwrap(), 1);
+    })
+}
+
+#[test]
+fn test_convert_into_circuitdag() {
+    pyo3::prepare_freethreaded_python();
+    let added_op = Operation::from(PauliX::new(0));
+    Python::with_gil(|py| {
+        let operation = convert_operation_to_pyobject(added_op).unwrap();
+
+        let added_dag = new_circuitdag(py);
+        let comparison = added_dag.call_method1("convert_into_circuitdag", (operation,));
+        assert!(comparison.is_err());
+    })
+}
diff --git a/qoqo/tests/integration/main.rs b/qoqo/tests/integration/main.rs
index 78cb3db6..18f58b0f 100644
--- a/qoqo/tests/integration/main.rs
+++ b/qoqo/tests/integration/main.rs
@@ -19,6 +19,10 @@ mod circuit;
 // #[cfg(test)]
 // mod devices;
 
+#[cfg(test)]
+#[cfg(feature = "circuitdag")]
+mod circuitdag;
+
 #[cfg(test)]
 mod quantum_program;
 
diff --git a/roqoqo/tests/integration/circuitdag.rs b/roqoqo/tests/integration/circuitdag.rs
index 799642c8..ece52cfe 100644
--- a/roqoqo/tests/integration/circuitdag.rs
+++ b/roqoqo/tests/integration/circuitdag.rs
@@ -12,7 +12,7 @@
 
 use std::collections::HashSet;
 
-use roqoqo::operations::*;
+use roqoqo::{operations::*, RoqoqoError};
 use roqoqo::{Circuit, CircuitDag};
 
 use test_case::test_case;
@@ -46,6 +46,35 @@ fn add_operation_no_involved_qubits(operation1: Operation, operation2: Operation
     assert_eq!(dag.commuting_operations().get(1), front1.as_ref());
 }
 
+#[test]
+fn test_partial_eq() {
+    let mut dag1: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+    let mut dag2: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+
+    dag1.add_to_back(Operation::from(PauliX::new(0)));
+    dag2.add_to_back(Operation::from(PauliX::new(0)));
+    dag1.add_to_back(Operation::from(PauliY::new(0)));
+    dag2.add_to_back(Operation::from(PauliY::new(0)));
+    dag1.add_to_back(Operation::from(CNOT::new(0, 1)));
+    dag2.add_to_back(Operation::from(CNOT::new(0, 1)));
+
+    assert_eq!(dag1, dag2);
+
+    dag2.add_to_back(Operation::from(PauliZ::new(1)));
+
+    assert_ne!(dag1, dag2);
+
+    let mut dag1: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+    let mut dag2: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+
+    dag1.add_to_back(Operation::from(PauliX::new(0)));
+    dag2.add_to_back(Operation::from(PauliY::new(0)));
+    dag1.add_to_back(Operation::from(PauliY::new(0)));
+    dag2.add_to_back(Operation::from(PauliX::new(0)));
+
+    assert_ne!(dag1, dag2);
+}
+
 #[test_case(Operation::from(PauliX::new(0)), Operation::from(PauliY::new(1)))]
 #[test_case(Operation::from(PauliY::new(1)), Operation::from(PauliZ::new(2)))]
 #[test_case(Operation::from(CNOT::new(0, 1)), Operation::from(PauliX::new(1)))]
@@ -61,7 +90,7 @@ fn check_parallel_blocks_set(operation1: Operation, operation2: Operation) {
     dag.add_to_back(operation1.clone());
 
     assert!(dag.last_parallel_block().len() == 1);
-    assert!(dag.last_parallel_block().len() == 1);
+    assert!(dag.first_parallel_block().len() == 1);
 
     dag.add_to_front(operation2.clone());
     dag.add_to_back(operation1.clone());
@@ -110,8 +139,8 @@ fn check_parallel_blocks_mixed(operation1: Operation, operation2: Operation) {
     )));
     dag.add_to_front(operation2);
 
-    assert!(dag.last_parallel_block().len() == 1);
     assert!(dag.first_parallel_block().len() == 2);
+    assert!(dag.last_parallel_block().len() == 1);
 }
 
 #[test_case(Operation::from(PauliX::new(0)))]
@@ -333,3 +362,105 @@ fn test_pragma_conditional(op_vec: Vec<Operation>) {
     assert!(!dag.first_operation_involving_qubit().is_empty());
     assert!(!dag.last_operation_involving_qubit().is_empty());
 }
+
+#[test_case(Operation::from(PauliX::new(0)))]
+#[test_case(Operation::from(PauliZ::new(1)))]
+#[test_case(Operation::from(CNOT::new(0, 1)))]
+#[test_case(Operation::from(PauliY::new(2)))]
+fn test_get(operation: Operation) {
+    let mut dag: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+
+    let node = dag.add_to_back(operation.clone());
+
+    assert_eq!(dag.get(node.unwrap()).unwrap(), &operation);
+}
+
+#[test]
+fn test_execution_blocked() {
+    let mut dag: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+
+    let a = dag.add_to_back(Operation::from(PauliX::new(0))).unwrap();
+    let b = dag.add_to_back(Operation::from(PauliZ::new(0))).unwrap();
+    let c = dag.add_to_back(Operation::from(PauliY::new(1))).unwrap();
+    let d = dag.add_to_back(Operation::from(CNOT::new(0, 1))).unwrap();
+    let e = dag
+        .add_to_back(Operation::from(ControlledPauliZ::new(1, 2)))
+        .unwrap();
+
+    assert!(dag.execution_blocked(&[a, b, c], &d).is_empty());
+    assert_eq!(dag.execution_blocked(&[a, b, c], &e), vec![d]);
+    assert!(dag.execution_blocked(&[a, b, c, d], &e).is_empty());
+    assert!(dag.execution_blocked(&[d, e], &a).is_empty());
+    assert_eq!(dag.execution_blocked(&[d, e], &b), vec![a]);
+    assert_eq!(dag.execution_blocked(&[], &e), vec![a, b, c, d]);
+}
+
+#[test]
+fn test_blocking_predecessors() {
+    let mut dag: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+
+    let a = dag.add_to_back(Operation::from(PauliX::new(0))).unwrap();
+    let b = dag.add_to_back(Operation::from(PauliZ::new(0))).unwrap();
+    let c = dag.add_to_back(Operation::from(PauliY::new(1))).unwrap();
+    let d = dag.add_to_back(Operation::from(CNOT::new(0, 1))).unwrap();
+
+    assert!(dag.blocking_predecessors(&[a, b, c], &d).is_empty());
+    assert!(dag.blocking_predecessors(&[a], &b).is_empty());
+    assert!(dag.blocking_predecessors(&[], &c).is_empty());
+    assert_eq!(dag.blocking_predecessors(&[a, b], &d), vec![c]);
+}
+
+#[test]
+fn test_new_front_layer() {
+    let mut dag: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+
+    let a = dag.add_to_back(Operation::from(PauliX::new(0))).unwrap();
+    let b = dag.add_to_back(Operation::from(PauliZ::new(0))).unwrap();
+    let c = dag.add_to_back(Operation::from(PauliY::new(1))).unwrap();
+    let d = dag.add_to_back(Operation::from(CNOT::new(0, 1))).unwrap();
+    let e = dag
+        .add_to_back(Operation::from(ControlledPauliZ::new(1, 2)))
+        .unwrap();
+
+    assert_eq!(
+        dag.new_front_layer(&[a, b, c], &[d], &e),
+        Err(RoqoqoError::GenericError {
+            msg: "The Operation to be executed is not in the current front layer.".to_string(),
+        })
+    );
+    assert_eq!(dag.new_front_layer(&[], &[a], &a), Ok(vec![b]));
+    assert_eq!(dag.new_front_layer(&[a, c], &[b], &b), Ok(vec![d]));
+    assert_eq!(dag.new_front_layer(&[a], &[b], &b), Ok(vec![b]));
+    assert_eq!(dag.new_front_layer(&[a, b, c], &[d], &d), Ok(vec![e]));
+    assert_eq!(dag.new_front_layer(&[a, b, c, d], &[e], &e), Ok(vec![]));
+}
+
+#[test]
+fn test_parallel_block_iterator() {
+    let mut dag: CircuitDag = CircuitDag::with_capacity(DEFAULT_NODE_NUMBER, DEFAULT_EDGE_NUMBER);
+
+    let a = dag.add_to_back(Operation::from(PauliX::new(0))).unwrap();
+    let b = dag.add_to_back(Operation::from(PauliZ::new(0))).unwrap();
+    let c = dag.add_to_back(Operation::from(PauliY::new(1))).unwrap();
+    let d = dag.add_to_back(Operation::from(PauliX::new(1))).unwrap();
+    let e = dag.add_to_back(Operation::from(CNOT::new(0, 1))).unwrap();
+
+    let mut par_bl = dag.parallel_blocks();
+
+    let vec = par_bl.next().unwrap();
+    for ind in vec {
+        assert!(ind == a || ind == c);
+    }
+
+    let vec = par_bl.next().unwrap();
+    for ind in vec {
+        assert!(ind == b || ind == d);
+    }
+
+    let vec = par_bl.next().unwrap();
+    for ind in vec {
+        assert!(ind == e);
+    }
+
+    assert!(par_bl.next().is_none());
+}
diff --git a/roqoqo/tests/integration/main.rs b/roqoqo/tests/integration/main.rs
index b0d8d2da..99e471c3 100644
--- a/roqoqo/tests/integration/main.rs
+++ b/roqoqo/tests/integration/main.rs
@@ -26,4 +26,5 @@ mod devices;
 mod quantum_program;
 
 #[cfg(test)]
+#[cfg(feature = "circuitdag")]
 mod circuitdag;