From 773bc5f7915480e3ddb43186b7aba8aedde81bdc Mon Sep 17 00:00:00 2001 From: Luca Mondada Date: Thu, 14 Dec 2023 15:50:41 +0000 Subject: [PATCH 1/9] feat!: Add lexicographic cost --- tket2/src/circuit/cost.rs | 120 +++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 52 deletions(-) diff --git a/tket2/src/circuit/cost.rs b/tket2/src/circuit/cost.rs index 4346d168..1d770053 100644 --- a/tket2/src/circuit/cost.rs +++ b/tket2/src/circuit/cost.rs @@ -41,14 +41,29 @@ pub trait CostDelta: /// This is used to order circuits based on major cost first, then minor cost. /// A typical example would be CX count as major cost and total gate count as /// minor cost. -#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, From)] -pub struct MajorMinorCost { - major: T, - minor: T, +pub type MajorMinorCost = LexicographicCost; + +impl From<(T, T)> for MajorMinorCost { + fn from((major, minor): (T, T)) -> Self { + Self([major, minor]) + } +} + +/// A cost that is ordered lexicographically. +/// +/// An array of cost functions, where the first one is infinitely more important +/// than the second, which is infinitely more important than the third, etc. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, From)] +pub struct LexicographicCost([T; N]); + +impl Default for LexicographicCost { + fn default() -> Self { + Self([Default::default(); N]) + } } // Serialise as string so that it is easy to write to CSV -impl serde::Serialize for MajorMinorCost { +impl serde::Serialize for LexicographicCost { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -57,70 +72,86 @@ impl serde::Serialize for MajorMinorCost { } } -impl Debug for MajorMinorCost { +impl Debug for LexicographicCost { // TODO: A nicer print for the logs fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "(major={}, minor={})", self.major, self.minor) + write!(f, "{:?}", self) } } -impl> Add for MajorMinorCost { - type Output = MajorMinorCost; +impl + Copy, const N: usize> Add for LexicographicCost { + type Output = Self; - fn add(self, rhs: MajorMinorCost) -> Self::Output { - (self.major + rhs.major, self.minor + rhs.minor).into() + fn add(mut self, rhs: Self) -> Self::Output { + for i in 0..N { + self.0[i] = self.0[i] + rhs.0[i]; + } + return self; } } -impl AddAssign for MajorMinorCost { +impl AddAssign for LexicographicCost { fn add_assign(&mut self, rhs: Self) { - self.major += rhs.major; - self.minor += rhs.minor; + for i in 0..N { + self.0[i] += rhs.0[i]; + } } } -impl + Default> Sum for MajorMinorCost { +impl + Default + Copy, const N: usize> Sum for LexicographicCost { fn sum>(iter: I) -> Self { - iter.reduce(|a, b| (a.major + b.major, a.minor + b.minor).into()) - .unwrap_or_default() + iter.reduce(|a, b| a + b).unwrap_or_default() } } -impl CostDelta for MajorMinorCost { +impl CostDelta for LexicographicCost { #[inline] fn as_isize(&self) -> isize { - self.major + if N > 0 { + self.0[0] + } else { + 0 + } } } -impl CircuitCost for MajorMinorCost { - type CostDelta = MajorMinorCost; +impl CircuitCost for LexicographicCost { + type CostDelta = LexicographicCost; #[inline] fn as_usize(&self) -> usize { - self.major + if N > 0 { + self.0[0] + } else { + 0 + } } #[inline] fn sub_cost(&self, other: &Self) -> Self::CostDelta { - let major = (self.major as isize) - (other.major as isize); - let minor = (self.minor as isize) - (other.minor as isize); - MajorMinorCost { major, minor } + let mut costdelta = [0; N]; + for i in 0..N { + costdelta[i] = (self.0[i] as isize) - (other.0[i] as isize); + } + LexicographicCost(costdelta) } #[inline] fn add_delta(&self, delta: &Self::CostDelta) -> Self { - MajorMinorCost { - major: self.major.saturating_add_signed(delta.major), - minor: self.minor.saturating_add_signed(delta.minor), + let mut ret = [0; N]; + for i in 0..N { + ret[i] = self.0[i].saturating_add_signed(delta.0[i]); } + Self(ret) } #[inline] fn div_cost(&self, n: NonZeroUsize) -> Self { - let major = (self.major.saturating_sub(1)) / n.get() + 1; - let minor = (self.minor.saturating_sub(1)) / n.get() + 1; - Self { major, minor } + let mut ret = [0; N]; + for i in 0..N { + ret[i] = (self.0[i].saturating_sub(1)) / n.get() + 1; + } + Self(ret) } } @@ -174,38 +205,23 @@ mod tests { #[test] fn major_minor() { - let a = MajorMinorCost { - major: 10, - minor: 2, - }; - let b = MajorMinorCost { - major: 20, - minor: 1, - }; + let a = LexicographicCost([10, 2]); + let b = LexicographicCost([20, 1]); assert!(a < b); - assert_eq!( - a + b, - MajorMinorCost { - major: 30, - minor: 3 - } - ); + assert_eq!(a + b, LexicographicCost([30, 3])); assert_eq!(a.sub_cost(&b).as_isize(), -10); assert_eq!(b.sub_cost(&a).as_isize(), 10); assert_eq!( a.div_cost(NonZeroUsize::new(2).unwrap()), - MajorMinorCost { major: 5, minor: 1 } + LexicographicCost([5, 1]) ); assert_eq!( a.div_cost(NonZeroUsize::new(3).unwrap()), - MajorMinorCost { major: 4, minor: 1 } + LexicographicCost([4, 1]) ); assert_eq!( a.div_cost(NonZeroUsize::new(1).unwrap()), - MajorMinorCost { - major: 10, - minor: 2 - } + LexicographicCost([10, 2]) ); } } From 0d3686dded0692999a287d5ab33e0ddafa4359d2 Mon Sep 17 00:00:00 2001 From: Luca Mondada Date: Thu, 14 Dec 2023 16:20:08 +0000 Subject: [PATCH 2/9] Satisfy clippy --- tket2/src/circuit/cost.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tket2/src/circuit/cost.rs b/tket2/src/circuit/cost.rs index 1d770053..a4acda21 100644 --- a/tket2/src/circuit/cost.rs +++ b/tket2/src/circuit/cost.rs @@ -2,7 +2,8 @@ use derive_more::From; use hugr::ops::OpType; -use std::fmt::{Debug, Display}; +use itertools::izip; +use std::fmt::Debug; use std::iter::Sum; use std::num::NonZeroUsize; use std::ops::{Add, AddAssign}; @@ -72,10 +73,10 @@ impl serde::Serialize for LexicographicCost { } } -impl Debug for LexicographicCost { +impl Debug for LexicographicCost { // TODO: A nicer print for the logs fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) + write!(f, "{:?}", self.0) } } @@ -86,7 +87,7 @@ impl + Copy, const N: usize> Add for LexicographicCost for i in 0..N { self.0[i] = self.0[i] + rhs.0[i]; } - return self; + self } } @@ -130,8 +131,8 @@ impl CircuitCost for LexicographicCost { #[inline] fn sub_cost(&self, other: &Self) -> Self::CostDelta { let mut costdelta = [0; N]; - for i in 0..N { - costdelta[i] = (self.0[i] as isize) - (other.0[i] as isize); + for (delta, &a, &b) in izip!(costdelta.iter_mut(), self.0.iter(), other.0.iter()) { + *delta = (a as isize) - (b as isize); } LexicographicCost(costdelta) } @@ -139,8 +140,8 @@ impl CircuitCost for LexicographicCost { #[inline] fn add_delta(&self, delta: &Self::CostDelta) -> Self { let mut ret = [0; N]; - for i in 0..N { - ret[i] = self.0[i].saturating_add_signed(delta.0[i]); + for (add, &a, &b) in izip!(ret.iter_mut(), self.0.iter(), delta.0.iter()) { + *add = a.saturating_add_signed(b); } Self(ret) } @@ -148,8 +149,8 @@ impl CircuitCost for LexicographicCost { #[inline] fn div_cost(&self, n: NonZeroUsize) -> Self { let mut ret = [0; N]; - for i in 0..N { - ret[i] = (self.0[i].saturating_sub(1)) / n.get() + 1; + for (div, &a) in izip!(ret.iter_mut(), self.0.iter()) { + *div = (a.saturating_sub(1)) / n.get() + 1; } Self(ret) } From 631258761609eeda7032341dcfec073f848f5428 Mon Sep 17 00:00:00 2001 From: Luca Mondada Date: Thu, 14 Dec 2023 16:21:41 +0000 Subject: [PATCH 3/9] Prettify zip --- tket2/src/circuit/cost.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tket2/src/circuit/cost.rs b/tket2/src/circuit/cost.rs index a4acda21..f91f29e2 100644 --- a/tket2/src/circuit/cost.rs +++ b/tket2/src/circuit/cost.rs @@ -131,7 +131,7 @@ impl CircuitCost for LexicographicCost { #[inline] fn sub_cost(&self, other: &Self) -> Self::CostDelta { let mut costdelta = [0; N]; - for (delta, &a, &b) in izip!(costdelta.iter_mut(), self.0.iter(), other.0.iter()) { + for (delta, &a, &b) in izip!(costdelta.iter_mut(), &self.0, &other.0) { *delta = (a as isize) - (b as isize); } LexicographicCost(costdelta) @@ -140,7 +140,7 @@ impl CircuitCost for LexicographicCost { #[inline] fn add_delta(&self, delta: &Self::CostDelta) -> Self { let mut ret = [0; N]; - for (add, &a, &b) in izip!(ret.iter_mut(), self.0.iter(), delta.0.iter()) { + for (add, &a, &b) in izip!(ret.iter_mut(), &self.0, &delta.0) { *add = a.saturating_add_signed(b); } Self(ret) @@ -149,7 +149,7 @@ impl CircuitCost for LexicographicCost { #[inline] fn div_cost(&self, n: NonZeroUsize) -> Self { let mut ret = [0; N]; - for (div, &a) in izip!(ret.iter_mut(), self.0.iter()) { + for (div, &a) in ret.iter_mut().zip(&self.0) { *div = (a.saturating_sub(1)) / n.get() + 1; } Self(ret) From 1a183b0d9f4c5aaa421cde326717631aae1a4c9b Mon Sep 17 00:00:00 2001 From: Luca Mondada Date: Thu, 14 Dec 2023 16:29:12 +0000 Subject: [PATCH 4/9] Add coverage --- tket2/src/circuit/cost.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tket2/src/circuit/cost.rs b/tket2/src/circuit/cost.rs index f91f29e2..3019c4c4 100644 --- a/tket2/src/circuit/cost.rs +++ b/tket2/src/circuit/cost.rs @@ -225,4 +225,32 @@ mod tests { LexicographicCost([10, 2]) ); } + + #[test] + fn zero_dim_cost() { + let a = LexicographicCost::([]); + let b = LexicographicCost::([]); + assert_eq!(a, b); + assert_eq!(a + b, LexicographicCost::([])); + assert_eq!(a.sub_cost(&b).as_isize(), 0); + assert_eq!(b.sub_cost(&a).as_isize(), 0); + assert_eq!(a.div_cost(NonZeroUsize::new(2).unwrap()), a); + assert_eq!(a.div_cost(NonZeroUsize::new(3).unwrap()), a); + assert_eq!(a.div_cost(NonZeroUsize::new(1).unwrap()), a); + } + + #[test] + fn as_usize() { + let a = LexicographicCost([10, 2]); + assert_eq!(a.as_usize(), 10); + let a = LexicographicCost::([]); + assert_eq!(a.as_usize(), 0); + } + + #[test] + fn serde_serialize() { + let a = LexicographicCost([10, 2]); + let s = serde_json::to_string(&a).unwrap(); + assert_eq!(s, "\"[10, 2]\""); + } } From 19eb5b274bdab57ea66843c7b39e8f493a35a43c Mon Sep 17 00:00:00 2001 From: Luca Mondada <72734770+lmondada@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:51:28 +0000 Subject: [PATCH 5/9] Update tket2/src/circuit/cost.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Agustín Borgna <121866228+aborgna-q@users.noreply.github.com> --- tket2/src/circuit/cost.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tket2/src/circuit/cost.rs b/tket2/src/circuit/cost.rs index 3019c4c4..c31ec446 100644 --- a/tket2/src/circuit/cost.rs +++ b/tket2/src/circuit/cost.rs @@ -44,9 +44,12 @@ pub trait CostDelta: /// minor cost. pub type MajorMinorCost = LexicographicCost; -impl From<(T, T)> for MajorMinorCost { - fn from((major, minor): (T, T)) -> Self { - Self([major, minor]) +impl From for LexicographicCost +where + V: Into<[T; N]>, +{ + fn from(v: V) -> Self { + Self(v.into()) } } @@ -54,7 +57,7 @@ impl From<(T, T)> for MajorMinorCost { /// /// An array of cost functions, where the first one is infinitely more important /// than the second, which is infinitely more important than the third, etc. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, From)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct LexicographicCost([T; N]); impl Default for LexicographicCost { From c688bc666f37f042c95d63e3e882d4df7fdbdc5e Mon Sep 17 00:00:00 2001 From: Luca Mondada Date: Thu, 14 Dec 2023 16:56:40 +0000 Subject: [PATCH 6/9] (,) -> [,] --- tket2/src/rewrite/strategy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket2/src/rewrite/strategy.rs b/tket2/src/rewrite/strategy.rs index 456e21bf..40b4cee7 100644 --- a/tket2/src/rewrite/strategy.rs +++ b/tket2/src/rewrite/strategy.rs @@ -344,7 +344,7 @@ where #[inline] fn op_cost(&self, op: &OpType) -> Self::OpCost { - ((self.major_cost)(op), (self.minor_cost)(op)).into() + [(self.major_cost)(op), (self.minor_cost)(op)].into() } } From 17cf6621e781abd9891f813a087ea21fd9fcc89f Mon Sep 17 00:00:00 2001 From: Luca Mondada Date: Thu, 14 Dec 2023 17:00:12 +0000 Subject: [PATCH 7/9] Remove unused import --- tket2/src/circuit/cost.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tket2/src/circuit/cost.rs b/tket2/src/circuit/cost.rs index c31ec446..c94ddef5 100644 --- a/tket2/src/circuit/cost.rs +++ b/tket2/src/circuit/cost.rs @@ -1,6 +1,5 @@ //! Cost definitions for a circuit. -use derive_more::From; use hugr::ops::OpType; use itertools::izip; use std::fmt::Debug; From 851421db05e290f64324c8c25ddb2518c19ba440 Mon Sep 17 00:00:00 2001 From: Luca Mondada Date: Thu, 14 Dec 2023 17:07:39 +0000 Subject: [PATCH 8/9] chore!: hike MSRV to 1.71 --- .github/workflows/ci.yml | 4 ++-- Cargo.toml | 2 +- DEVELOPMENT.md | 2 +- README.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e366e04e..d57a4b50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,13 +64,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: ['1.70', stable, beta] + rust: ['1.71', stable, beta] # workaround to ignore non-stable tests when running the merge queue checks # see: https://github.community/t/how-to-conditionally-include-exclude-items-in-matrix-eg-based-on-branch/16853/6 isMerge: - ${{ github.event_name == 'merge_group' }} exclude: - - rust: '1.70' + - rust: '1.71' isMerge: true - rust: beta isMerge: true diff --git a/Cargo.toml b/Cargo.toml index d04c60f4..44848e8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ default-members = ["tket2"] [workspace.package] version = "0.0.0-alpha.1" -rust-version = "1.70" +rust-version = "1.71" edition = "2021" homepage = "https://github.com/CQCL/tket2" license-file = "LICENCE" diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 3453c3b1..62feb168 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -28,7 +28,7 @@ shell by setting up [direnv](https://devenv.sh/automatic-shell-activation/). To setup the environment manually you will need: -- Rust 1.70+: https://www.rust-lang.org/tools/install +- Rust 1.71+: https://www.rust-lang.org/tools/install - Poetry: https://python-poetry.org/ diff --git a/README.md b/README.md index eff080f0..7fa7ec36 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Version 2 of the TKET compiler. [build_status]: https://github.com/CQCL-DEV/hugr/workflows/Continuous%20integration/badge.svg?branch=main - [msrv]: https://img.shields.io/badge/rust-1.70.0%2B-blue.svg + [msrv]: https://img.shields.io/badge/rust-1.71.0%2B-blue.svg [codecov]: https://img.shields.io/codecov/c/gh/CQCL/tket2?logo=codecov ## Features From 8684bc944415419a6455144ccf2bd66d954f638c Mon Sep 17 00:00:00 2001 From: Luca Mondada Date: Thu, 14 Dec 2023 17:11:39 +0000 Subject: [PATCH 9/9] revert: "(,) -> [,]" This reverts commit c688bc666f37f042c95d63e3e882d4df7fdbdc5e. --- tket2/src/rewrite/strategy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket2/src/rewrite/strategy.rs b/tket2/src/rewrite/strategy.rs index 40b4cee7..456e21bf 100644 --- a/tket2/src/rewrite/strategy.rs +++ b/tket2/src/rewrite/strategy.rs @@ -344,7 +344,7 @@ where #[inline] fn op_cost(&self, op: &OpType) -> Self::OpCost { - [(self.major_cost)(op), (self.minor_cost)(op)].into() + ((self.major_cost)(op), (self.minor_cost)(op)).into() } }