diff --git a/test_files/eccs/nam_4_2.rwr b/test_files/eccs/nam_4_2.rwr index 70e488de..06c9829b 100644 Binary files a/test_files/eccs/nam_4_2.rwr and b/test_files/eccs/nam_4_2.rwr differ diff --git a/test_files/eccs/nam_6_3.rwr b/test_files/eccs/nam_6_3.rwr index 26e64abc..5c80ad86 100644 Binary files a/test_files/eccs/nam_6_3.rwr and b/test_files/eccs/nam_6_3.rwr differ diff --git a/test_files/eccs/small_eccs.rwr b/test_files/eccs/small_eccs.rwr index 7ac61c14..0ea390a3 100644 Binary files a/test_files/eccs/small_eccs.rwr and b/test_files/eccs/small_eccs.rwr differ diff --git a/tket2-eccs/src/tket2_eccs/data/nam_6_3.rwr b/tket2-eccs/src/tket2_eccs/data/nam_6_3.rwr index 26e64abc..5c80ad86 100644 Binary files a/tket2-eccs/src/tket2_eccs/data/nam_6_3.rwr and b/tket2-eccs/src/tket2_eccs/data/nam_6_3.rwr differ diff --git a/tket2-hseries/src/bin/tket2-hseries.rs b/tket2-hseries/src/bin/tket2-hseries.rs index a610603c..a16a1cf9 100644 --- a/tket2-hseries/src/bin/tket2-hseries.rs +++ b/tket2-hseries/src/bin/tket2-hseries.rs @@ -9,7 +9,7 @@ fn main() { CliArgs::GenExtensions(args) => { let reg = ExtensionRegistry::try_new([ tket2::extension::TKET2_EXTENSION.to_owned(), - tket2::extension::angle::ANGLE_EXTENSION.to_owned(), + tket2::extension::rotation::ROTATION_EXTENSION.to_owned(), tket2_hseries::extension::hseries::EXTENSION.to_owned(), tket2_hseries::extension::futures::EXTENSION.to_owned(), tket2_hseries::extension::result::EXTENSION.to_owned(), diff --git a/tket2-hseries/src/extension/hseries.rs b/tket2-hseries/src/extension/hseries.rs index c6e846ab..c4b4d2f4 100644 --- a/tket2-hseries/src/extension/hseries.rs +++ b/tket2-hseries/src/extension/hseries.rs @@ -44,7 +44,6 @@ lazy_static! { futures::EXTENSION.name(), PRELUDE.name(), FLOAT_TYPES.name(), - tket2::extension::angle::ANGLE_EXTENSION.name(), ].into_iter().cloned())); HSeriesOp::load_all_ops(&mut ext).unwrap(); ext @@ -57,7 +56,6 @@ lazy_static! { futures::EXTENSION.to_owned(), PRELUDE.to_owned(), FLOAT_TYPES.to_owned(), - tket2::extension::angle::ANGLE_EXTENSION.to_owned(), ]).unwrap(); } diff --git a/tket2-hseries/src/extension/hseries/lower.rs b/tket2-hseries/src/extension/hseries/lower.rs index 02427c90..5d67f76f 100644 --- a/tket2-hseries/src/extension/hseries/lower.rs +++ b/tket2-hseries/src/extension/hseries/lower.rs @@ -1,22 +1,30 @@ -use std::collections::HashMap; - use hugr::{ algorithms::validation::{ValidatePassError, ValidationLevel}, builder::{BuildError, DFGBuilder, Dataflow, DataflowHugr}, extension::ExtensionRegistry, hugr::{hugrmut::HugrMut, HugrError}, ops::{self, DataflowOpTrait}, - std_extensions::arithmetic::float_types::ConstF64, + std_extensions::arithmetic::{float_ops::FloatOps, float_types::ConstF64}, types::Signature, Hugr, HugrView, Node, Wire, }; +use lazy_static::lazy_static; +use std::collections::HashMap; use strum::IntoEnumIterator; use thiserror::Error; -use tket2::{extension::angle::AngleOpBuilder, Tk2Op}; +use tket2::{extension::rotation::RotationOpBuilder, Tk2Op}; use crate::extension::hseries::{HSeriesOp, HSeriesOpBuilder}; -use super::REGISTRY; +lazy_static! { + /// Extension registry including [crate::extension::hseries::REGISTRY] and + /// [tket2::extension::rotation::ROTATION_EXTENSION]. + pub static ref REGISTRY: ExtensionRegistry = { + let mut registry = super::REGISTRY.to_owned(); + registry.register(tket2::extension::rotation::ROTATION_EXTENSION.to_owned()).unwrap(); + registry + }; +} pub(super) fn pi_mul_f64(builder: &mut T, multiplier: f64) -> Wire { const_f64(builder, multiplier * std::f64::consts::PI) @@ -68,19 +76,19 @@ fn op_to_hugr(op: Tk2Op) -> Result { (Tk2Op::CY, [c, t]) => b.build_cy(*c, *t)?.into(), (Tk2Op::CZ, [c, t]) => b.build_cz(*c, *t)?.into(), (Tk2Op::Rx, [q, angle]) => { - let float = b.add_atorad(*angle)?; + let float = build_to_radians(&mut b, *angle)?; vec![b.build_rx(*q, float)?] } (Tk2Op::Ry, [q, angle]) => { - let float = b.add_atorad(*angle)?; + let float = build_to_radians(&mut b, *angle)?; vec![b.build_ry(*q, float)?] } (Tk2Op::Rz, [q, angle]) => { - let float = b.add_atorad(*angle)?; + let float = build_to_radians(&mut b, *angle)?; vec![b.add_rz(*q, float)?] } (Tk2Op::CRz, [c, t, angle]) => { - let float = b.add_atorad(*angle)?; + let float = build_to_radians(&mut b, *angle)?; b.build_crz(*c, *t, float)?.into() } (Tk2Op::Toffoli, [a, b_, c]) => b.build_toffoli(*a, *b_, *c)?.into(), @@ -89,6 +97,13 @@ fn op_to_hugr(op: Tk2Op) -> Result { Ok(b.finish_hugr_with_outputs(outputs, ®ISTRY)?) } +fn build_to_radians(b: &mut DFGBuilder, rotation: Wire) -> Result { + let turns = b.add_to_halfturns(rotation)?; + let pi = pi_mul_f64(b, 1.0); + let float = b.add_dataflow_op(FloatOps::fmul, [turns, pi])?.out_wire(0); + Ok(float) +} + /// Lower `Tk2Op` operations to `HSeriesOp` operations. pub fn lower_tk2_op(hugr: &mut impl HugrMut) -> Result, LowerTk2Error> { let replaced_nodes = lower_direct(hugr)?; @@ -179,7 +194,7 @@ impl LowerTket2ToHSeriesPass { #[cfg(test)] mod test { use hugr::{builder::FunctionBuilder, type_row, HugrView}; - use tket2::{extension::angle::ANGLE_TYPE, Circuit}; + use tket2::{extension::rotation::ROTATION_TYPE, Circuit}; use super::*; use rstest::rstest; @@ -251,7 +266,7 @@ mod test { #[test] fn test_mixed() { - let mut b = DFGBuilder::new(Signature::new(type_row![ANGLE_TYPE], type_row![])).unwrap(); + let mut b = DFGBuilder::new(Signature::new(type_row![ROTATION_TYPE], type_row![])).unwrap(); let [angle] = b.input_wires_arr(); let [q] = b.add_dataflow_op(Tk2Op::QAlloc, []).unwrap().outputs_arr(); let [q] = b.add_dataflow_op(Tk2Op::H, [q]).unwrap().outputs_arr(); @@ -264,8 +279,8 @@ mod test { let lowered = lower_tk2_op(&mut h).unwrap(); assert_eq!(lowered.len(), 4); - // dfg, input, output, alloc, phasedx, rz, atorad, phasedx, free + 4x(float + load) - assert_eq!(h.node_count(), 17); + // dfg, input, output, alloc, phasedx, rz, toturns, fmul, phasedx, free + 5x(float + load) + assert_eq!(h.node_count(), 20); assert_eq!(check_lowered(&h), Ok(())); } } diff --git a/tket2-py/tket2/extensions/_json_defs/tket2/angle.json b/tket2-py/tket2/extensions/_json_defs/tket2/angle.json deleted file mode 100644 index fba13689..00000000 --- a/tket2-py/tket2/extensions/_json_defs/tket2/angle.json +++ /dev/null @@ -1,396 +0,0 @@ -{ - "version": "0.1.0", - "name": "tket2.angle", - "extension_reqs": [], - "types": { - "angle": { - "extension": "tket2.angle", - "name": "angle", - "params": [], - "description": "angle type expressed as dyadic rational multiples of 2π", - "bound": { - "b": "Explicit", - "bound": "C" - } - } - }, - "values": {}, - "operations": { - "aadd": { - "extension": "tket2.angle", - "name": "aadd", - "description": "addition of angles", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - }, - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "output": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "adiv": { - "extension": "tket2.angle", - "name": "adiv", - "description": "Divide angle by an integer. If the integer is not a power of 2, or if the resulting denominator would exceed 2^64, the result is rounded to the nearest multiple of 2 pi / 2^ 64", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - }, - { - "t": "I" - } - ], - "output": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "aeq": { - "extension": "tket2.angle", - "name": "aeq", - "description": "check angle equality", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - }, - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "output": [ - { - "t": "Sum", - "s": "Unit", - "size": 2 - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "afromrad": { - "extension": "tket2.angle", - "name": "afromrad", - "description": "construct angle from radians, rounding given a log-denominator", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "I" - }, - { - "t": "Opaque", - "extension": "arithmetic.float.types", - "id": "float64", - "args": [], - "bound": "C" - } - ], - "output": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "amul": { - "extension": "tket2.angle", - "name": "amul", - "description": "multiply angle by a scalar", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - }, - { - "t": "I" - } - ], - "output": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "aneg": { - "extension": "tket2.angle", - "name": "aneg", - "description": "negation of an angle", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "output": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "anew": { - "extension": "tket2.angle", - "name": "anew", - "description": "construct angle from numerator and log-denominator, returning an error if invalid", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "I" - }, - { - "t": "I" - } - ], - "output": [ - { - "t": "Sum", - "s": "General", - "rows": [ - [ - { - "t": "Opaque", - "extension": "prelude", - "id": "error", - "args": [], - "bound": "C" - } - ], - [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ] - ] - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "aparts": { - "extension": "tket2.angle", - "name": "aparts", - "description": "decompose angle into numerator and log-denominator", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "output": [ - { - "t": "I" - }, - { - "t": "I" - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "asub": { - "extension": "tket2.angle", - "name": "asub", - "description": "subtraction of the second angle from the first", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - }, - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "output": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "atorad": { - "extension": "tket2.angle", - "name": "atorad", - "description": "convert angle to radians", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "output": [ - { - "t": "Opaque", - "extension": "arithmetic.float.types", - "id": "float64", - "args": [], - "bound": "C" - } - ], - "extension_reqs": [] - } - }, - "binary": false - }, - "atrunc": { - "extension": "tket2.angle", - "name": "atrunc", - "description": "truncate an angle to one with a lower log-denominator with the nearest value, rounding down in [0, 2π) if necessary", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - }, - { - "t": "I" - } - ], - "output": [ - { - "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", - "args": [], - "bound": "C" - } - ], - "extension_reqs": [] - } - }, - "binary": false - } - } -} diff --git a/tket2-py/tket2/extensions/_json_defs/tket2/hseries.json b/tket2-py/tket2/extensions/_json_defs/tket2/hseries.json index 700ddefa..3e94fc0f 100644 --- a/tket2-py/tket2/extensions/_json_defs/tket2/hseries.json +++ b/tket2-py/tket2/extensions/_json_defs/tket2/hseries.json @@ -4,7 +4,6 @@ "extension_reqs": [ "arithmetic.float.types", "prelude", - "tket2.angle", "tket2.futures" ], "types": {}, diff --git a/tket2-py/tket2/extensions/_json_defs/tket2/quantum.json b/tket2-py/tket2/extensions/_json_defs/tket2/quantum.json index bf48f5f2..5e1b36ea 100644 --- a/tket2-py/tket2/extensions/_json_defs/tket2/quantum.json +++ b/tket2-py/tket2/extensions/_json_defs/tket2/quantum.json @@ -24,8 +24,8 @@ }, { "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", + "extension": "tket2.rotation", + "id": "rotation", "args": [], "bound": "C" } @@ -302,8 +302,8 @@ }, { "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", + "extension": "tket2.rotation", + "id": "rotation", "args": [], "bound": "C" } @@ -334,8 +334,8 @@ }, { "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", + "extension": "tket2.rotation", + "id": "rotation", "args": [], "bound": "C" } @@ -371,8 +371,8 @@ }, { "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", + "extension": "tket2.rotation", + "id": "rotation", "args": [], "bound": "C" } @@ -649,8 +649,8 @@ "output": [ { "t": "Opaque", - "extension": "tket2.angle", - "id": "angle", + "extension": "tket2.rotation", + "id": "rotation", "args": [], "bound": "C" } diff --git a/tket2-py/tket2/extensions/_json_defs/tket2/rotation.json b/tket2-py/tket2/extensions/_json_defs/tket2/rotation.json new file mode 100644 index 00000000..ce80d4ea --- /dev/null +++ b/tket2-py/tket2/extensions/_json_defs/tket2/rotation.json @@ -0,0 +1,126 @@ +{ + "version": "0.1.0", + "name": "tket2.rotation", + "extension_reqs": [], + "types": { + "rotation": { + "extension": "tket2.rotation", + "name": "rotation", + "params": [], + "description": "rotation type expressed as number of half turns", + "bound": { + "b": "Explicit", + "bound": "C" + } + } + }, + "values": {}, + "operations": { + "from_halfturns": { + "extension": "tket2.rotation", + "name": "from_halfturns", + "description": "Construct rotation from number of half-turns (would be multiples of π in radians).", + "signature": { + "params": [], + "body": { + "input": [ + { + "t": "Opaque", + "extension": "arithmetic.float.types", + "id": "float64", + "args": [], + "bound": "C" + } + ], + "output": [ + { + "t": "Sum", + "s": "General", + "rows": [ + [], + [ + { + "t": "Opaque", + "extension": "tket2.rotation", + "id": "rotation", + "args": [], + "bound": "C" + } + ] + ] + } + ], + "extension_reqs": [] + } + }, + "binary": false + }, + "radd": { + "extension": "tket2.rotation", + "name": "radd", + "description": "Add two angles together (experimental).", + "signature": { + "params": [], + "body": { + "input": [ + { + "t": "Opaque", + "extension": "tket2.rotation", + "id": "rotation", + "args": [], + "bound": "C" + }, + { + "t": "Opaque", + "extension": "tket2.rotation", + "id": "rotation", + "args": [], + "bound": "C" + } + ], + "output": [ + { + "t": "Opaque", + "extension": "tket2.rotation", + "id": "rotation", + "args": [], + "bound": "C" + } + ], + "extension_reqs": [] + } + }, + "binary": false + }, + "to_halfturns": { + "extension": "tket2.rotation", + "name": "to_halfturns", + "description": "Convert rotation to number of half-turns (would be multiples of π in radians).", + "signature": { + "params": [], + "body": { + "input": [ + { + "t": "Opaque", + "extension": "tket2.rotation", + "id": "rotation", + "args": [], + "bound": "C" + } + ], + "output": [ + { + "t": "Opaque", + "extension": "arithmetic.float.types", + "id": "float64", + "args": [], + "bound": "C" + } + ], + "extension_reqs": [] + } + }, + "binary": false + } + } +} diff --git a/tket2/src/circuit.rs b/tket2/src/circuit.rs index d54ef645..7f4a332c 100644 --- a/tket2/src/circuit.rs +++ b/tket2/src/circuit.rs @@ -631,7 +631,7 @@ mod tests { }; use super::*; - use crate::extension::angle::ConstAngle; + use crate::extension::rotation::ConstRotation; use crate::serialize::load_tk1_json_str; use crate::utils::{build_module_with_circuit, build_simple_circuit}; use crate::Tk2Op; @@ -661,7 +661,7 @@ mod tests { build_simple_circuit(2, |circ| { circ.append(Tk2Op::H, [0])?; circ.append(Tk2Op::CX, [0, 1])?; - let angle = circ.add_constant(ConstAngle::PI_2); + let angle = circ.add_constant(ConstRotation::PI_2); circ.append_and_consume( Tk2Op::Rz, [CircuitUnit::Linear(1), CircuitUnit::Wire(angle)], diff --git a/tket2/src/circuit/command.rs b/tket2/src/circuit/command.rs index 6fc68f11..901505e5 100644 --- a/tket2/src/circuit/command.rs +++ b/tket2/src/circuit/command.rs @@ -485,7 +485,7 @@ mod test { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; - use crate::extension::angle::ConstAngle; + use crate::extension::rotation::ConstRotation; use crate::extension::REGISTRY; use crate::utils::{build_module_with_circuit, build_simple_circuit}; use crate::Tk2Op; @@ -590,7 +590,7 @@ mod test { let mut h = DFGBuilder::new(Signature::new(qb_row.clone(), qb_row)).unwrap(); let [q_in] = h.input_wires_arr(); - let constant = h.add_constant(Value::extension(ConstAngle::PI_2)); + let constant = h.add_constant(Value::extension(ConstRotation::PI_2)); let loaded_const = h.load_const(&constant); let rz = h.add_dataflow_op(Tk2Op::Rz, [q_in, loaded_const]).unwrap(); @@ -605,10 +605,7 @@ mod test { // First command is the constant definition. // It has a single output. let const_cmd = commands.next().unwrap(); - assert_eq!( - const_cmd.optype().name().as_str(), - "const:custom:a(2π*1/2^2)" - ); + assert_eq!(const_cmd.optype().name().as_str(), "const:custom:a(π*0.5)"); assert_eq_iter!(const_cmd.inputs().map(|(u, _, _)| u), [],); assert_eq_iter!( const_cmd.outputs().map(|(u, _, _)| u), diff --git a/tket2/src/extension.rs b/tket2/src/extension.rs index 7b0fb166..dc60295b 100644 --- a/tket2/src/extension.rs +++ b/tket2/src/extension.rs @@ -4,7 +4,6 @@ use crate::serialize::pytket::OpaqueTk1Op; use crate::Tk2Op; -use angle::ANGLE_TYPE; use hugr::extension::prelude::PRELUDE; use hugr::extension::simple_op::MakeOpDef; use hugr::extension::{ @@ -16,10 +15,11 @@ use hugr::types::type_param::{TypeArg, TypeParam}; use hugr::types::{CustomType, PolyFuncType, PolyFuncTypeRV, Signature}; use hugr::{type_row, Extension}; use lazy_static::lazy_static; +use rotation::ROTATION_TYPE; use smol_str::SmolStr; /// Definition for Angle ops and types. -pub mod angle; +pub mod rotation; /// The ID of the TKET1 extension. pub const TKET1_EXTENSION_ID: ExtensionId = IdentList::new_unchecked("TKET1"); @@ -58,7 +58,7 @@ pub static ref REGISTRY: ExtensionRegistry = ExtensionRegistry::try_new([ PRELUDE.to_owned(), TKET2_EXTENSION.to_owned(), FLOAT_TYPES.to_owned(), - angle::ANGLE_EXTENSION.to_owned() + rotation::ROTATION_EXTENSION.to_owned() ]).unwrap(); @@ -112,7 +112,7 @@ pub static ref TKET2_EXTENSION: Extension = { e.add_op( SYM_OP_ID, "Store a sympy expression that can be evaluated to an angle.".to_string(), - PolyFuncType::new(vec![TypeParam::String], Signature::new(type_row![], type_row![ANGLE_TYPE])), + PolyFuncType::new(vec![TypeParam::String], Signature::new(type_row![], type_row![ROTATION_TYPE])), ) .unwrap(); e diff --git a/tket2/src/extension/angle.rs b/tket2/src/extension/angle.rs deleted file mode 100644 index 7acb6a83..00000000 --- a/tket2/src/extension/angle.rs +++ /dev/null @@ -1,422 +0,0 @@ -use hugr::builder::{BuildError, Dataflow}; -use hugr::extension::prelude::{sum_with_error, BOOL_T, USIZE_T}; -use hugr::extension::simple_op::{MakeOpDef, MakeRegisteredOp}; -use hugr::extension::{ExtensionId, ExtensionSet, Version}; -use hugr::ops::constant::{downcast_equal_consts, CustomConst}; -use hugr::std_extensions::arithmetic::float_types::FLOAT64_TYPE; -use hugr::{type_row, Wire}; -use hugr::{ - types::{ConstTypeError, CustomType, Signature, Type, TypeBound}, - Extension, -}; -use smol_str::SmolStr; -use std::f64::consts::TAU; -use strum::{EnumIter, EnumString, IntoStaticStr}; - -use lazy_static::lazy_static; - -/// Name of tket 2 angle extension. -pub const ANGLE_EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("tket2.angle"); - -/// Current version of the TKET 2 angle extension -pub const ANGLE_EXTENSION_VERSION: Version = Version::new(0, 1, 0); - -lazy_static! { - /// The extension definition for TKET2 angle type and ops. - pub static ref ANGLE_EXTENSION: Extension = { - let mut e = Extension::new(ANGLE_EXTENSION_ID, ANGLE_EXTENSION_VERSION); - add_to_extension(&mut e); - e - }; -} - -/// Identifier for the angle type. -const ANGLE_TYPE_ID: SmolStr = SmolStr::new_inline("angle"); -/// Dyadic rational angle type (as [CustomType]) -pub const ANGLE_CUSTOM_TYPE: CustomType = - CustomType::new_simple(ANGLE_TYPE_ID, ANGLE_EXTENSION_ID, TypeBound::Copyable); - -/// Type representing an angle that is a dyadic rational multiple of 2π (as [Type]) -pub const ANGLE_TYPE: Type = Type::new_extension(ANGLE_CUSTOM_TYPE); - -/// The largest permitted log-denominator. -pub const LOG_DENOM_MAX: u8 = 53; - -const fn is_valid_log_denom(n: u8) -> bool { - n <= LOG_DENOM_MAX -} - -/// An angle -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct ConstAngle { - log_denom: u8, - value: u64, -} - -impl ConstAngle { - /// The constant π - pub const PI: Self = Self::new_unchecked(1, 1); - /// The constant 2π - pub const TAU: Self = Self::new_unchecked(0, 0); - /// The constant π/2 - pub const PI_2: Self = Self::new_unchecked(2, 1); - /// The constant π/4 - pub const PI_4: Self = Self::new_unchecked(3, 1); - - /// Create a new [`ConstAngle`] from a log-denominator and a numerator without - /// checking for validity. - const fn new_unchecked(log_denom: u8, value: u64) -> Self { - Self { log_denom, value } - } - /// Create a new [`ConstAngle`] from a log-denominator and a numerator - pub fn new(log_denom: u8, value: u64) -> Result { - if !is_valid_log_denom(log_denom) { - return Err(ConstTypeError::CustomCheckFail( - hugr::types::CustomCheckFailure::Message( - "Invalid angle log-denominator.".to_owned(), - ), - )); - } - if value >= (1u64 << log_denom) { - return Err(ConstTypeError::CustomCheckFail( - hugr::types::CustomCheckFailure::Message( - "Invalid unsigned integer value.".to_owned(), - ), - )); - } - Ok(Self { log_denom, value }) - } - - /// Create a new [`ConstAngle`] from a log-denominator and a floating-point value in radians, - /// rounding to the nearest corresponding value. (Ties round away from zero.) - pub fn from_radians_rounding(log_denom: u8, theta: f64) -> Result { - if !is_valid_log_denom(log_denom) { - return Err(ConstTypeError::CustomCheckFail( - hugr::types::CustomCheckFailure::Message( - "Invalid angle log-denominator.".to_owned(), - ), - )); - } - let a = (((1u64 << log_denom) as f64) * theta / TAU).round() as i64; - Ok(Self { - log_denom, - value: a.rem_euclid(1i64 << log_denom) as u64, - }) - } - - /// Create a new [`ConstAngle`] from a floating-point value in radians, - /// using the highest possible log-denominator and - /// rounding to the nearest corresponding value. (Ties round away from zero.) - pub fn from_radians_rounding_max(theta: f64) -> Result { - Self::from_radians_rounding(LOG_DENOM_MAX, theta) - } - - /// Returns the value of the constant - pub fn value(&self) -> u64 { - self.value - } - - /// Returns the log-denominator of the constant - pub fn log_denom(&self) -> u8 { - self.log_denom - } - - /// Returns the value of the constant in radians - pub fn to_radians(&self) -> f64 { - self.to_turns() * TAU - } - - /// Returns the value of the constant divided by 2π - pub fn to_turns(&self) -> f64 { - (self.value as f64) / (1u64 << self.log_denom) as f64 - } -} - -#[typetag::serde] -impl CustomConst for ConstAngle { - fn name(&self) -> SmolStr { - format!("a(2π*{}/2^{})", self.value, self.log_denom).into() - } - - fn get_type(&self) -> Type { - ANGLE_TYPE - } - - fn equal_consts(&self, other: &dyn CustomConst) -> bool { - downcast_equal_consts(self, other) - } - fn extension_reqs(&self) -> ExtensionSet { - ExtensionSet::singleton(&ANGLE_EXTENSION_ID) - } -} - -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, EnumIter, IntoStaticStr, EnumString)] -#[allow(missing_docs, non_camel_case_types)] -#[non_exhaustive] -/// Angle operations -pub enum AngleOp { - /// Truncate an angle to one with a lower log-denominator with the nearest value, rounding down in [0, 2π) if necessary - atrunc, - /// Addition of angles - aadd, - /// Subtraction of the second angle from the first - asub, - /// Negation of an angle - aneg, - /// Construct angle from numerator and log-denominator - anew, - /// Decompose angle into numerator and log-denominator - aparts, - /// Construct angle from radians - afromrad, - /// Convert angle to radians - atorad, - /// Check angle equality - aeq, - /// Multiply angle by a scalar - amul, - /// Divide by scalar with rounding - adiv, -} - -impl MakeOpDef for AngleOp { - fn from_def( - op_def: &hugr::extension::OpDef, - ) -> Result - where - Self: Sized, - { - hugr::extension::simple_op::try_from_name(op_def.name(), op_def.extension()) - } - - fn signature(&self) -> hugr::extension::SignatureFunc { - match self { - AngleOp::atrunc => { - Signature::new(type_row![ANGLE_TYPE, USIZE_T], type_row![ANGLE_TYPE]) - } - AngleOp::aadd | AngleOp::asub => { - Signature::new(type_row![ANGLE_TYPE, ANGLE_TYPE], type_row![ANGLE_TYPE]) - } - AngleOp::aneg => Signature::new_endo(type_row![ANGLE_TYPE]), - AngleOp::anew => Signature::new( - type_row![USIZE_T, USIZE_T], - vec![sum_with_error(ANGLE_TYPE).into()], - ), - AngleOp::aparts => Signature::new(type_row![ANGLE_TYPE], type_row![USIZE_T, USIZE_T]), - AngleOp::afromrad => { - Signature::new(type_row![USIZE_T, FLOAT64_TYPE], type_row![ANGLE_TYPE]) - } - AngleOp::atorad => Signature::new(type_row![ANGLE_TYPE], type_row![FLOAT64_TYPE]), - AngleOp::aeq => Signature::new(type_row![ANGLE_TYPE, ANGLE_TYPE], type_row![BOOL_T]), - AngleOp::amul | AngleOp::adiv => { - Signature::new(type_row![ANGLE_TYPE, USIZE_T], type_row![ANGLE_TYPE]) - } - } - .into() - } - - fn description(&self) -> String { - match self { - AngleOp::atrunc => "truncate an angle to one with a lower log-denominator with the nearest value, rounding down in [0, 2π) if necessary", - AngleOp::aadd => "addition of angles", - AngleOp::asub => "subtraction of the second angle from the first", - AngleOp::aneg => "negation of an angle", - AngleOp::anew => "construct angle from numerator and log-denominator, returning an error if invalid", - AngleOp::aparts => "decompose angle into numerator and log-denominator", - AngleOp::afromrad => "construct angle from radians, rounding given a log-denominator", - AngleOp::atorad => "convert angle to radians", - AngleOp::aeq => "check angle equality", - AngleOp::amul => "multiply angle by a scalar", - AngleOp::adiv => "Divide angle by an integer. If the integer is not a power of 2, or if the resulting denominator would exceed 2^64, the result is rounded to the nearest multiple of 2 pi / 2^ 64", - }.to_owned() - } - - fn extension(&self) -> hugr::extension::ExtensionId { - ANGLE_EXTENSION_ID - } - - // TODO constant folding - // https://github.com/CQCL/tket2/issues/405 -} - -impl MakeRegisteredOp for AngleOp { - fn extension_id(&self) -> hugr::extension::ExtensionId { - ANGLE_EXTENSION_ID - } - - fn registry<'s, 'r: 's>(&'s self) -> &'r hugr::extension::ExtensionRegistry { - &super::REGISTRY - } -} - -pub(super) fn add_to_extension(extension: &mut Extension) { - extension - .add_type( - ANGLE_TYPE_ID, - vec![], - "angle type expressed as dyadic rational multiples of 2π".to_owned(), - TypeBound::Copyable.into(), - ) - .unwrap(); - - AngleOp::load_all_ops(extension).expect("add fail"); -} - -/// An extension trait for [Dataflow] providing methods to add -/// "tket2.angle" operations. -pub trait AngleOpBuilder: Dataflow { - /// Add a "tket2.angle.atrunc" op. - fn add_atrunc(&mut self, angle: Wire, log_denom: Wire) -> Result { - Ok(self - .add_dataflow_op(AngleOp::atrunc, [angle, log_denom])? - .out_wire(0)) - } - /// Add a "tket2.angle.aadd" op. - fn add_aadd(&mut self, angle1: Wire, angle2: Wire) -> Result { - Ok(self - .add_dataflow_op(AngleOp::aadd, [angle1, angle2])? - .out_wire(0)) - } - - /// Add a "tket2.angle.asub" op. - fn add_asub(&mut self, angle1: Wire, angle2: Wire) -> Result { - Ok(self - .add_dataflow_op(AngleOp::asub, [angle1, angle2])? - .out_wire(0)) - } - - /// Add a "tket2.angle.aneg" op. - fn add_aneg(&mut self, angle: Wire) -> Result { - Ok(self.add_dataflow_op(AngleOp::aneg, [angle])?.out_wire(0)) - } - - /// Add a "tket2.angle.anew" op. - fn add_anew(&mut self, numerator: Wire, log_denominator: Wire) -> Result { - Ok(self - .add_dataflow_op(AngleOp::anew, [numerator, log_denominator])? - .out_wire(0)) - } - - /// Add a "tket2.angle.aparts" op. - fn add_aparts(&mut self, angle: Wire) -> Result<[Wire; 2], BuildError> { - Ok(self - .add_dataflow_op(AngleOp::aparts, [angle])? - .outputs_arr()) - } - - /// Add a "tket2.angle.afromrad" op. - fn add_afromrad(&mut self, log_denominator: Wire, radians: Wire) -> Result { - Ok(self - .add_dataflow_op(AngleOp::afromrad, [log_denominator, radians])? - .out_wire(0)) - } - - /// Add a "tket2.angle.atorad" op. - fn add_atorad(&mut self, angle: Wire) -> Result { - Ok(self.add_dataflow_op(AngleOp::atorad, [angle])?.out_wire(0)) - } - - /// Add a "tket2.angle.aeq" op. - fn add_aeq(&mut self, angle1: Wire, angle2: Wire) -> Result { - Ok(self - .add_dataflow_op(AngleOp::aeq, [angle1, angle2])? - .out_wire(0)) - } - - /// Add a "tket2.angle.amul" op. - fn add_amul(&mut self, angle: Wire, scalar: Wire) -> Result { - Ok(self - .add_dataflow_op(AngleOp::amul, [angle, scalar])? - .out_wire(0)) - } - - /// Add a "tket2.angle.adiv" op. - fn add_adiv(&mut self, angle: Wire, scalar: Wire) -> Result { - Ok(self - .add_dataflow_op(AngleOp::adiv, [angle, scalar])? - .out_wire(0)) - } -} - -impl AngleOpBuilder for D {} - -#[cfg(test)] -mod test { - use hugr::{ - builder::{DFGBuilder, DataflowHugr}, - ops::OpType, - }; - use strum::IntoEnumIterator; - - use crate::extension::REGISTRY; - - use super::*; - - #[test] - fn test_angle_consts() { - let const_a32_7 = ConstAngle::new(5, 7).unwrap(); - let const_a33_7 = ConstAngle::new(6, 7).unwrap(); - let const_a32_8 = ConstAngle::new(6, 8).unwrap(); - assert_ne!(const_a32_7, const_a33_7); - assert_ne!(const_a32_7, const_a32_8); - assert_eq!(const_a32_7, ConstAngle::new(5, 7).unwrap()); - - assert_eq!(const_a32_7.get_type(), ANGLE_TYPE); - assert!(matches!( - ConstAngle::new(3, 256), - Err(ConstTypeError::CustomCheckFail(_)) - )); - assert!(matches!( - ConstAngle::new(54, 256), - Err(ConstTypeError::CustomCheckFail(_)) - )); - let const_af1 = ConstAngle::from_radians_rounding(5, 0.21874 * TAU).unwrap(); - assert_eq!(const_af1.value(), 7); - assert_eq!(const_af1.log_denom(), 5); - - assert!(ConstAngle::from_radians_rounding(54, 0.21874 * TAU).is_err()); - - assert!(const_a32_7.equal_consts(&ConstAngle::new(5, 7).unwrap())); - assert_ne!(const_a32_7, const_a33_7); - - assert_eq!(const_a32_8.name(), "a(2π*8/2^6)"); - } - - #[test] - fn test_ops() { - let ops = AngleOp::iter().collect::>(); - for op in ops { - let optype: OpType = op.into(); - assert_eq!(optype.cast(), Some(op)); - } - } - - #[test] - fn test_builder() { - let mut builder = - DFGBuilder::new(Signature::new(vec![ANGLE_TYPE, USIZE_T], vec![BOOL_T])).unwrap(); - - let [angle, scalar] = builder.input_wires_arr(); - let radians = builder.add_atorad(angle).unwrap(); - let angle = builder.add_afromrad(scalar, radians).unwrap(); - let angle = builder.add_amul(angle, scalar).unwrap(); - let angle = builder.add_adiv(angle, scalar).unwrap(); - let angle = builder.add_aadd(angle, angle).unwrap(); - let angle = builder.add_asub(angle, angle).unwrap(); - let [num, log_denom] = builder.add_aparts(angle).unwrap(); - let _angle_sum = builder.add_anew(num, log_denom).unwrap(); - let angle = builder.add_aneg(angle).unwrap(); - let angle = builder.add_atrunc(angle, log_denom).unwrap(); - let bool = builder.add_aeq(angle, angle).unwrap(); - - let _hugr = builder.finish_hugr_with_outputs([bool], ®ISTRY).unwrap(); - } - - #[rstest::rstest] - fn const_angle_statics( - #[values(ConstAngle::TAU, ConstAngle::PI, ConstAngle::PI_2, ConstAngle::PI_4)] - konst: ConstAngle, - ) { - assert_eq!(ConstAngle::new(konst.log_denom(), konst.value()), Ok(konst)); - } -} diff --git a/tket2/src/extension/rotation.rs b/tket2/src/extension/rotation.rs new file mode 100644 index 00000000..826a4eaf --- /dev/null +++ b/tket2/src/extension/rotation.rs @@ -0,0 +1,289 @@ +use hugr::builder::{BuildError, Dataflow}; +use hugr::extension::simple_op::{MakeOpDef, MakeRegisteredOp}; +use hugr::extension::{prelude::option_type, ExtensionId, ExtensionSet, Version}; +use hugr::ops::constant::{downcast_equal_consts, CustomConst}; +use hugr::std_extensions::arithmetic::float_types::FLOAT64_TYPE; +use hugr::{type_row, Wire}; +use hugr::{ + types::{ConstTypeError, CustomType, Signature, Type, TypeBound}, + Extension, +}; +use smol_str::SmolStr; +use std::f64::consts::PI; +use strum::{EnumIter, EnumString, IntoStaticStr}; + +use lazy_static::lazy_static; + +/// Name of tket 2 rotation extension. +pub const ROTATION_EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("tket2.rotation"); + +/// Current version of the TKET 2 rotation extension +pub const ROTATION_EXTENSION_VERSION: Version = Version::new(0, 1, 0); + +lazy_static! { + /// The extension definition for TKET2 rotation type and ops. + pub static ref ROTATION_EXTENSION: Extension = { + let mut e = Extension::new(ROTATION_EXTENSION_ID, ROTATION_EXTENSION_VERSION); + add_to_extension(&mut e); + e + }; +} + +/// Identifier for the rotation type. +const ROTATION_TYPE_ID: SmolStr = SmolStr::new_inline("rotation"); +/// Rotation type (as [CustomType]) +pub const ROTATION_CUSTOM_TYPE: CustomType = + CustomType::new_simple(ROTATION_TYPE_ID, ROTATION_EXTENSION_ID, TypeBound::Copyable); + +/// Type representing a rotation that is a number of half turns (as [Type]) +pub const ROTATION_TYPE: Type = Type::new_extension(ROTATION_CUSTOM_TYPE); + +/// A rotation +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ConstRotation { + half_turns: f64, +} + +impl ConstRotation { + /// The constant π + pub const PI: Self = Self::new_unchecked(1.0); + /// The constant 2π + pub const TAU: Self = Self::new_unchecked(2.0); + /// The constant π/2 + pub const PI_2: Self = Self::new_unchecked(0.5); + /// The constant π/4 + pub const PI_4: Self = Self::new_unchecked(0.25); + + const fn new_unchecked(half_turns: f64) -> Self { + Self { half_turns } + } + /// Create a new [`ConstRotation`] from a number of half turns + pub fn new(half_turns: f64) -> Result { + // test for a valid float value + if half_turns.is_nan() || half_turns.is_infinite() { + return Err(ConstTypeError::CustomCheckFail( + hugr::types::CustomCheckFailure::Message(format!( + "Invalid rotation value {}.", + half_turns + )), + )); + } + Ok(Self { half_turns }) + } + + /// Returns the value of the constant in radians + pub fn to_radians(&self) -> f64 { + self.half_turns * PI + } + + /// Create a new [`ConstRotation`] from a floating-point value in radians, + pub fn from_radians(theta: f64) -> Result { + Self::new(theta / PI) + } + + /// Returns the number of half turns in the rotation. + pub fn half_turns(&self) -> f64 { + self.half_turns + } +} + +#[typetag::serde] +impl CustomConst for ConstRotation { + fn name(&self) -> SmolStr { + format!("a(π*{})", self.half_turns).into() + } + + fn get_type(&self) -> Type { + ROTATION_TYPE + } + + fn equal_consts(&self, other: &dyn CustomConst) -> bool { + downcast_equal_consts(self, other) + } + fn extension_reqs(&self) -> ExtensionSet { + ExtensionSet::singleton(&ROTATION_EXTENSION_ID) + } +} + +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, EnumIter, IntoStaticStr, EnumString)] +#[allow(missing_docs, non_camel_case_types)] +#[non_exhaustive] +/// Rotation operations +pub enum RotationOp { + /// Construct rotation from number of half-turns (would be multiples of π in radians). + from_halfturns, + /// Convert rotation to number of half-turns (would be multiples of π in radians). + to_halfturns, + /// Add two angles together (experimental, may be removed, use float addition + /// first instead if possible). + radd, +} + +impl MakeOpDef for RotationOp { + fn from_def( + op_def: &hugr::extension::OpDef, + ) -> Result + where + Self: Sized, + { + hugr::extension::simple_op::try_from_name(op_def.name(), op_def.extension()) + } + + fn signature(&self) -> hugr::extension::SignatureFunc { + match self { + RotationOp::from_halfturns => Signature::new( + type_row![FLOAT64_TYPE], + Type::from(option_type(type_row![ROTATION_TYPE])), + ), + RotationOp::to_halfturns => { + Signature::new(type_row![ROTATION_TYPE], type_row![FLOAT64_TYPE]) + } + RotationOp::radd => Signature::new( + type_row![ROTATION_TYPE, ROTATION_TYPE], + type_row![ROTATION_TYPE], + ), + } + .into() + } + + fn description(&self) -> String { + match self { + RotationOp::from_halfturns => { + "Construct rotation from number of half-turns (would be multiples of π in radians)." + } + RotationOp::to_halfturns => { + "Convert rotation to number of half-turns (would be multiples of π in radians)." + } + RotationOp::radd => "Add two angles together (experimental).", + } + .to_owned() + } + + fn extension(&self) -> hugr::extension::ExtensionId { + ROTATION_EXTENSION_ID + } + + // TODO constant folding + // https://github.com/CQCL/tket2/issues/405 +} + +impl MakeRegisteredOp for RotationOp { + fn extension_id(&self) -> hugr::extension::ExtensionId { + ROTATION_EXTENSION_ID + } + + fn registry<'s, 'r: 's>(&'s self) -> &'r hugr::extension::ExtensionRegistry { + &super::REGISTRY + } +} + +pub(super) fn add_to_extension(extension: &mut Extension) { + extension + .add_type( + ROTATION_TYPE_ID, + vec![], + "rotation type expressed as number of half turns".to_owned(), + TypeBound::Copyable.into(), + ) + .unwrap(); + + RotationOp::load_all_ops(extension).expect("add fail"); +} + +/// An extension trait for [Dataflow] providing methods to add +/// "tket2.rotation" operations. +pub trait RotationOpBuilder: Dataflow { + /// Add a "tket2.rotation.fromturns" op. + fn add_from_halfturns(&mut self, turns: Wire) -> Result { + Ok(self + .add_dataflow_op(RotationOp::from_halfturns, [turns])? + .out_wire(0)) + } + + /// Add a "tket2.rotation.toturns" op. + fn add_to_halfturns(&mut self, rotation: Wire) -> Result { + Ok(self + .add_dataflow_op(RotationOp::to_halfturns, [rotation])? + .out_wire(0)) + } +} + +impl RotationOpBuilder for D {} + +#[cfg(test)] +mod test { + use hugr::{ + builder::{DFGBuilder, DataflowHugr}, + ops::OpType, + }; + use strum::IntoEnumIterator; + + use crate::extension::REGISTRY; + + use super::*; + + #[test] + fn test_rotation_consts() { + let const_57 = ConstRotation::new(5.7).unwrap(); + let const_01 = ConstRotation::new(0.1).unwrap(); + let const_256 = ConstRotation::new(256.0).unwrap(); + assert_ne!(const_57, const_01); + assert_ne!(const_57, const_256); + assert_eq!(const_57, ConstRotation::new(5.7).unwrap()); + + assert_eq!(const_57.get_type(), ROTATION_TYPE); + assert!(matches!( + ConstRotation::new(f64::INFINITY), + Err(ConstTypeError::CustomCheckFail(_)) + )); + assert!(matches!( + ConstRotation::new(f64::NAN), + Err(ConstTypeError::CustomCheckFail(_)) + )); + let const_af1 = ConstRotation::from_radians(0.75 * PI).unwrap(); + assert_eq!(const_af1.half_turns(), 0.75); + + assert!(const_57.equal_consts(&ConstRotation::new(5.7).unwrap())); + assert_ne!(const_57, const_01); + + assert_eq!(const_256.name(), "a(π*256)"); + } + + #[test] + fn test_ops() { + let ops = RotationOp::iter().collect::>(); + for op in ops { + let optype: OpType = op.into(); + assert_eq!(optype.cast(), Some(op)); + } + } + + #[test] + fn test_builder() { + let mut builder = DFGBuilder::new(Signature::new( + ROTATION_TYPE, + Type::from(option_type(ROTATION_TYPE)), + )) + .unwrap(); + + let [rotation] = builder.input_wires_arr(); + let turns = builder.add_to_halfturns(rotation).unwrap(); + let mb_rotation = builder.add_from_halfturns(turns).unwrap(); + let _hugr = builder + .finish_hugr_with_outputs([mb_rotation], ®ISTRY) + .unwrap(); + } + + #[rstest::rstest] + fn const_rotation_statics( + #[values( + ConstRotation::TAU, + ConstRotation::PI, + ConstRotation::PI_2, + ConstRotation::PI_4 + )] + konst: ConstRotation, + ) { + assert_eq!(ConstRotation::new(konst.half_turns()), Ok(konst)); + } +} diff --git a/tket2/src/ops.rs b/tket2/src/ops.rs index f35c1482..b6cb23ed 100644 --- a/tket2/src/ops.rs +++ b/tket2/src/ops.rs @@ -1,4 +1,4 @@ -use crate::extension::angle::ANGLE_TYPE; +use crate::extension::rotation::ROTATION_TYPE; use crate::extension::{ SYM_OP_ID, TKET2_EXTENSION as EXTENSION, TKET2_EXTENSION_ID as EXTENSION_ID, }; @@ -115,8 +115,8 @@ impl MakeOpDef for Tk2Op { CX | CZ | CY => Signature::new_endo(type_row![QB_T; 2]), Toffoli => Signature::new_endo(type_row![QB_T; 3]), Measure => Signature::new(one_qb_row, type_row![QB_T, BOOL_T]), - Rz | Rx | Ry => Signature::new(type_row![QB_T, ANGLE_TYPE], one_qb_row), - CRz => Signature::new(type_row![QB_T, QB_T, ANGLE_TYPE], type_row![QB_T; 2]), + Rz | Rx | Ry => Signature::new(type_row![QB_T, ROTATION_TYPE], one_qb_row), + CRz => Signature::new(type_row![QB_T, QB_T, ROTATION_TYPE], type_row![QB_T; 2]), QAlloc => Signature::new(type_row![], one_qb_row), QFree => Signature::new(one_qb_row, type_row![]), } diff --git a/tket2/src/optimiser/badger.rs b/tket2/src/optimiser/badger.rs index 0c64fd9e..54d62f7a 100644 --- a/tket2/src/optimiser/badger.rs +++ b/tket2/src/optimiser/badger.rs @@ -549,14 +549,14 @@ mod tests { use rstest::{fixture, rstest}; use crate::serialize::load_tk1_json_str; - use crate::{extension::angle::ANGLE_TYPE, optimiser::badger::BadgerOptions}; + use crate::{extension::rotation::ROTATION_TYPE, optimiser::badger::BadgerOptions}; use crate::{extension::REGISTRY, Circuit, Tk2Op}; use super::{BadgerOptimiser, DefaultBadgerOptimiser}; #[fixture] fn rz_rz() -> Circuit { - let input_t = vec![QB_T, ANGLE_TYPE, ANGLE_TYPE]; + let input_t = vec![QB_T, ROTATION_TYPE, ROTATION_TYPE]; let output_t = vec![QB_T]; let mut h = DFGBuilder::new(Signature::new(input_t, output_t)).unwrap(); @@ -624,7 +624,7 @@ mod tests { fn rz_rz_cancellation(rz_rz: Circuit, #[case] badger_opt: DefaultBadgerOptimiser) { use hugr::ops::OpType; - use crate::{extension::angle::AngleOp, op_matches}; + use crate::{extension::rotation::RotationOp, op_matches}; let opt_rz = badger_opt.optimise( &rz_rz, @@ -641,7 +641,7 @@ mod tests { .unwrap(); // Rzs combined into a single one. - assert_eq!(op1.cast(), Some(AngleOp::aadd)); + assert_eq!(op1.cast(), Some(RotationOp::radd)); assert!(op_matches(op2, Tk2Op::Rz)); } diff --git a/tket2/src/optimiser/badger/qtz_circuit.rs b/tket2/src/optimiser/badger/qtz_circuit.rs index 212328b6..00d85f1b 100644 --- a/tket2/src/optimiser/badger/qtz_circuit.rs +++ b/tket2/src/optimiser/badger/qtz_circuit.rs @@ -10,7 +10,7 @@ use hugr::{CircuitUnit, Hugr}; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use crate::extension::angle::{AngleOp, ANGLE_TYPE}; +use crate::extension::rotation::{RotationOp, ROTATION_TYPE}; use crate::{Circuit, Tk2Op}; #[derive(Debug, Serialize, Deserialize)] @@ -41,7 +41,7 @@ struct RepCircData { fn map_op(opstr: &str) -> Op { if opstr == "add" { - return AngleOp::aadd.into(); + return RotationOp::radd.into(); } // TODO, more match opstr { @@ -64,7 +64,7 @@ fn map_op(opstr: &str) -> Op { impl From for Circuit { fn from(RepCircData { circ: rc, meta }: RepCircData) -> Self { let qb_types: Vec = vec![QB_T; meta.n_qb]; - let param_types: Vec = vec![ANGLE_TYPE; meta.n_input_param]; + let param_types: Vec = vec![ROTATION_TYPE; meta.n_input_param]; let mut builder = DFGBuilder::new(Signature::new( [qb_types.clone(), param_types].concat(), qb_types, diff --git a/tket2/src/passes/commutation.rs b/tket2/src/passes/commutation.rs index 5792b080..87b203af 100644 --- a/tket2/src/passes/commutation.rs +++ b/tket2/src/passes/commutation.rs @@ -326,7 +326,7 @@ pub fn apply_greedy_commutation(circ: &mut Circuit) -> Result Circuit { let build = || { let mut dfg = DFGBuilder::new(Signature::new( - type_row![QB_T, QB_T, ANGLE_TYPE], + type_row![QB_T, QB_T, ROTATION_TYPE], type_row![QB_T, QB_T], ))?; diff --git a/tket2/src/portmatching/pattern.rs b/tket2/src/portmatching/pattern.rs index 97d95533..fdfdc6b2 100644 --- a/tket2/src/portmatching/pattern.rs +++ b/tket2/src/portmatching/pattern.rs @@ -170,7 +170,7 @@ mod tests { use hugr::ops::OpType; use hugr::types::Signature; - use crate::extension::angle::ANGLE_TYPE; + use crate::extension::rotation::ROTATION_TYPE; use crate::extension::REGISTRY; use crate::utils::build_simple_circuit; use crate::Tk2Op; @@ -188,7 +188,7 @@ mod tests { /// A circuit with two rotation gates in sequence, sharing a param fn circ_with_copy() -> Circuit { - let input_t = vec![QB_T, ANGLE_TYPE]; + let input_t = vec![QB_T, ROTATION_TYPE]; let output_t = vec![QB_T]; let mut h = DFGBuilder::new(Signature::new(input_t, output_t)).unwrap(); @@ -206,7 +206,7 @@ mod tests { /// A circuit with two rotation gates in parallel, sharing a param fn circ_with_copy_disconnected() -> Circuit { - let input_t = vec![QB_T, QB_T, ANGLE_TYPE]; + let input_t = vec![QB_T, QB_T, ROTATION_TYPE]; let output_t = vec![QB_T, QB_T]; let mut h = DFGBuilder::new(Signature::new(input_t, output_t)).unwrap(); diff --git a/tket2/src/serialize/pytket.rs b/tket2/src/serialize/pytket.rs index e3afd379..66adc89e 100644 --- a/tket2/src/serialize/pytket.rs +++ b/tket2/src/serialize/pytket.rs @@ -26,7 +26,7 @@ use tket_json_rs::circuit_json::{self, SerialCircuit}; use tket_json_rs::optype::OpType as SerialOpType; use crate::circuit::Circuit; -use crate::extension::angle::ConstAngle; +use crate::extension::rotation::ConstRotation; use self::decoder::Tk1Decoder; use self::encoder::Tk1Encoder; @@ -297,10 +297,7 @@ fn try_param_to_constant(param: &str) -> Option { return None; }; - let radians = half_turns * std::f64::consts::PI; - ConstAngle::from_radians_rounding_max(radians) - .ok() - .map(Into::into) + ConstRotation::new(half_turns).ok().map(Into::into) } /// Convert a HUGR angle constant to a TKET1 parameter. @@ -309,8 +306,8 @@ fn try_param_to_constant(param: &str) -> Option { /// whereas HUGR uses radians. #[inline] fn try_constant_to_param(val: &Value) -> Option { - let const_angle = val.get_custom_value::()?; - let half_turns = const_angle.to_turns() * 2.0; + let const_angle = val.get_custom_value::()?; + let half_turns = const_angle.half_turns(); Some(half_turns.to_string()) } diff --git a/tket2/src/serialize/pytket/encoder.rs b/tket2/src/serialize/pytket/encoder.rs index 63a11222..28d6e4a9 100644 --- a/tket2/src/serialize/pytket/encoder.rs +++ b/tket2/src/serialize/pytket/encoder.rs @@ -12,7 +12,7 @@ use tket_json_rs::circuit_json::{self, SerialCircuit}; use crate::circuit::command::{CircuitUnit, Command}; use crate::circuit::Circuit; -use crate::extension::angle::{AngleOp, ANGLE_TYPE}; +use crate::extension::rotation::{RotationOp, ROTATION_TYPE}; use crate::ops::match_symb_const_op; use crate::serialize::pytket::RegisterHash; use crate::Tk2Op; @@ -49,7 +49,7 @@ impl Tk1Encoder { // Check for unsupported input types. for (_, _, typ) in circ.units() { - if ![ANGLE_TYPE, QB_T, BOOL_T].contains(&typ) { + if ![ROTATION_TYPE, QB_T, BOOL_T].contains(&typ) { return Err(TK1ConvertError::NonSerializableInputs { typ }); } } @@ -125,7 +125,7 @@ impl Tk1Encoder { ) }); bit_args.push(reg); - } else if ty == ANGLE_TYPE { + } else if ty == ROTATION_TYPE { let CircuitUnit::Wire(param_wire) = unit else { unreachable!("Angle types are not linear.") }; @@ -553,7 +553,7 @@ impl ParameterTracker { let mut tracker = ParameterTracker::default(); let angle_input_wires = circ.units().filter_map(|u| match u { - (CircuitUnit::Wire(w), _, ty) if ty == ANGLE_TYPE => Some(w), + (CircuitUnit::Wire(w), _, ty) if ty == ROTATION_TYPE => Some(w), _ => None, }); @@ -575,8 +575,8 @@ impl ParameterTracker { let input_count = if let Some(signature) = optype.dataflow_signature() { // Only consider commands where all inputs are parameters, // and some outputs are also parameters. - let all_inputs = signature.input().iter().all(|ty| ty == &ANGLE_TYPE); - let some_output = signature.output().iter().any(|ty| ty == &ANGLE_TYPE); + let all_inputs = signature.input().iter().all(|ty| ty == &ROTATION_TYPE); + let some_output = signature.output().iter().any(|ty| ty == &ROTATION_TYPE); if !all_inputs || !some_output { return Ok(false); } @@ -597,7 +597,7 @@ impl ParameterTracker { panic!("Angle types are not linear") }; let Some(param) = self.parameters.get(&wire) else { - let typ = ANGLE_TYPE; + let typ = ROTATION_TYPE; return Err(OpConvertError::UnresolvedParamInput { typ, optype: optype.clone(), @@ -619,7 +619,7 @@ impl ParameterTracker { // Re-use the parameter from the input. inputs[0].clone() } - OpType::ExtensionOp(_) if optype.cast() == Some(AngleOp::aadd) => { + OpType::ExtensionOp(_) if optype.cast() == Some(RotationOp::radd) => { format!("{} + {}", inputs[0], inputs[1]) } _ => { diff --git a/tket2/src/serialize/pytket/op/native.rs b/tket2/src/serialize/pytket/op/native.rs index 552c1449..cb8c8fee 100644 --- a/tket2/src/serialize/pytket/op/native.rs +++ b/tket2/src/serialize/pytket/op/native.rs @@ -9,7 +9,7 @@ use hugr::IncomingPort; use tket_json_rs::circuit_json; use tket_json_rs::optype::OpType as Tk1OpType; -use crate::extension::angle::ANGLE_TYPE; +use crate::extension::rotation::ROTATION_TYPE; use crate::Tk2Op; /// An operation with a native TKET2 counterpart. @@ -159,7 +159,7 @@ impl NativeOp { let types = sig.input_types().to_owned(); sig.input_ports() .zip(types) - .filter(|(_, ty)| ty == &ANGLE_TYPE) + .filter(|(_, ty)| ty == &ROTATION_TYPE) .map(|(port, _)| port) }) } @@ -179,7 +179,7 @@ impl NativeOp { self.input_qubits += 1; } else if ty == &BOOL_T { self.input_bits += 1; - } else if ty == &ANGLE_TYPE { + } else if ty == &ROTATION_TYPE { self.num_params += 1; } } diff --git a/tket2/src/serialize/pytket/op/serialised.rs b/tket2/src/serialize/pytket/op/serialised.rs index 35c0d7a3..b32ac9c6 100644 --- a/tket2/src/serialize/pytket/op/serialised.rs +++ b/tket2/src/serialize/pytket/op/serialised.rs @@ -10,7 +10,7 @@ use hugr::IncomingPort; use serde::de::Error; use tket_json_rs::circuit_json; -use crate::extension::angle::ANGLE_TYPE; +use crate::extension::rotation::ROTATION_TYPE; use crate::extension::{REGISTRY, TKET1_EXTENSION, TKET1_EXTENSION_ID, TKET1_OP_NAME}; use crate::serialize::pytket::OpConvertError; @@ -38,7 +38,7 @@ pub struct OpaqueTk1Op { /// instead stored purely as metadata for the `Operation`. param_inputs: Vec>, /// The number of non-None inputs in `param_inputs`, corresponding to the - /// ANGLE_TYPE inputs to the Hugr operation. + /// ROTATION_TYPE inputs to the Hugr operation. pub num_params: usize, } @@ -112,7 +112,7 @@ impl OpaqueTk1Op { vec![BOOL_T.clone(); self.num_bits], ] .concat(); - let params = vec![ANGLE_TYPE; self.num_params]; + let params = vec![ROTATION_TYPE; self.num_params]; Signature::new([linear.clone(), params].concat(), linear) .with_extension_delta(TKET1_EXTENSION_ID) } diff --git a/tket2/src/serialize/pytket/tests.rs b/tket2/src/serialize/pytket/tests.rs index abc776f3..53c55353 100644 --- a/tket2/src/serialize/pytket/tests.rs +++ b/tket2/src/serialize/pytket/tests.rs @@ -15,7 +15,7 @@ use tket_json_rs::optype; use super::{TKETDecode, METADATA_Q_OUTPUT_REGISTERS}; use crate::circuit::Circuit; -use crate::extension::angle::{AngleOp, ConstAngle, ANGLE_TYPE}; +use crate::extension::rotation::{ConstRotation, RotationOp, ROTATION_TYPE}; use crate::extension::REGISTRY; use crate::Tk2Op; @@ -188,13 +188,13 @@ fn circ_measure_ancilla() -> Circuit { #[fixture] fn circ_add_angles_symbolic() -> Circuit { - let input_t = vec![QB_T, ANGLE_TYPE, ANGLE_TYPE]; + let input_t = vec![QB_T, ROTATION_TYPE, ROTATION_TYPE]; let output_t = vec![QB_T]; let mut h = DFGBuilder::new(Signature::new(input_t, output_t)).unwrap(); let [qb, f1, f2] = h.input_wires_arr(); let [f12] = h - .add_dataflow_op(AngleOp::aadd, [f1, f2]) + .add_dataflow_op(RotationOp::radd, [f1, f2]) .unwrap() .outputs_arr(); let [qb] = h @@ -212,10 +212,10 @@ fn circ_add_angles_constants() -> Circuit { let qb = h.input_wires().next().unwrap(); - let point2 = h.add_load_value(ConstAngle::new(2, 3).unwrap()); - let point3 = h.add_load_value(ConstAngle::new(4, 5).unwrap()); + let point2 = h.add_load_value(ConstRotation::new(0.2).unwrap()); + let point3 = h.add_load_value(ConstRotation::new(0.3).unwrap()); let point5 = h - .add_dataflow_op(AngleOp::aadd, [point2, point3]) + .add_dataflow_op(RotationOp::radd, [point2, point3]) .unwrap() .out_wire(0); @@ -290,7 +290,7 @@ fn circuit_roundtrip(#[case] circ: Circuit, #[case] decoded_sig: Signature) { /// expressions. #[rstest] #[case::symbolic(circ_add_angles_symbolic(), "f0 + f1")] -#[case::constants(circ_add_angles_constants(), "1.5 + 0.625")] +#[case::constants(circ_add_angles_constants(), "0.2 + 0.3")] fn test_add_angle_serialise(#[case] circ_add_angles: Circuit, #[case] param_str: &str) { let ser: SerialCircuit = SerialCircuit::encode(&circ_add_angles).unwrap(); assert_eq!(ser.commands.len(), 1);