Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support classical expressions #86

Merged
merged 8 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pythonize = { workspace = true, optional = true }
strum = { workspace = true, features = ["derive"] }

[dev-dependencies]
itertools = { workspace = true }
pyo3 = { workspace = true }
rstest = { workspace = true }
assert-json-diff = { workspace = true }
Expand All @@ -37,6 +38,7 @@ name = "integration"
path = "tests/lib.rs"

[workspace.dependencies]
itertools = "0.13.0"
pyo3 = "0.22.2"
pythonize = "0.22.0"
rstest = "0.23.0"
Expand Down
9 changes: 9 additions & 0 deletions src/circuit_json.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Contains structs for serializing and deserializing TKET circuits to and from
//! JSON.

use crate::clexpr::ClExpr;
use crate::opbox::OpBox;
use crate::optype::OpType;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -168,6 +169,12 @@ pub struct Operation<P = String> {
#[serde(rename = "box")]
#[serde(skip_serializing_if = "Option::is_none")]
pub op_box: Option<OpBox>,
/// Classical expression.
///
/// Required if the operation is of type [`OpType::ClExpr`].
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "expr")]
pub classical_expr: Option<ClExpr>,
/// The pre-computed signature.
#[serde(skip_serializing_if = "Option::is_none")]
pub signature: Option<Vec<String>>,
Expand Down Expand Up @@ -240,6 +247,7 @@ impl<P> Default for Operation<P> {
data: None,
params: None,
op_box: None,
classical_expr: None,
signature: None,
conditional: None,
classical: None,
Expand Down Expand Up @@ -289,6 +297,7 @@ impl<P> Operation<P> {
.params
.map(|params| params.into_iter().map(f).collect()),
op_box: self.op_box,
classical_expr: self.classical_expr,
signature: self.signature,
conditional: self.conditional,
classical: self.classical,
Expand Down
87 changes: 87 additions & 0 deletions src/clexpr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! Classical expressions

pub mod op;
pub mod operator;

use operator::ClOperator;
use serde::de::SeqAccess;
use serde::ser::SerializeSeq;
use serde::{Deserialize, Serialize};

/// Data encoding a classical expression.
///
/// A classical expression operates over multi-bit registers and/or individual bits,
/// which are identified here by their individual bit positions.
///
/// This is included in a [`Operation`] when the operation is a [`OpType::ClExpr`].
///
/// [`Operation`]: crate::circuit_json::Operation
/// [`OpType::ClExpr`]: crate::optype::OpType::ClExpr
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ClExpr {
/// Mapping between bit variables in the expression and the position of the
/// corresponding bit in the `args` list.
pub bit_posn: Vec<(u32, u32)>,
/// The encoded expression.
pub expr: ClOperator,
/// The input bits of the expression.
pub reg_posn: Vec<InputClRegister>,
/// The output bits of the expression.
pub output_posn: ClRegisterBits,
}

/// An input register for a classical expression.
///
/// Contains the input index as well as the bits that are part of the register.
///
/// Serialized as a list with two elements: the index and the bits.
#[derive(Debug, Default, PartialEq, Clone)]
pub struct InputClRegister {
/// The index of the register variable in the expression.
pub index: u32,
/// The sequence of positions of bits comprising the register variable.
pub bits: ClRegisterBits,
}

/// The sequence of positions of bits in the output.
///
/// Registers are little-endian, so the first bit is the least significant.
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ClRegisterBits(pub Vec<u32>);

impl Serialize for InputClRegister {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(2))?;
seq.serialize_element(&self.index)?;
seq.serialize_element(&self.bits)?;
seq.end()
}
}

impl<'de> Deserialize<'de> for InputClRegister {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct Visitor;

impl<'de_vis> serde::de::Visitor<'de_vis> for Visitor {
type Value = InputClRegister;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a list of two elements: the index and the bits")
}

fn visit_seq<A: SeqAccess<'de_vis>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let index = seq
.next_element::<u32>()?
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
let bits = seq
.next_element::<ClRegisterBits>()?
.ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
Ok(InputClRegister { index, bits })
}
}

deserializer.deserialize_seq(Visitor)
}
}
73 changes: 73 additions & 0 deletions src/clexpr/op.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Classical expression operations.

use serde::{Deserialize, Serialize};
use strum::EnumString;

