This commit is contained in:
zk⁷ 2024-03-30 16:29:20 +03:00
commit dcf80a5974
10 changed files with 2787 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/target/
/Cargo.lock
/test.qASM
/mem.bin

19
Cargo.toml Normal file
View 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
View 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
View 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
```

3
build.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
lalrpop::process_root().unwrap();
}

634
src/ast.rs Normal file
View 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

File diff suppressed because it is too large Load diff

311
src/grammar.lalrpop Normal file
View 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
View file

188
src/main.rs Normal file
View 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(),
}
}