push
This commit is contained in:
commit
dcf80a5974
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/target/
|
||||
/Cargo.lock
|
||||
/test.qASM
|
||||
/mem.bin
|
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "q_asm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitvec = "1.0.1"
|
||||
clap = { version = "4.2.7", features = ["derive"] }
|
||||
codespan-reporting = "0.11.1"
|
||||
lazy_static = "1.4.0"
|
||||
nalgebra = "0.32.2"
|
||||
num-traits = "0.2.15"
|
||||
rand = "0.8.5"
|
||||
lalrpop-util = { version = "0.20.0", features = ["lexer"] }
|
||||
|
||||
[build_dependencies]
|
||||
lalrpop = { version = "0.20.0", features = ["lexer"] }
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 zkdream
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
127
README.md
Normal file
127
README.md
Normal file
|
@ -0,0 +1,127 @@
|
|||
```
|
||||
qbits n
|
||||
cbits n
|
||||
qregs n
|
||||
cregs n
|
||||
mem n
|
||||
|
||||
<code>
|
||||
```
|
||||
|
||||
The order of the headers is important and should not be changed. The `qbits` header specifies the number of
|
||||
qubits in each quantum register. The `cbits` header specifies the number of classical bits in each classical
|
||||
register. The `qregs` header specifies the number of quantum registers. The `cregs` header specifies the number
|
||||
of classical registers. The `n` after each header is any integer number. Operands/arguments for all
|
||||
instructions are delimited by spaces and not commas.
|
||||
|
||||
Code execution should always end at a `hlt` instruction. If the emulator reaches the end of code but does not
|
||||
encounter a `hlt` instruction, it will end with a "PC out of bounds" error.
|
||||
|
||||
## Quantum Instructions
|
||||
The general format for quantum instructions are:
|
||||
```
|
||||
op qn <other arguments>
|
||||
```
|
||||
Where `op` is the name/opcode, `qn` specifies a specific qubit `n` of the currently selected quantum register.
|
||||
`<other arguments>` can include more qubits as arguments, or in the case of some instructions, a rotation expressed
|
||||
as a rational multiple of pi, in the format `[n]pi[/n]`, where `n` can be any integer number, and items
|
||||
in `[]` are optional. Quantum registers can be selected via the `qsel` instruction, which has the general format
|
||||
`qsel qrn` where `n` is any non-negative number.
|
||||
|
||||
List of currently implemented quantum instructions:
|
||||
|
||||
| Quantum Gate | Instruction name | Syntax example | Explanation |
|
||||
| ------------------- | ---------------- | ------------------- | ----------- |
|
||||
| Hadamard | h | `h q0` | Applies a Hadamard to qubit 0 |
|
||||
| CNOT | cnot | `cnot q0 q1` | Applies a CNOT to qubit 1 with qubit 0 being the control |
|
||||
| CCNOT/Toffoli | ccnot | `ccnot q0 q1 q2` | Applies a Toffoli to qubit 2 with qubit 0 and qubit 1 being the controls |
|
||||
| Pauli X | x | `x q0` | Applies a Pauli X to qubit 0 |
|
||||
| Pauli Y | y | `y q0` | Applies a Pauli Y to qubit 0 |
|
||||
| Pauli Z | z | `z q0` | Applies a Pauli Z to qubit 0 |
|
||||
| Rx | rx | `rx q0 pi/3` | Rotates the statevector of qubit 0 by pi/3 radians along X axis on bloch sphere |
|
||||
| Ry | ry | `ry q0 pi` | Rotates the statevector of qubit 0 by pi radians along Y axis on bloch sphere |
|
||||
| Rz | rz | `rz q0 pi/4` | Rotates the statevector of qubit 0 by pi/4 radians along Z axis on bloch sphere |
|
||||
| U gate | u | `u q0 pi pi/3 pi/6` | Rotates the statevector of qubit 0 by the 3 Euler angles pi, pi/3, pi/6 |
|
||||
| S gate | s | `s q0` | Applies an S gate to qubit 0 |
|
||||
| T gate | t | `t q0` | Applies a T gate to qubit 0 |
|
||||
| S-dagger | sdg | `sdg q0` | Applies a S-dagger or the inverse of S gate to qubit 0 |
|
||||
| T-dagger | tdg | `tdg q0` | Applies a T-dagger or the inverse of T gate to qubit 0 |
|
||||
| Phase gate | p | `p q0 pi/3` | Applies a relative phase of pi/3 radians to qubit 0 |
|
||||
| Controlled Hadamard | ch | `ch q0 q1` | Applies a controlled Hadamard to qubit 1 with qubit 0 being the control |
|
||||
| Controlled Pauli Y | cy | `cy q0 q1` | Applies a controlled Pauli Y to qubit 1 with qubit 0 being the control |
|
||||
| Controlled Pauli Z | cz | `cz q0 q1` | Applies a controlled Pauli Z to qubit 1 with qubit 0 being the control |
|
||||
| Controlled Phase | cp | `cp q0 q1 pi/2` | Applies a controlled Phase gate to qubit 1 of pi/2 radians with qubit 0 being the control |
|
||||
| Swap | swap | `swap q0 q1` | Swaps the state of qubits 0 and 1 |
|
||||
| Square Root NOT | sqrtx | `sqrtx q0 ` | Applies a sqrt(NOT)/sqrt(X) to qubit 0 |
|
||||
| Square Root Swap | sqrtswp | `sqrtswp q0 q1` | Applies a sqrt(Swap) to qubits 0 and 1, halfway swapping their state |
|
||||
| Controlled Swap | cswap | `cswap q0 q1 q2` | Swaps the state of qubits 1 and 2 with qubit 0 being the control |
|
||||
| Measure | m | `m q0 cr1 c3` | Measures the state of qubit 0 into 3rd bit of classical register 1 |
|
||||
|
||||
*Note: Remove any measurement operations before running the emulator with `--print-state` (or `-p`) as the emulator does not ignore them currently when run with that flag set*
|
||||
|
||||
## Classical Instructions
|
||||
General format for classical instructions are:
|
||||
```
|
||||
op <operands>
|
||||
```
|
||||
Where `op` is the name/opcode, operands may include `crn`, which specifies a specific classical register `n`, or
|
||||
an immediate literal value (for now non-negative due to not implemented in parser yet) Other than these differences,
|
||||
they behave basically the same as any other assembly language instructions.
|
||||
|
||||
List of currently implemented classical instructions:
|
||||
|
||||
*Note: The value of a register refers to the value stored in the register. The value of an immediate is the immediate number itself.*
|
||||
|
||||
*Note 2: An operand can either be a register or immediate unless a restriction is specified.*
|
||||
|
||||
| Instruction name | Description |
|
||||
| ---------------- | ----------- |
|
||||
| add | op1 = op2 + op3. op1 is always a register. |
|
||||
| sub | op1 = op2 - op3. op1 is always a register. |
|
||||
| mult | op1 = op2 * op3. op1 is always a register. All values are treated unsigned. |
|
||||
| umult | op1 = (op2 * op3) >> (cbits/2). op1 is always a register. All values are treated unsigned. |
|
||||
| div | op1 = op2 / op3. op1 is always a register. Performs integer division. All values are treated unsigned. |
|
||||
| smult | op1 = op2 * op3. op1 is always a register. All values are treated signed. |
|
||||
| sumult | op1 = (op2 * op3) >> (cbits/2). op1 is always a register. All values are treated signed. |
|
||||
| sdiv | op1 = op2 / op3. op1 is always a register. Performs integer division. All values are treated signed. |
|
||||
| not | op1 = ~op2. op1 is always a register. |
|
||||
| and | op1 = op2 & op3. op1 is always a register. |
|
||||
| or | op1 = op2 \| op3. op1 is always a register. |
|
||||
| xor | op1 = op2 ^ op3. op1 is always a register. |
|
||||
| nand | op1 = ~(op2 & op3). op1 is always a register. |
|
||||
| nor | op1 = ~(op2 \| op3). op1 is always a register. |
|
||||
| xnor | op1 = ~(op2 ^ op3). op1 is always a register. |
|
||||
|
||||
## Misc. Instructions
|
||||
These instructions are here because.
|
||||
|
||||
| Instruction name | Description |
|
||||
| ---------------- | ----------- |
|
||||
| qsel | Selects a quantum register so that proceeding quantum instructions act on that qreg. |
|
||||
| cmp | Updates flags based on comparing values in op1 and op2. op1 is always a register. |
|
||||
| jmp | Unconditionally jump to a label. |
|
||||
| jeq | Jump to label if comparison resulted in EQ flag set. |
|
||||
| jne | Jump to label if comparsion did not result in EQ flag set. |
|
||||
| jg | Jump to label if comparison resulted in GREATER flag set. |
|
||||
| jge | Jump to label if comparison resulted in GREATER or EQ flag set. |
|
||||
| jl | Jump to label if comparison resulted in LESSER flag set. |
|
||||
| jle | Jump to label if comparison resulted in LESSER or EQ flag set. |
|
||||
| hlt | Halt the program. |
|
||||
|
||||
# Examples
|
||||
This program simulates the $\ket{\Phi^+}$ bell state:
|
||||
```
|
||||
qbits 2
|
||||
cbits 2
|
||||
qregs 1
|
||||
cregs 1
|
||||
|
||||
qsel qr0
|
||||
h q0
|
||||
cnot q0 q1
|
||||
|
||||
m q0 cr0 c0
|
||||
m q1 cr0 c1
|
||||
|
||||
hlt
|
||||
```
|
634
src/ast.rs
Normal file
634
src/ast.rs
Normal file
|
@ -0,0 +1,634 @@
|
|||
use std::{collections::HashMap, fmt::Display, ops::Range};
|
||||
|
||||
use lalrpop_util::{lalrpop_mod, ParseError};
|
||||
lalrpop_mod!(grammar);
|
||||
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
|
||||
type CGateInfo = (usize, Vec<(String, IdentType)>, Vec<ResolvedInst>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Program {
|
||||
// (qbits, cbits, qregs, cregs, mem_size)
|
||||
pub headers: (usize, usize, usize, usize, usize),
|
||||
pub custom_gates: HashMap<String, CGateInfo>,
|
||||
pub instructions: Vec<ResolvedInst>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SourceSpan {
|
||||
pub file: usize,
|
||||
pub span: Range<usize>,
|
||||
}
|
||||
impl SourceSpan {
|
||||
pub fn new(file: usize, span: Range<usize>) -> Self {
|
||||
Self { file, span }
|
||||
}
|
||||
}
|
||||
|
||||
type ParseResult<'a> = Result<Program, ResolveError>;
|
||||
|
||||
pub fn parse(code: &str, file: usize) -> (ParseResult, Vec<Diagnostic<usize>>) {
|
||||
let mut errs = Vec::new();
|
||||
let ast = grammar::ProgramParser::new().parse(file, &mut errs, code);
|
||||
let mut diags = Vec::new();
|
||||
|
||||
for err in errs {
|
||||
let diag = Diagnostic::error();
|
||||
match err {
|
||||
ParseError::InvalidToken { location } => diags.push(
|
||||
diag.with_message("Invalid token")
|
||||
.with_labels(vec![Label::primary(file, location..location)]),
|
||||
),
|
||||
|
||||
ParseError::UnrecognizedEof { location, .. } => diags.push(
|
||||
diag.with_message("Unexpected EOF")
|
||||
.with_labels(vec![Label::primary(file, location..location)]),
|
||||
),
|
||||
|
||||
ParseError::UnrecognizedToken { token, expected } => diags.push(
|
||||
diag.with_message("Unrecognized token")
|
||||
.with_labels(vec![Label::primary(file, token.0..token.2)])
|
||||
.with_notes(expected),
|
||||
),
|
||||
|
||||
ParseError::ExtraToken { token } => diags.push(
|
||||
diag.with_message("Extra token found")
|
||||
.with_labels(vec![Label::primary(file, token.0..token.2)]),
|
||||
),
|
||||
|
||||
ParseError::User { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(ast) = ast {
|
||||
(ast, diags)
|
||||
} else {
|
||||
let mut diag = Diagnostic::error();
|
||||
let err = ast.unwrap_err();
|
||||
match err {
|
||||
ParseError::InvalidToken { location } => {
|
||||
diag = diag
|
||||
.with_message("Invalid token")
|
||||
.with_labels(vec![Label::primary(file, location..location)])
|
||||
}
|
||||
|
||||
ParseError::UnrecognizedEof { location, .. } => {
|
||||
diag = diag
|
||||
.with_message("Unexpected EOF")
|
||||
.with_labels(vec![Label::primary(file, location..location)])
|
||||
}
|
||||
|
||||
ParseError::UnrecognizedToken { token, .. } => {
|
||||
diag = diag
|
||||
.with_message("Unrecognized token")
|
||||
.with_labels(vec![Label::primary(file, token.0..token.2)])
|
||||
}
|
||||
|
||||
ParseError::ExtraToken { token } => {
|
||||
diag = diag
|
||||
.with_message("Extra token found")
|
||||
.with_labels(vec![Label::primary(file, token.0..token.2)])
|
||||
}
|
||||
|
||||
ParseError::User { .. } => todo!(),
|
||||
}
|
||||
|
||||
(Err(ResolveError::ParseError(diag)), diags)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_ast(
|
||||
headers: (usize, usize, usize, usize, usize),
|
||||
ast: Vec<Inst>,
|
||||
) -> Result<Program, ResolveError> {
|
||||
let mut label_map = HashMap::new();
|
||||
let mut custom_gates = HashMap::new();
|
||||
let mut resolved_insts: Vec<ResolvedInst> = Vec::new();
|
||||
|
||||
// Get all labels from the AST
|
||||
let mut i = 0usize;
|
||||
for inst in &ast {
|
||||
if let Inst::Label(name) = inst {
|
||||
label_map.insert(name, i);
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
// Update all instructions with the label map
|
||||
for inst in &ast {
|
||||
match inst {
|
||||
Inst::Label(_) => {}
|
||||
|
||||
Inst::Jmp(name, s) => {
|
||||
if let Some(offset) = label_map.get(name) {
|
||||
resolved_insts.push(ResolvedInst::Jmp(*offset, s.clone()))
|
||||
} else {
|
||||
return Err(ResolveError::UndefinedLabel(name.clone(), s.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Jeq(name, s) => {
|
||||
if let Some(offset) = label_map.get(name) {
|
||||
resolved_insts.push(ResolvedInst::Jeq(*offset, s.clone()))
|
||||
} else {
|
||||
return Err(ResolveError::UndefinedLabel(name.clone(), s.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Jne(name, s) => {
|
||||
if let Some(offset) = label_map.get(name) {
|
||||
resolved_insts.push(ResolvedInst::Jne(*offset, s.clone()))
|
||||
} else {
|
||||
return Err(ResolveError::UndefinedLabel(name.clone(), s.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Jg(name, s) => {
|
||||
if let Some(offset) = label_map.get(name) {
|
||||
resolved_insts.push(ResolvedInst::Jg(*offset, s.clone()))
|
||||
} else {
|
||||
return Err(ResolveError::UndefinedLabel(name.clone(), s.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Jge(name, s) => {
|
||||
if let Some(offset) = label_map.get(name) {
|
||||
resolved_insts.push(ResolvedInst::Jge(*offset, s.clone()))
|
||||
} else {
|
||||
return Err(ResolveError::UndefinedLabel(name.clone(), s.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Jl(name, s) => {
|
||||
if let Some(offset) = label_map.get(name) {
|
||||
resolved_insts.push(ResolvedInst::Jle(*offset, s.clone()))
|
||||
} else {
|
||||
return Err(ResolveError::UndefinedLabel(name.clone(), s.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
// Custom instruction stuff
|
||||
Inst::CustomGateDef(name, qbits, args, body) => {
|
||||
let body = resolve_ast(headers, body.clone())?.instructions;
|
||||
custom_gates.insert(name.clone(), (*qbits, args.clone(), body));
|
||||
}
|
||||
Inst::Custom(name, qbits, args, s) => {
|
||||
if let Some(gate) = custom_gates.get(name) {
|
||||
let gate_qbits = gate.0;
|
||||
let gate_args = &gate.1;
|
||||
let diag = Diagnostic::error();
|
||||
|
||||
if qbits.len() != gate_qbits {
|
||||
return Err(ResolveError::CustomGateError(
|
||||
diag.with_message("Given qubits to gate don't match gate definition")
|
||||
.with_labels(vec![Label::primary(s.file, s.span.clone())])
|
||||
.with_notes(vec![format!(
|
||||
"Note: Gate expects {} qubits, but {} were given",
|
||||
gate_qbits,
|
||||
qbits.len()
|
||||
)]),
|
||||
));
|
||||
}
|
||||
if args.iter().map(|x| x.get_type()).collect::<Vec<_>>()
|
||||
!= gate_args.iter().map(|x| x.1).collect::<Vec<_>>()
|
||||
{
|
||||
return Err(ResolveError::CustomGateError(
|
||||
diag.with_message("Given args to gate don't match gate definition")
|
||||
.with_labels(vec![Label::primary(s.file, s.span.clone())])
|
||||
.with_notes(vec![format!(
|
||||
"Note: Gate expects arg types {}, but given arg types were {}",
|
||||
format_vec(gate_args.iter().map(|x| x.1).collect()),
|
||||
format_vec(args.iter().map(|x| x.get_type()).collect()),
|
||||
)]),
|
||||
));
|
||||
}
|
||||
|
||||
resolved_insts.push(inst.into())
|
||||
} else {
|
||||
return Err(ResolveError::UndefinedGate(name.clone(), s.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Err => {}
|
||||
|
||||
_ => resolved_insts.push(inst.into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Program {
|
||||
headers,
|
||||
custom_gates,
|
||||
instructions: resolved_insts,
|
||||
})
|
||||
}
|
||||
|
||||
fn format_vec<T: Display>(vec: Vec<T>) -> String {
|
||||
let mut s = String::from("[");
|
||||
for e in vec {
|
||||
s += format!("{}, ", e).as_str()
|
||||
}
|
||||
s.pop();
|
||||
s.pop();
|
||||
s += "]";
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Inst {
|
||||
// Quantum Instructions
|
||||
Qsel(usize, SourceSpan),
|
||||
Id(usize, SourceSpan),
|
||||
Hadamard(usize, SourceSpan),
|
||||
Cnot(usize, usize, SourceSpan),
|
||||
Ccnot(usize, usize, usize, SourceSpan),
|
||||
X(usize, SourceSpan),
|
||||
Y(usize, SourceSpan),
|
||||
Z(usize, SourceSpan),
|
||||
Rx(usize, Rotation, SourceSpan),
|
||||
Ry(usize, Rotation, SourceSpan),
|
||||
Rz(usize, Rotation, SourceSpan),
|
||||
U(usize, Rotation, Rotation, Rotation, SourceSpan),
|
||||
S(usize, SourceSpan),
|
||||
T(usize, SourceSpan),
|
||||
Sdg(usize, SourceSpan),
|
||||
Tdg(usize, SourceSpan),
|
||||
Phase(usize, Rotation, SourceSpan),
|
||||
Ch(usize, usize, SourceSpan),
|
||||
Cy(usize, usize, SourceSpan),
|
||||
Cz(usize, usize, SourceSpan),
|
||||
CPhase(usize, usize, Rotation, SourceSpan),
|
||||
Swap(usize, usize, SourceSpan),
|
||||
SqrtX(usize, SourceSpan),
|
||||
SqrtSwap(usize, usize, SourceSpan),
|
||||
CSwap(usize, usize, usize, SourceSpan),
|
||||
Measure(usize, usize, usize, SourceSpan),
|
||||
|
||||
// Custom gate stuff
|
||||
// (name, qbits, args, body)
|
||||
CustomGateDef(String, usize, Vec<(String, IdentType)>, Vec<Inst>),
|
||||
Custom(String, Vec<usize>, Vec<IdentVal>, SourceSpan),
|
||||
|
||||
// Classical Instructions
|
||||
Mov(Operand, Operand, SourceSpan),
|
||||
Add(Operand, Operand, Operand, SourceSpan),
|
||||
Sub(Operand, Operand, Operand, SourceSpan),
|
||||
Mul(Operand, Operand, Operand, SourceSpan),
|
||||
UMul(Operand, Operand, Operand, SourceSpan),
|
||||
Div(Operand, Operand, Operand, SourceSpan),
|
||||
SMul(Operand, Operand, Operand, SourceSpan),
|
||||
SUMul(Operand, Operand, Operand, SourceSpan),
|
||||
SDiv(Operand, Operand, Operand, SourceSpan),
|
||||
Not(Operand, Operand, SourceSpan),
|
||||
And(Operand, Operand, Operand, SourceSpan),
|
||||
Or(Operand, Operand, Operand, SourceSpan),
|
||||
Xor(Operand, Operand, Operand, SourceSpan),
|
||||
Nand(Operand, Operand, Operand, SourceSpan),
|
||||
Nor(Operand, Operand, Operand, SourceSpan),
|
||||
Xnor(Operand, Operand, Operand, SourceSpan),
|
||||
|
||||
// Misc
|
||||
Cmp(Operand, Operand, SourceSpan),
|
||||
Jmp(String, SourceSpan),
|
||||
Jeq(String, SourceSpan),
|
||||
Jne(String, SourceSpan),
|
||||
Jg(String, SourceSpan),
|
||||
Jge(String, SourceSpan),
|
||||
Jl(String, SourceSpan),
|
||||
Jle(String, SourceSpan),
|
||||
Hlt,
|
||||
|
||||
Label(String),
|
||||
|
||||
Err,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ResolvedInst {
|
||||
// Quantum Instructions
|
||||
Qsel(usize, SourceSpan),
|
||||
Id(usize, SourceSpan),
|
||||
Hadamard(usize, SourceSpan),
|
||||
Cnot(usize, usize, SourceSpan),
|
||||
Ccnot(usize, usize, usize, SourceSpan),
|
||||
X(usize, SourceSpan),
|
||||
Y(usize, SourceSpan),
|
||||
Z(usize, SourceSpan),
|
||||
Rx(usize, Rotation, SourceSpan),
|
||||
Ry(usize, Rotation, SourceSpan),
|
||||
Rz(usize, Rotation, SourceSpan),
|
||||
U(usize, Rotation, Rotation, Rotation, SourceSpan),
|
||||
S(usize, SourceSpan),
|
||||
T(usize, SourceSpan),
|
||||
Sdg(usize, SourceSpan),
|
||||
Tdg(usize, SourceSpan),
|
||||
Phase(usize, Rotation, SourceSpan),
|
||||
Ch(usize, usize, SourceSpan),
|
||||
Cy(usize, usize, SourceSpan),
|
||||
Cz(usize, usize, SourceSpan),
|
||||
CPhase(usize, usize, Rotation, SourceSpan),
|
||||
Swap(usize, usize, SourceSpan),
|
||||
SqrtX(usize, SourceSpan),
|
||||
SqrtSwap(usize, usize, SourceSpan),
|
||||
CSwap(usize, usize, usize, SourceSpan),
|
||||
Measure(usize, usize, usize, SourceSpan),
|
||||
|
||||
// Custom gate stuff
|
||||
Custom(String, Vec<usize>, Vec<IdentVal>, SourceSpan),
|
||||
|
||||
// Classical Instructions
|
||||
Mov(Operand, Operand, SourceSpan),
|
||||
Add(Operand, Operand, Operand, SourceSpan),
|
||||
Sub(Operand, Operand, Operand, SourceSpan),
|
||||
Mul(Operand, Operand, Operand, SourceSpan),
|
||||
UMul(Operand, Operand, Operand, SourceSpan),
|
||||
Div(Operand, Operand, Operand, SourceSpan),
|
||||
SMul(Operand, Operand, Operand, SourceSpan),
|
||||
SUMul(Operand, Operand, Operand, SourceSpan),
|
||||
SDiv(Operand, Operand, Operand, SourceSpan),
|
||||
Not(Operand, Operand, SourceSpan),
|
||||
And(Operand, Operand, Operand, SourceSpan),
|
||||
Or(Operand, Operand, Operand, SourceSpan),
|
||||
Xor(Operand, Operand, Operand, SourceSpan),
|
||||
Nand(Operand, Operand, Operand, SourceSpan),
|
||||
Nor(Operand, Operand, Operand, SourceSpan),
|
||||
Xnor(Operand, Operand, Operand, SourceSpan),
|
||||
|
||||
// Misc
|
||||
Cmp(Operand, Operand, SourceSpan),
|
||||
Jmp(usize, SourceSpan),
|
||||
Jeq(usize, SourceSpan),
|
||||
Jne(usize, SourceSpan),
|
||||
Jg(usize, SourceSpan),
|
||||
Jge(usize, SourceSpan),
|
||||
Jl(usize, SourceSpan),
|
||||
Jle(usize, SourceSpan),
|
||||
|
||||
Hlt,
|
||||
}
|
||||
|
||||
impl From<&Inst> for ResolvedInst {
|
||||
// The most painful damn function ever to write
|
||||
fn from(value: &Inst) -> Self {
|
||||
match value {
|
||||
// Quantum instructions
|
||||
Inst::Qsel(q, s) => Self::Qsel(*q, s.clone()),
|
||||
Inst::Id(q, s) => Self::Id(*q, s.clone()),
|
||||
Inst::Hadamard(q, s) => Self::Hadamard(*q, s.clone()),
|
||||
Inst::Cnot(q1, q2, s) => Self::Cnot(*q1, *q2, s.clone()),
|
||||
Inst::Ccnot(q1, q2, q3, s) => Self::Ccnot(*q1, *q2, *q3, s.clone()),
|
||||
Inst::X(q, s) => Self::X(*q, s.clone()),
|
||||
Inst::Y(q, s) => Self::Y(*q, s.clone()),
|
||||
Inst::Z(q, s) => Self::Z(*q, s.clone()),
|
||||
Inst::Rx(q, r, s) => Self::Rx(*q, r.clone(), s.clone()),
|
||||
Inst::Ry(q, r, s) => Self::Ry(*q, r.clone(), s.clone()),
|
||||
Inst::Rz(q, r, s) => Self::Rz(*q, r.clone(), s.clone()),
|
||||
Inst::U(q, r1, r2, r3, s) => Self::U(*q, r1.clone(), r2.clone(), r3.clone(), s.clone()),
|
||||
Inst::S(q, s) => Self::S(*q, s.clone()),
|
||||
Inst::T(q, s) => Self::T(*q, s.clone()),
|
||||
Inst::Sdg(q, s) => Self::Sdg(*q, s.clone()),
|
||||
Inst::Tdg(q, s) => Self::Tdg(*q, s.clone()),
|
||||
Inst::Phase(q, r, s) => Self::Phase(*q, r.clone(), s.clone()),
|
||||
Inst::Ch(q1, q2, s) => Self::Ch(*q1, *q2, s.clone()),
|
||||
Inst::Cy(q1, q2, s) => Self::Cy(*q1, *q2, s.clone()),
|
||||
Inst::Cz(q1, q2, s) => Self::Cz(*q1, *q2, s.clone()),
|
||||
Inst::CPhase(q1, q2, r, s) => Self::CPhase(*q1, *q2, r.clone(), s.clone()),
|
||||
Inst::Swap(q1, q2, s) => Self::Swap(*q1, *q2, s.clone()),
|
||||
Inst::SqrtX(q, s) => Self::SqrtX(*q, s.clone()),
|
||||
Inst::SqrtSwap(q1, q2, s) => Self::SqrtSwap(*q1, *q2, s.clone()),
|
||||
Inst::CSwap(q1, q2, q3, s) => Self::CSwap(*q1, *q2, *q3, s.clone()),
|
||||
Inst::Measure(q, cr, cb, s) => Self::Measure(*q, *cr, *cb, s.clone()),
|
||||
|
||||
// Custom gate stuff
|
||||
Inst::Custom(name, qbits, args, s) => {
|
||||
Self::Custom(name.clone(), qbits.clone(), args.clone(), s.clone())
|
||||
}
|
||||
|
||||
// Classical Instructions
|
||||
Inst::Mov(cr1, val, s) => Self::Mov(cr1.clone(), val.clone(), s.clone()),
|
||||
Inst::Add(cr1, cr2, cr3, s) => {
|
||||
Self::Add(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Sub(cr1, cr2, cr3, s) => {
|
||||
Self::Sub(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Mul(cr1, cr2, cr3, s) => {
|
||||
Self::Mul(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::UMul(cr1, cr2, cr3, s) => {
|
||||
Self::UMul(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Div(cr1, cr2, cr3, s) => {
|
||||
Self::Div(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::SMul(cr1, cr2, cr3, s) => {
|
||||
Self::SMul(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::SUMul(cr1, cr2, cr3, s) => {
|
||||
Self::SUMul(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::SDiv(cr1, cr2, cr3, s) => {
|
||||
Self::SDiv(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Not(cr1, cr2, s) => Self::Not(cr1.clone(), cr2.clone(), s.clone()),
|
||||
Inst::And(cr1, cr2, cr3, s) => {
|
||||
Self::And(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Or(cr1, cr2, cr3, s) => {
|
||||
Self::Or(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Xor(cr1, cr2, cr3, s) => {
|
||||
Self::Xor(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Nand(cr1, cr2, cr3, s) => {
|
||||
Self::Nand(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Nor(cr1, cr2, cr3, s) => {
|
||||
Self::Nor(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
Inst::Xnor(cr1, cr2, cr3, s) => {
|
||||
Self::Xnor(cr1.clone(), cr2.clone(), cr3.clone(), s.clone())
|
||||
}
|
||||
|
||||
// Misc
|
||||
Inst::Cmp(cr1, val, s) => Self::Cmp(cr1.clone(), val.clone(), s.clone()),
|
||||
Inst::Hlt => Self::Hlt,
|
||||
|
||||
_ => unreachable!(), /* Atleast hopefully unreachable if I didn't mess up stuff */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResolvedInst {
|
||||
pub fn get_span(&self) -> &SourceSpan {
|
||||
match self {
|
||||
Self::Qsel(_, s) => s,
|
||||
Self::Id(_, s) => s,
|
||||
Self::Hadamard(_, s) => s,
|
||||
Self::Cnot(_, _, s) => s,
|
||||
Self::Ccnot(_, _, _, s) => s,
|
||||
Self::X(_, s) => s,
|
||||
Self::Y(_, s) => s,
|
||||
Self::Z(_, s) => s,
|
||||
Self::Rx(_, _, s) => s,
|
||||
Self::Ry(_, _, s) => s,
|
||||
Self::Rz(_, _, s) => s,
|
||||
Self::U(_, _, _, _, s) => s,
|
||||
Self::S(_, s) => s,
|
||||
Self::T(_, s) => s,
|
||||
Self::Sdg(_, s) => s,
|
||||
Self::Tdg(_, s) => s,
|
||||
Self::Phase(_, _, s) => s,
|
||||
Self::Ch(_, _, s) => s,
|
||||
Self::Cy(_, _, s) => s,
|
||||
Self::Cz(_, _, s) => s,
|
||||
Self::CPhase(_, _, _, s) => s,
|
||||
Self::Swap(_, _, s) => s,
|
||||
Self::SqrtX(_, s) => s,
|
||||
Self::SqrtSwap(_, _, s) => s,
|
||||
Self::CSwap(_, _, _, s) => s,
|
||||
Self::Measure(_, _, _, s) => s,
|
||||
|
||||
// Custom gate stuff
|
||||
Self::Custom(_, _, _, s) => s,
|
||||
|
||||
// Classical Instructions
|
||||
Self::Mov(_, _, s) => s,
|
||||
Self::Add(_, _, _, s) => s,
|
||||
Self::Sub(_, _, _, s) => s,
|
||||
Self::Mul(_, _, _, s) => s,
|
||||
Self::UMul(_, _, _, s) => s,
|
||||
Self::Div(_, _, _, s) => s,
|
||||
Self::SMul(_, _, _, s) => s,
|
||||
Self::SUMul(_, _, _, s) => s,
|
||||
Self::SDiv(_, _, _, s) => s,
|
||||
Self::Not(_, _, s) => s,
|
||||
Self::And(_, _, _, s) => s,
|
||||
Self::Or(_, _, _, s) => s,
|
||||
Self::Xor(_, _, _, s) => s,
|
||||
Self::Nand(_, _, _, s) => s,
|
||||
Self::Nor(_, _, _, s) => s,
|
||||
Self::Xnor(_, _, _, s) => s,
|
||||
|
||||
// Misc
|
||||
Self::Cmp(_, _, s) => s,
|
||||
Self::Jmp(_, s) => s,
|
||||
Self::Jeq(_, s) => s,
|
||||
Self::Jne(_, s) => s,
|
||||
Self::Jg(_, s) => s,
|
||||
Self::Jge(_, s) => s,
|
||||
Self::Jl(_, s) => s,
|
||||
Self::Jle(_, s) => s,
|
||||
|
||||
Self::Hlt => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Operand {
|
||||
Imm(i64),
|
||||
Reg(usize),
|
||||
Addr(MemAddr),
|
||||
Ident(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum IdentVal {
|
||||
Rot(Rot),
|
||||
|
||||
Imm(i64),
|
||||
Reg(usize),
|
||||
Addr(MemAddr),
|
||||
}
|
||||
impl IdentVal {
|
||||
pub fn get_type(&self) -> IdentType {
|
||||
match self {
|
||||
Self::Rot(_) => IdentType::Rot,
|
||||
Self::Imm(_) => IdentType::Imm,
|
||||
Self::Reg(_) => IdentType::Reg,
|
||||
Self::Addr(_) => IdentType::MemAddr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rot(&self) -> Option<Rot> {
|
||||
match self {
|
||||
Self::Rot(rot) => Some(*rot),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<&IdentVal> for Operand {
|
||||
fn from(value: &IdentVal) -> Self {
|
||||
match value {
|
||||
IdentVal::Imm(imm) => Self::Imm(*imm),
|
||||
IdentVal::Reg(reg) => Self::Reg(*reg),
|
||||
IdentVal::Addr(addr) => Self::Addr(*addr),
|
||||
|
||||
IdentVal::Rot(_) => unreachable!(), // hopefully...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum IdentType {
|
||||
Rot,
|
||||
|
||||
Imm,
|
||||
Reg,
|
||||
MemAddr,
|
||||
}
|
||||
impl Display for IdentType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Rot => write!(f, "Rot"),
|
||||
Self::Imm => write!(f, "Imm"),
|
||||
Self::Reg => write!(f, "Reg"),
|
||||
Self::MemAddr => write!(f, "Addr"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MemAddr {
|
||||
Address(usize),
|
||||
// (reg, align, offset) = reg * align + offset
|
||||
Indirect(usize, usize, usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Rot(pub i64, pub u64);
|
||||
impl Rot {
|
||||
pub fn get_angle(&self) -> f64 {
|
||||
(self.0 as f64) * std::f64::consts::PI / (self.1 as f64)
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Rotation {
|
||||
Rot(Rot),
|
||||
Ident(String),
|
||||
}
|
||||
impl Rotation {
|
||||
pub fn get_rot(&self, args: &HashMap<String, IdentVal>) -> Option<Rot> {
|
||||
match self {
|
||||
Self::Rot(rot) => Some(*rot),
|
||||
Self::Ident(ident) => {
|
||||
if let Some(rot) = args.get(ident) {
|
||||
rot.get_rot()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ResolveError {
|
||||
UndefinedLabel(String, SourceSpan),
|
||||
ParseError(Diagnostic<usize>),
|
||||
UndefinedGate(String, SourceSpan),
|
||||
CustomGateError(Diagnostic<usize>),
|
||||
}
|
1480
src/emulator.rs
Normal file
1480
src/emulator.rs
Normal file
File diff suppressed because it is too large
Load diff
311
src/grammar.lalrpop
Normal file
311
src/grammar.lalrpop
Normal file
|
@ -0,0 +1,311 @@
|
|||
use crate::ast::*;
|
||||
|
||||
use lalrpop_util::ParseError;
|
||||
|
||||
grammar<'err>(file: usize, errors: &'err mut Vec<ParseError<usize, Token<'input>, &'static str>>);
|
||||
|
||||
match {
|
||||
"qbits",
|
||||
"cbits",
|
||||
"qregs",
|
||||
"cregs",
|
||||
"mem",
|
||||
r"q[0-9]+" => QBIT,
|
||||
r"qr[0-9]+" => QREG,
|
||||
r"cr[0-9]+" => CREG,
|
||||
r"c[0-9]+" => CBIT,
|
||||
"gate",
|
||||
"Rot",
|
||||
"Imm",
|
||||
"Reg",
|
||||
"Addr",
|
||||
r"[0-9]+" => NUM,
|
||||
":",
|
||||
"pi",
|
||||
"/",
|
||||
"[",
|
||||
"]",
|
||||
"(",
|
||||
")",
|
||||
"{",
|
||||
"}",
|
||||
"+",
|
||||
"*",
|
||||
"=",
|
||||
";",
|
||||
r"#.*" => {},
|
||||
r"\n+" => NEWLINE,
|
||||
r" +" => SPACE,
|
||||
"-",
|
||||
|
||||
// Instructions
|
||||
// Quantum instructions
|
||||
"h",
|
||||
"cnot",
|
||||
"ccnot",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"rx",
|
||||
"ry",
|
||||
"rz",
|
||||
"u",
|
||||
"s",
|
||||
"t",
|
||||
"sdg",
|
||||
"tdg",
|
||||
"p",
|
||||
"ch",
|
||||
"cy",
|
||||
"cz",
|
||||
"cp",
|
||||
"swap",
|
||||
"sqrtx",
|
||||
"sqrtswp",
|
||||
"cswap",
|
||||
|
||||
// Classical instructions
|
||||
"mov",
|
||||
"add",
|
||||
"sub",
|
||||
"mult",
|
||||
"umult",
|
||||
"div",
|
||||
"smult",
|
||||
"sumult",
|
||||
"sdiv",
|
||||
"not",
|
||||
"and",
|
||||
"or",
|
||||
"xor",
|
||||
"nand",
|
||||
"nor",
|
||||
"xnor",
|
||||
|
||||
// Misc instructions
|
||||
"qsel",
|
||||
"m",
|
||||
"cmp",
|
||||
"jmp",
|
||||
"jeq",
|
||||
"jne",
|
||||
"jg",
|
||||
"jge",
|
||||
"jl",
|
||||
"jle",
|
||||
"hlt",
|
||||
} else {
|
||||
r"[a-zA-Z_][a-zA-Z0-9_]*" => IDENT,
|
||||
}
|
||||
|
||||
pub Program: Result<Program, ResolveError> = {
|
||||
<h: Headers> <i: Instructions> => resolve_ast(h, i)
|
||||
}
|
||||
|
||||
Headers: (usize, usize, usize, usize, usize) = {
|
||||
"qbits" SPACE <qbits: Num> NEWLINE
|
||||
"cbits" SPACE <cbits: Num> NEWLINE
|
||||
"qregs" SPACE <qregs: Num> NEWLINE
|
||||
"cregs" SPACE <cregs: Num> NEWLINE
|
||||
"mem" SPACE <mem: Num> NEWLINE
|
||||
=> (qbits, cbits, qregs, cregs, mem)
|
||||
}
|
||||
|
||||
Instructions: Vec<Inst> = {
|
||||
(SPACE? <Instruction> NEWLINE)*
|
||||
}
|
||||
|
||||
Instruction: Inst = {
|
||||
// Quantum instructions
|
||||
<l: @L> "h" SPACE <q:Qbit> <r: @R> => Inst::Hadamard(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "cnot" SPACE <q0:Qbit> SPACE <q1:Qbit> <r: @R> => Inst::Cnot(q0, q1, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "ccnot" SPACE <q0:Qbit> SPACE <q1:Qbit> SPACE <q2:Qbit> <r: @R> => Inst::Ccnot(q0, q1, q2, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "x" SPACE <q:Qbit> <r: @R> => Inst::X(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "y" SPACE <q:Qbit> <r: @R> => Inst::Y(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "z" SPACE <q:Qbit> <r: @R> => Inst::Z(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "rx" SPACE <q:Qbit> SPACE <rot:Rot> <r: @R> => Inst::Rx(q, rot, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "ry" SPACE <q:Qbit> SPACE <rot:Rot> <r: @R> => Inst::Ry(q, rot, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "rz" SPACE <q:Qbit> SPACE <rot:Rot> <r: @R> => Inst::Rz(q, rot, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "u" SPACE <q:Qbit> SPACE <r0:Rot> SPACE <r1:Rot> SPACE <r2:Rot> <r: @R> => Inst::U(q, r0, r1, r2, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "s" SPACE <q:Qbit> <r: @R> => Inst::S(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "t" SPACE <q:Qbit> <r: @R> => Inst::T(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "sdg" SPACE <q:Qbit> <r: @R> => Inst::Sdg(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "tdg" SPACE <q:Qbit> <r: @R> => Inst::Tdg(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "p" SPACE <q:Qbit> SPACE <rot:Rot> <r: @R> => Inst::Phase(q, rot, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "ch" SPACE <q0:Qbit> SPACE <q1:Qbit> <r: @R> => Inst::Ch(q0, q1, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "cy" SPACE <q0:Qbit> SPACE <q1:Qbit> <r: @R> => Inst::Cy(q0, q1, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "cz" SPACE <q0:Qbit> SPACE <q1:Qbit> <r: @R> => Inst::Cz(q0, q1, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "cp" SPACE <q0:Qbit> SPACE <q1:Qbit> SPACE <rot:Rot> <r:@R> => Inst::CPhase(q0, q1, rot, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "swap" <q0:Qbit> SPACE <q1:Qbit> <r: @R> => Inst::Swap(q0, q1, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "sqrtx" <q:Qbit> <r: @R> => Inst::SqrtX(q, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "sqrtswp" <q0:Qbit> SPACE <q1:Qbit> <r: @R> => Inst::SqrtSwap(q0, q1, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "cswap" SPACE <q0:Qbit> SPACE <q1:Qbit> SPACE <q2:Qbit> <r: @R> => Inst::CSwap(q0, q1, q2, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "m" SPACE <q:Qbit> SPACE <cr:Creg> SPACE <c:Cbit> <r: @R> => Inst::Measure(q, cr, c, SourceSpan::new(file, l..r)),
|
||||
|
||||
// Custom gates stuff
|
||||
"gate" SPACE <name:Ident> SPACE? "(" SPACE? "qbits" SPACE? "=" SPACE? <qbits:Num> SPACE? ")" SPACE?
|
||||
<args:("(" SPACE? <Ident> SPACE? ":" SPACE? <IdentType> SPACE? ")" SPACE?)*>
|
||||
"{" NEWLINE <body:Instructions> "}" => Inst::CustomGateDef(name, qbits, args, body),
|
||||
|
||||
<l: @L> <name:Ident> SPACE <mut qbits:(<Qbit> SPACE)*> <e1:Qbit?> <args:(";" SPACE <(<CGateArg> SPACE)*> <CGateArg?>)?> <r:@R> => {
|
||||
let mut other_args;
|
||||
let e2;
|
||||
if let Some(args) = args {
|
||||
other_args = args.0;
|
||||
e2 = args.1;
|
||||
} else {
|
||||
other_args = Vec::new();
|
||||
e2 = None;
|
||||
}
|
||||
|
||||
Inst::Custom(
|
||||
name,
|
||||
match e1 {
|
||||
None => qbits,
|
||||
Some(e) => {
|
||||
qbits.push(e);
|
||||
qbits
|
||||
}
|
||||
},
|
||||
match e2 {
|
||||
None => other_args,
|
||||
Some(e) => {
|
||||
other_args.push(e);
|
||||
other_args
|
||||
}
|
||||
},
|
||||
SourceSpan::new(file, l..r),
|
||||
)
|
||||
},
|
||||
|
||||
// Classical instructions
|
||||
<l: @L> "mov" SPACE <r1:SrcOp> SPACE <r2:AnyOp> <r: @R> => Inst::Mov(r1, r2, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "add" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Add(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "sub" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Sub(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "mult" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Mul(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "umult" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::UMul(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "div" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Div(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "smult" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::SMul(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "sumult" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::SUMul(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "sdiv" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::SDiv(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "not" SPACE <r1:SrcOp> SPACE <r2:AnyOp> <r: @R> => Inst::Not(r1, r2, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "and" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::And(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "or" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Or(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "xor" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Xor(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "nand" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Nand(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "nor" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Nor(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
<l: @L> "xnor" SPACE <r1:SrcOp> SPACE <r2:AnyOp> SPACE <r3:AnyOp> <r: @R> => Inst::Xnor(
|
||||
r1, r2, r3,
|
||||
SourceSpan::new(file, l..r),
|
||||
),
|
||||
|
||||
// Misc instructions
|
||||
<l: @L> "qsel" SPACE <qr:Qreg> <r: @R> => Inst::Qsel(qr, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "cmp" SPACE <r1:SrcOp> SPACE <r2:AnyOp> <r: @R> => Inst::Cmp(r1, r2, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "jmp" SPACE <lbl:Label> <r: @R> => Inst::Jmp(lbl, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "jeq" SPACE <lbl:Label> <r: @R> => Inst::Jeq(lbl, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "jne" SPACE <lbl:Label> <r: @R> => Inst::Jne(lbl, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "jg" SPACE <lbl:Label> <r: @R> => Inst::Jg(lbl, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "jge" SPACE <lbl:Label> <r: @R> => Inst::Jge(lbl, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "jl" SPACE <lbl:Label> <r: @R> => Inst::Jl(lbl, SourceSpan::new(file, l..r)),
|
||||
<l: @L> "jle" SPACE <lbl:Label> <r: @R> => Inst::Jle(lbl, SourceSpan::new(file, l..r)),
|
||||
"hlt" => Inst::Hlt,
|
||||
|
||||
<Label> ":" => Inst::Label(<>),
|
||||
|
||||
! => {
|
||||
errors.push(<>.error);
|
||||
Inst::Err
|
||||
}
|
||||
}
|
||||
|
||||
Num: usize = NUM => <>.parse().unwrap();
|
||||
Imm: isize = "-"? NUM => ((<>).0.unwrap_or("").to_string() + (<>).1).parse::<isize>().unwrap();
|
||||
|
||||
Label: String = IDENT => String::from(<>);
|
||||
Ident: String = IDENT => String::from(<>);
|
||||
|
||||
Qbit: usize = QBIT => <>[1..].parse().unwrap();
|
||||
Qreg: usize = QREG => <>[2..].parse().unwrap();
|
||||
_Rot: Rot = <sgn:"-"?> <num:Num?> "pi" <den:("/" <Num>)?> => {
|
||||
let sign = if sgn.is_some() { -1 } else { 1 };
|
||||
let num = if let Some(num) = num { num as i64 } else { 1 };
|
||||
let den = if let Some(den) = den { den as u64 } else { 1 };
|
||||
Rot(sign * num, den)
|
||||
};
|
||||
Rot: Rotation = _Rot => Rotation::Rot(<>);
|
||||
Creg: usize = CREG => <>[2..].parse().unwrap();
|
||||
Cbit: usize = CBIT => <>[1..].parse().unwrap();
|
||||
Mem: MemAddr = {
|
||||
"[" SPACE? <Creg> SPACE? "]" => MemAddr::Indirect(<>, 1, 0),
|
||||
"[" SPACE? <Num> SPACE? "]" => MemAddr::Address(<>),
|
||||
"[" SPACE? <reg: Creg> SPACE? "+" SPACE? <offset: Num> SPACE? "]" => MemAddr::Indirect(reg, 1, offset),
|
||||
"[" SPACE? <reg: Creg> SPACE? "*" SPACE? <align: Num> SPACE? "+" SPACE? <offset: Num> SPACE? "]" => MemAddr::Indirect(reg, align, offset),
|
||||
};
|
||||
SrcOp: Operand = {
|
||||
Creg => Operand::Reg(<>),
|
||||
Mem => Operand::Addr(<>),
|
||||
|
||||
Ident => Operand::Ident(<>),
|
||||
};
|
||||
AnyOp: Operand = {
|
||||
Imm => Operand::Imm(<> as i64),
|
||||
Creg => Operand::Reg(<>),
|
||||
Mem => Operand::Addr(<>),
|
||||
|
||||
Ident => Operand::Ident(<>),
|
||||
};
|
||||
|
||||
CGateArg: IdentVal = {
|
||||
_Rot => IdentVal::Rot(<>),
|
||||
|
||||
Imm => IdentVal::Imm(<> as i64),
|
||||
Creg => IdentVal::Reg(<>),
|
||||
Mem => IdentVal::Addr(<>)
|
||||
};
|
||||
IdentType: IdentType = {
|
||||
"Rot" => IdentType::Rot,
|
||||
|
||||
"Imm" => IdentType::Imm,
|
||||
"Reg" => IdentType::Reg,
|
||||
"Addr" => IdentType::MemAddr,
|
||||
};
|
0
src/lib.rs
Normal file
0
src/lib.rs
Normal file
188
src/main.rs
Normal file
188
src/main.rs
Normal file
|
@ -0,0 +1,188 @@
|
|||
use std::{
|
||||
fs,
|
||||
io::{Read, Write},
|
||||
};
|
||||
|
||||
use clap::{ArgAction, Parser};
|
||||
|
||||
use codespan_reporting::{
|
||||
diagnostic::{Diagnostic, Label},
|
||||
files::SimpleFiles,
|
||||
term::{
|
||||
self,
|
||||
termcolor::{ColorChoice, StandardStream},
|
||||
Config,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod ast;
|
||||
pub mod emulator;
|
||||
|
||||
use ast::*;
|
||||
use emulator::Emulator;
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
let mut f = fs::File::open(&args.input_file).unwrap();
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents).unwrap();
|
||||
|
||||
let mut files = SimpleFiles::new();
|
||||
files.add(&args.input_file, &contents);
|
||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
let config = Config::default();
|
||||
|
||||
let (prog, diags) = ast::parse(&contents, 0);
|
||||
|
||||
for diag in &diags {
|
||||
term::emit(&mut writer.lock(), &config, &files, diag).unwrap()
|
||||
}
|
||||
|
||||
if !diags.is_empty() {
|
||||
std::process::exit(1)
|
||||
}
|
||||
|
||||
if let Ok(prog) = prog {
|
||||
let mut emulator = Emulator::new(&prog);
|
||||
let mut results = vec![vec![0u64; 1 << prog.headers.1]; prog.headers.3];
|
||||
|
||||
if args.print_emu_state {
|
||||
let res = emulator.run();
|
||||
if let Err(diag) = res {
|
||||
term::emit(&mut writer.lock(), &config, &files, &diag).unwrap();
|
||||
std::process::exit(3)
|
||||
}
|
||||
|
||||
println!("{}", emulator);
|
||||
return;
|
||||
}
|
||||
|
||||
if args.classical {
|
||||
let res = emulator.run();
|
||||
if let Err(diag) = res {
|
||||
term::emit(&mut writer.lock(), &config, &files, &diag).unwrap();
|
||||
std::process::exit(3)
|
||||
}
|
||||
|
||||
println!("Registers data:");
|
||||
print!("Reg:");
|
||||
for i in 0..prog.headers.3 {
|
||||
print!("\tcr{}", i)
|
||||
}
|
||||
println!();
|
||||
print!("Data:");
|
||||
let data = emulator.get_cregs_state();
|
||||
for reg in data {
|
||||
print!("\t{:01$b}", reg.get_val().0, prog.headers.1)
|
||||
}
|
||||
println!();
|
||||
|
||||
let mut mem_file = fs::File::options()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open("mem.bin")
|
||||
.unwrap();
|
||||
|
||||
let mem = emulator.get_mem_state();
|
||||
for chunk in mem.chunks(16) {
|
||||
for data in chunk {
|
||||
write!(mem_file, "{:01$b} ", data.get_val().0, prog.headers.1).unwrap()
|
||||
}
|
||||
writeln!(mem_file).unwrap()
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for _ in 0..args.shots {
|
||||
let res = emulator.run();
|
||||
if let Err(diag) = res {
|
||||
term::emit(&mut writer.lock(), &config, &files, &diag).unwrap();
|
||||
std::process::exit(3)
|
||||
}
|
||||
|
||||
let cregs = emulator.get_cregs_state();
|
||||
for (creg, res) in cregs.iter().zip(results.iter_mut()) {
|
||||
let val1 = creg.get_val().0;
|
||||
res[val1 as usize] += 1
|
||||
}
|
||||
|
||||
emulator.reset()
|
||||
}
|
||||
|
||||
println!("creg statistics:");
|
||||
for (i, result) in results.iter().enumerate() {
|
||||
println!("creg {} stat:", i);
|
||||
let sum = result.iter().sum::<u64>() as f64;
|
||||
for (j, res) in result.iter().enumerate() {
|
||||
println!(
|
||||
"[STATE {:01$b}] freq: {2};\tprob: {3:.5}",
|
||||
j,
|
||||
prog.headers.1,
|
||||
res,
|
||||
(*res as f64) / sum
|
||||
)
|
||||
}
|
||||
println!("-------------------")
|
||||
}
|
||||
} else if let Err(e) = prog {
|
||||
let diag = Diagnostic::error();
|
||||
match e {
|
||||
ResolveError::UndefinedLabel(name, s) => term::emit(
|
||||
&mut writer.lock(),
|
||||
&config,
|
||||
&files,
|
||||
&diag
|
||||
.with_message(format!("Reference to undefined label \"{}\"", name))
|
||||
.with_labels(vec![Label::primary(s.file, s.span)]),
|
||||
)
|
||||
.unwrap(),
|
||||
|
||||
ResolveError::ParseError(diag) => {
|
||||
term::emit(&mut writer.lock(), &config, &files, &diag).unwrap()
|
||||
}
|
||||
|
||||
ResolveError::UndefinedGate(name, s) => term::emit(
|
||||
&mut writer.lock(),
|
||||
&config,
|
||||
&files,
|
||||
&diag
|
||||
.with_message(format!("Usage of undefined gate \"{}\"", name))
|
||||
.with_labels(vec![Label::primary(s.file, s.span)]),
|
||||
)
|
||||
.unwrap(),
|
||||
|
||||
ResolveError::CustomGateError(diag) => {
|
||||
term::emit(&mut writer.lock(), &config, &files, &diag).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
/// The input .qASM file
|
||||
input_file: String,
|
||||
|
||||
/// Number of times to run the program for statistics information
|
||||
#[clap(short = 's', long = "shots", default_value_t = 1024)]
|
||||
shots: u64,
|
||||
|
||||
/// Print the statevectors of the quantum registers and terminate the program
|
||||
#[clap(short = 'p', long = "print-state", default_value_t = false, action = ArgAction::SetTrue)]
|
||||
print_emu_state: bool,
|
||||
|
||||
#[clap(short = 'c', long = "classical", default_value_t = false, action = ArgAction::SetTrue)]
|
||||
classical: bool,
|
||||
}
|
||||
|
||||
fn _escaped(c: char) -> String {
|
||||
match c {
|
||||
'\n' => String::from("\\n"),
|
||||
'\r' => String::from("\\r"),
|
||||
_ => c.to_string(),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue