From 85185fd253465de89553fb3424f8a9863ac2b305 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Tue, 15 Mar 2022 16:54:15 -0600 Subject: [PATCH] feat: support ternary logic instructions (#60) --- src/instruction.rs | 53 +++++++++++++++++ src/parser/command.rs | 23 ++++++- src/parser/common.rs | 39 +++++++++--- src/parser/instruction.rs | 122 +++++++++++++++++++++++++++++++++++--- src/program/graph.rs | 1 + src/program/memory.rs | 30 +++++++--- src/program/mod.rs | 1 + 7 files changed, 244 insertions(+), 25 deletions(-) diff --git a/src/instruction.rs b/src/instruction.rs index b90f514a..179aae87 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -103,6 +103,44 @@ impl fmt::Display for UnaryOperator { } } +#[derive(Clone, Debug, PartialEq)] +pub enum TernaryOperand { + LiteralInteger(i64), + LiteralReal(f64), + MemoryReference(MemoryReference), +} + +impl fmt::Display for TernaryOperand { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self { + TernaryOperand::LiteralInteger(value) => write!(f, "{}", value), + TernaryOperand::LiteralReal(value) => write!(f, "{}", value), + TernaryOperand::MemoryReference(value) => write!(f, "{}", value), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum TernaryOperator { + Equal, + GreaterThanOrEqual, + GreaterThan, + LessThanOrEqual, + LessThan, +} + +impl fmt::Display for TernaryOperator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self { + TernaryOperator::Equal => write!(f, "EQ"), + TernaryOperator::GreaterThanOrEqual => write!(f, "GE"), + TernaryOperator::GreaterThan => write!(f, "GT"), + TernaryOperator::LessThanOrEqual => write!(f, "LE"), + TernaryOperator::LessThan => write!(f, "LT"), + } + } +} + #[derive(Clone, Debug, PartialEq)] pub enum AttributeValue { String(String), @@ -410,6 +448,12 @@ pub struct Arithmetic { pub source: ArithmeticOperand, } +#[derive(Clone, Debug, PartialEq)] +pub struct TernaryLogic { + pub operator: TernaryOperator, + pub operands: (MemoryReference, MemoryReference, TernaryOperand), +} + #[derive(Clone, Debug, PartialEq)] pub struct BinaryLogic { pub operator: BinaryOperator, @@ -493,6 +537,7 @@ pub enum Instruction { SwapPhases(SwapPhases), WaveformDefinition(WaveformDefinition), Arithmetic(Arithmetic), + TernaryLogic(TernaryLogic), BinaryLogic(BinaryLogic), UnaryLogic(UnaryLogic), Halt, @@ -541,6 +586,7 @@ impl From<&Instruction> for InstructionRole { | Instruction::ShiftPhase(_) | Instruction::SwapPhases(_) => InstructionRole::RFControl, Instruction::Arithmetic(_) + | Instruction::TernaryLogic(_) | Instruction::BinaryLogic(_) | Instruction::UnaryLogic(_) | Instruction::Move(_) @@ -863,6 +909,13 @@ impl fmt::Display for Instruction { write!(f, "JUMP-WHEN @{} {}", target, condition) } Instruction::Label(Label(label)) => write!(f, "LABEL @{}", label), + Instruction::TernaryLogic(TernaryLogic { operator, operands }) => { + write!( + f, + "{} {} {} {}", + operator, operands.0, operands.1, operands.2 + ) + } Instruction::BinaryLogic(BinaryLogic { operator, operands }) => { write!(f, "{} {} {}", operator, operands.0, operands.1) } diff --git a/src/parser/command.rs b/src/parser/command.rs index 7aa534ac..390f94ad 100644 --- a/src/parser/command.rs +++ b/src/parser/command.rs @@ -23,8 +23,8 @@ use crate::instruction::{ Arithmetic, ArithmeticOperand, ArithmeticOperator, BinaryLogic, BinaryOperator, Calibration, Capture, CircuitDefinition, Declaration, Delay, Exchange, Fence, FrameDefinition, Instruction, Jump, JumpUnless, JumpWhen, Label, Load, Measurement, Move, Pragma, Pulse, RawCapture, Reset, - SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, Store, UnaryLogic, UnaryOperator, - Waveform, WaveformDefinition, + SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, Store, TernaryLogic, + TernaryOperator, UnaryLogic, UnaryOperator, Waveform, WaveformDefinition, }; use crate::parser::common::parse_variable_qubit; use crate::parser::instruction::parse_block; @@ -58,6 +58,25 @@ pub fn parse_arithmetic( )) } +/// Parse a logical ternary instruction of the form `addr addr ( addr | number )`. +/// Called using the logical operator itself (such as `EQ`) which should be previously parsed. +pub fn parse_logical_ternary( + operator: TernaryOperator, + input: ParserInput, +) -> ParserResult { + let (input, destination) = common::parse_memory_reference(input)?; + let (input, left) = common::parse_memory_reference(input)?; + let (input, right) = common::parse_ternary_logic_operand(input)?; + + Ok(( + input, + Instruction::TernaryLogic(TernaryLogic { + operator, + operands: (destination, left, right), + }), + )) +} + /// Parse a logical binary instruction of the form `addr ( addr | INT )`. /// Called using the logical operator itself (such as `AND`) which should be previously parsed. pub fn parse_logical_binary( diff --git a/src/parser/common.rs b/src/parser/common.rs index dde463f0..c985c46a 100644 --- a/src/parser/common.rs +++ b/src/parser/common.rs @@ -27,7 +27,7 @@ use crate::{ expression::Expression, instruction::{ ArithmeticOperand, AttributeValue, BinaryOperand, FrameIdentifier, GateModifier, - MemoryReference, Qubit, ScalarType, Vector, WaveformInvocation, + MemoryReference, Qubit, ScalarType, TernaryOperand, Vector, WaveformInvocation, }, parser::lexer::Operator, token, @@ -65,9 +65,36 @@ pub fn parse_arithmetic_operand<'a>(input: ParserInput<'a>) -> ParserResult<'a, ArithmeticOperand::LiteralInteger(sign * (v as i64)) }, ), - map(parse_memory_reference, |f| { - ArithmeticOperand::MemoryReference(f) - }), + map(parse_memory_reference, ArithmeticOperand::MemoryReference), + ))(input) +} + +/// Parse the operand of a ternary instruction, which may be a literal integer, literal real number, or memory reference. +pub fn parse_ternary_logic_operand<'a>(input: ParserInput<'a>) -> ParserResult<'a, TernaryOperand> { + alt(( + map( + tuple((opt(token!(Operator(o))), token!(Float(v)))), + |(op, v)| { + let sign = match op { + None => 1f64, + Some(Operator::Minus) => -1f64, + _ => panic!("Implement this error"), // TODO + }; + TernaryOperand::LiteralReal(sign * v) + }, + ), + map( + tuple((opt(token!(Operator(o))), token!(Integer(v)))), + |(op, v)| { + let sign = match op { + None => 1, + Some(Operator::Minus) => -1, + _ => panic!("Implement this error"), // TODO + }; + TernaryOperand::LiteralInteger(sign * (v as i64)) + }, + ), + map(parse_memory_reference, TernaryOperand::MemoryReference), ))(input) } @@ -85,9 +112,7 @@ pub fn parse_binary_logic_operand<'a>(input: ParserInput<'a>) -> ParserResult<'a BinaryOperand::LiteralInteger(sign * (v as i64)) }, ), - map(parse_memory_reference, |f| { - BinaryOperand::MemoryReference(f) - }), + map(parse_memory_reference, BinaryOperand::MemoryReference), ))(input) } diff --git a/src/parser/instruction.rs b/src/parser/instruction.rs index a73c708c..3e344cd5 100644 --- a/src/parser/instruction.rs +++ b/src/parser/instruction.rs @@ -20,7 +20,9 @@ use nom::{ }; use crate::{ - instruction::{ArithmeticOperator, BinaryOperator, Instruction, UnaryOperator}, + instruction::{ + ArithmeticOperator, BinaryOperator, Instruction, TernaryOperator, UnaryOperator, + }, token, }; @@ -54,10 +56,18 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult { Command::DefWaveform => command::parse_defwaveform(remainder), Command::Delay => command::parse_delay(remainder), Command::Div => command::parse_arithmetic(ArithmeticOperator::Divide, remainder), - // Command::Eq => {} + Command::Eq => command::parse_logical_ternary(TernaryOperator::Equal, remainder), + Command::GE => { + command::parse_logical_ternary(TernaryOperator::GreaterThanOrEqual, remainder) + } + Command::GT => { + command::parse_logical_ternary(TernaryOperator::GreaterThan, remainder) + } + Command::LE => { + command::parse_logical_ternary(TernaryOperator::LessThanOrEqual, remainder) + } + Command::LT => command::parse_logical_ternary(TernaryOperator::LessThan, remainder), Command::Fence => command::parse_fence(remainder), - // Command::GE => {} - // Command::GT => {} Command::Halt => Ok((remainder, Instruction::Halt)), // Command::Include => {} Command::Ior => command::parse_logical_binary(BinaryOperator::Ior, remainder), @@ -65,9 +75,7 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult { Command::JumpUnless => command::parse_jump_unless(remainder), Command::JumpWhen => command::parse_jump_when(remainder), Command::Label => command::parse_label(remainder), - // Command::LE => {} Command::Load => command::parse_load(remainder), - // Command::LT => {} Command::Measure => command::parse_measurement(remainder), Command::Move => command::parse_move(remainder), Command::Exchange => command::parse_exchange(remainder), @@ -152,8 +160,9 @@ mod tests { Arithmetic, ArithmeticOperand, ArithmeticOperator, AttributeValue, BinaryLogic, BinaryOperand, BinaryOperator, Calibration, Capture, FrameDefinition, FrameIdentifier, Gate, Instruction, Jump, JumpWhen, Label, MemoryReference, Move, Pulse, Qubit, RawCapture, - Reset, SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, UnaryLogic, - UnaryOperator, Waveform, WaveformDefinition, WaveformInvocation, + Reset, SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, TernaryLogic, + TernaryOperand, TernaryOperator, UnaryLogic, UnaryOperator, Waveform, WaveformDefinition, + WaveformInvocation, }; use crate::parser::lexer::lex; use crate::{make_test, real}; @@ -237,6 +246,103 @@ mod tests { ] ); + make_test!( + ternary_logic, + parse_instructions, + "EQ dest ro 0\nLT dest ro[1] -1\nLE dest ro 1.2\nGT dest ro[2] 1e-6\nGE dest ro x", + vec![ + Instruction::TernaryLogic(TernaryLogic { + operator: TernaryOperator::Equal, + operands: ( + MemoryReference { + name: "dest".to_owned(), + index: 0 + }, + MemoryReference { + name: "ro".to_owned(), + index: 0 + }, + TernaryOperand::LiteralInteger(0) + ) + }), + Instruction::TernaryLogic(TernaryLogic { + operator: TernaryOperator::LessThan, + operands: ( + MemoryReference { + name: "dest".to_owned(), + index: 0 + }, + MemoryReference { + name: "ro".to_owned(), + index: 1 + }, + TernaryOperand::LiteralInteger(-1) + ) + }), + Instruction::TernaryLogic(TernaryLogic { + operator: TernaryOperator::LessThanOrEqual, + operands: ( + MemoryReference { + name: "dest".to_owned(), + index: 0 + }, + MemoryReference { + name: "ro".to_owned(), + index: 0 + }, + TernaryOperand::LiteralReal(1.2) + ) + }), + Instruction::TernaryLogic(TernaryLogic { + operator: TernaryOperator::GreaterThan, + operands: ( + MemoryReference { + name: "dest".to_owned(), + index: 0 + }, + MemoryReference { + name: "ro".to_owned(), + index: 2 + }, + TernaryOperand::LiteralReal(0.000001) + ) + }), + Instruction::TernaryLogic(TernaryLogic { + operator: TernaryOperator::GreaterThanOrEqual, + operands: ( + MemoryReference { + name: "dest".to_owned(), + index: 0 + }, + MemoryReference { + name: "ro".to_owned(), + index: 0 + }, + TernaryOperand::MemoryReference(MemoryReference { + name: "x".to_owned(), + index: 0 + }), + ) + }) + ] + ); + + #[test] + fn test_ternary_logic_error() { + [ + "EQ ro 1 1", + "LT 1 1 1", + "LE 1 x ro", + "GT 1 ro x", + "GE dest 0.3 4", + ] + .iter() + .for_each(|input| { + let tokens = lex(input).unwrap(); + assert!(parse_instructions(&tokens).is_err()) + }) + } + make_test!( binary_logic, parse_instructions, diff --git a/src/program/graph.rs b/src/program/graph.rs index df9f1e0c..de09abd7 100644 --- a/src/program/graph.rs +++ b/src/program/graph.rs @@ -511,6 +511,7 @@ impl ScheduledProgram { let instruction_index = Some(index); match instruction { Instruction::Arithmetic(_) + | Instruction::TernaryLogic(_) | Instruction::BinaryLogic(_) | Instruction::UnaryLogic(_) | Instruction::Capture(_) diff --git a/src/program/memory.rs b/src/program/memory.rs index 7335fe35..cfe06fe4 100644 --- a/src/program/memory.rs +++ b/src/program/memory.rs @@ -20,7 +20,8 @@ use crate::instruction::{ Arithmetic, ArithmeticOperand, BinaryLogic, BinaryOperand, Capture, CircuitDefinition, Delay, Exchange, Gate, GateDefinition, Instruction, Jump, JumpUnless, JumpWhen, Label, Load, MeasureCalibrationDefinition, Measurement, MemoryReference, Move, Pulse, RawCapture, SetPhase, - SetScale, ShiftPhase, Store, UnaryLogic, Vector, WaveformInvocation, + SetScale, ShiftPhase, Store, TernaryLogic, TernaryOperand, UnaryLogic, Vector, + WaveformInvocation, }; #[derive(Clone, Debug, Hash, PartialEq)] @@ -90,18 +91,31 @@ impl Instruction { /// Return all memory accesses by the instruction - in expressions, captures, and memory manipulation pub fn get_memory_accesses(&self) -> MemoryAccesses { match self { + Instruction::TernaryLogic(TernaryLogic { operands, .. }) => { + let mut reads = HashSet::from([operands.1.name.clone()]); + let writes = HashSet::from([operands.0.name.clone()]); + if let TernaryOperand::MemoryReference(mem) = &operands.2 { + reads.insert(mem.name.clone()); + } + + MemoryAccesses { + reads, + writes, + ..Default::default() + } + } Instruction::BinaryLogic(BinaryLogic { operands, .. }) => { - let mut r_memories = HashSet::new(); - let mut w_memories = HashSet::new(); - r_memories.insert(operands.0.name.clone()); - w_memories.insert(operands.0.name.clone()); + let mut reads = HashSet::new(); + let mut writes = HashSet::new(); + reads.insert(operands.0.name.clone()); + writes.insert(operands.0.name.clone()); if let BinaryOperand::MemoryReference(mem) = &operands.1 { - r_memories.insert(mem.name.clone()); + reads.insert(mem.name.clone()); } MemoryAccesses { - reads: r_memories, - writes: w_memories, + reads, + writes, ..Default::default() } } diff --git a/src/program/mod.rs b/src/program/mod.rs index 3dd5dfc8..1926354b 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -173,6 +173,7 @@ impl Program { | Instruction::Pragma(_) | Instruction::WaveformDefinition(_) | Instruction::Arithmetic(_) + | Instruction::TernaryLogic(_) | Instruction::BinaryLogic(_) | Instruction::UnaryLogic(_) | Instruction::Halt