/// List of supported classical expressions.
///
/// Corresponds to `pytket.circuit.ClOp`.
#[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq, Eq, Hash, EnumString)]
#[non_exhaustive]
pub enum ClOp {
/// Invalid operation
#[default]
INVALID,

/// Bitwise AND
BitAnd,
/// Bitwise OR
BitOr,
/// Bitwise XOR
BitXor,
/// Bitwise equality
BitEq,
/// Bitwise inequality
BitNeq,
/// Bitwise NOT
BitNot,
/// Constant zero bit
BitZero,
/// Constant one bit
BitOne,

/// Registerwise AND
RegAnd,
/// Registerwise OR
RegOr,
/// Registerwise XOR
RegXor,
/// Registerwise equality
RegEq,
/// Registerwise inequality
RegNeq,
/// Registerwise NOT
RegNot,
/// Constant all-zeros register
RegZero,
/// Constant all-ones register
RegOne,
/// Integer less-than comparison
RegLt,
/// Integer greater-than comparison
RegGt,
/// Integer less-than-or-equal comparison
RegLeq,
/// Integer greater-than-or-equal comparison
RegGeq,
/// Integer addition
RegAdd,
/// Integer subtraction
RegSub,
/// Integer multiplication
RegMul,
/// Integer division
RegDiv,
/// Integer exponentiation
RegPow,
/// Left shift
RegLsh,
/// Right shift
RegRsh,
/// Integer negation
RegNeg,
}
78 changes: 78 additions & 0 deletions src/clexpr/operator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! A tree of operators forming a classical expression.

use serde::{Deserialize, Serialize};

use super::op::ClOp;

/// A node in a classical expression tree.
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ClOperator {
/// The operation to be performed.
pub op: ClOp,
/// The arguments to the operation.
pub args: Vec<ClArgument>,
}

/// An argument to a classical expression operation.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(tag = "type", content = "input")]
pub enum ClArgument {
/// A terminal argument.
#[serde(rename = "term")]
Terminal(ClTerminal),
/// A sub-expression.
#[serde(rename = "expr")]
Expression(Box<ClOperator>),
}

/// A terminal argument in a classical expression operation.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(tag = "type", content = "term")]
pub enum ClTerminal {
/// A terminal argument.
#[serde(rename = "var")]
Variable(ClVariable),
/// A constant integer.
#[serde(rename = "int")]
Int(u64),
}

/// A variable terminal argument in a classical expression operation.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Hash)]
#[non_exhaustive]
#[serde(tag = "type", content = "var")]
pub enum ClVariable {
/// A register variable.
#[serde(rename = "reg")]
Register {
/// The register index.
index: u32,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also a Bit variable which should be in this enum too.

/// A constant bit.
#[serde(rename = "bit")]
Bit {
/// The bit index.
index: u32,
},
}

impl Default for ClArgument {
fn default() -> Self {
ClArgument::Terminal(ClTerminal::default())
}
}

impl Default for ClTerminal {
fn default() -> Self {
ClTerminal::Int(0)
}
}

impl Default for ClVariable {
fn default() -> Self {
ClVariable::Register { index: 0 }
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! [TKET](https://github.com/CQCL/tket) quantum compiler.

pub mod circuit_json;
pub mod clexpr;
pub mod opbox;
pub mod optype;
#[cfg(feature = "pyo3")]
Expand Down
2 changes: 2 additions & 0 deletions src/opbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ pub enum OpBox {
control_state: u32,
},
/// Holding box for abstract expressions on Bits.
///
/// Deprecated in favour of [`OpType::ClExpr`].
ClassicalExpBox {
id: BoxID,
n_i: u32,
Expand Down
11 changes: 11 additions & 0 deletions src/optype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ pub enum OpType {

/// See [`ClassicalExpBox`]
///
/// Deprecated. Use [`OpType::ClExpr`] instead.
///
/// [`ClassicalExpBox`]: crate::opbox::OpBox::ClassicalExpBox
ClassicalExpBox,

Expand Down Expand Up @@ -547,4 +549,13 @@ pub enum OpType {
///
/// [`DiagonalBox`]: crate::opbox::OpBox::DiagonalBox
DiagonalBox,

/// Classical expression.
///
/// An operation of this type is accompanied by a [`ClExpr`] object.
///
/// This is a replacement of the deprecated [`OpType::ClassicalExpBox`].
///
/// [`ClExpr`]: crate::clexpr::ClExpr
ClExpr,
}
Loading
Loading