Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Implement LaTeX generation #145

Open
wants to merge 127 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 104 commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
7be2094
Initial ToLaTeX commit.
Feb 6, 2023
479974d
Add X and Y gate tests with helper function.
Feb 7, 2023
8fb30e5
Move gate tests to tests submodule gates.
Feb 7, 2023
cb02ef8
Add TikZ operators.
Feb 7, 2023
5cb096d
Add latex dependency as feature.
Feb 8, 2023
029d337
Rename TikzOperators to TikzOperator and CamelCase variant names.
Feb 8, 2023
c9fc7e9
Move latex.rs and snapshots into latex directory.
Feb 13, 2023
a25abb4
Initial commit for LaTeX abstract factory model.
Feb 16, 2023
1d418a4
Revert "Initial commit for LaTeX abstract factory model."
Feb 17, 2023
3f217f4
Remove new snapshot of failed X gate unit test.
Feb 17, 2023
ed1f8a0
Remove latex dependency and rename feature to-latex to latex.
Feb 17, 2023
e00633e
Add comments and update names wrt Quantikz docs.
Feb 22, 2023
b7642bc
Update snapshot test names.
Feb 22, 2023
865f495
Add document template and unit tests.
Feb 23, 2023
dc5b563
Add gate command and update command variant data types.
Feb 25, 2023
6486920
Add test for single qubit with two gates X and Y.
Feb 25, 2023
5cbfb4d
Build a Diagram from a vector of Circuits.
Feb 25, 2023
f3ea53b
Remove extraneous gate from snapshot test.
Feb 28, 2023
2a97404
Add LaTeX gen for single qubit gate instructions.
Feb 28, 2023
f39f1d8
Add multi-line n-qubit single qubit gates.
Feb 28, 2023
1a3d66c
Update doc string example for get_command.
Feb 28, 2023
6a4894d
Preserve order of wires by indexing circuit keys in vector.
Mar 1, 2023
04a1723
Add initial CNOT LaTeX gen for variable qubits.
Mar 1, 2023
458e750
Merge branch 'main' into to-latex
Mar 2, 2023
568fcdf
Add CNOT gate.
Mar 7, 2023
4a8cf9b
Add combined single and multi qubit gate tests.
Mar 7, 2023
e615f2c
Add settings test module with snapshot test.
Mar 7, 2023
8494931
Add impute missing qubits setting.
Mar 7, 2023
2048ccd
Debug CNOT with two TDD tests of complex Programs.
Mar 8, 2023
5094048
Optimize ctrl/targ programs with only one call to set_ctrl_targ.
Mar 8, 2023
dad6ac4
Allow CNOT for a single qubit.
Mar 8, 2023
423a7ec
Revert "Allow CNOT for a single qubit."
Mar 8, 2023
e36623a
Handle CNOT without target.
Mar 8, 2023
b2b1f1a
Working draft of CCNOT and CONTROLLED.
Mar 8, 2023
298075f
Revert "Allow CNOT for a single qubit."
Mar 8, 2023
9fc3c62
Merge with to-latex.
Mar 9, 2023
39f8f46
Add CONTROLLED and CCNOT.
Mar 9, 2023
b6a9e20
Update doc and remove unused variables.
Mar 9, 2023
6e1dfba
Reformat doc.
Mar 9, 2023
9062255
Reformat file.
Mar 9, 2023
6f3c0c3
Propagate LatexGenError instead of panic.
Mar 10, 2023
d40808e
Add PHASE, CZ, and CPHASE.
Mar 10, 2023
0cfb8d2
Initial docs.
Mar 10, 2023
6a20a52
Add DAGGER modifier.
Mar 11, 2023
5655d7e
Add texify numerical constants setting.
Mar 13, 2023
370c3cc
Fix get_symbol doctest.
Mar 13, 2023
5d9c138
Initial good modifiers test.
Mar 13, 2023
e49e7d7
Merge branch 'main' into to-latex
Mar 13, 2023
f0591fb
Merge branch 'to-latex' into good-tests
Mar 13, 2023
a068411
Add multiple controlled modifiers to single qubit gates.
Mar 13, 2023
86a280e
Add good defgate and modifiers tests.
Mar 14, 2023
2338b2e
Refactor to_latex with initial circuit of used qubits.
Mar 14, 2023
d31b231
Move to_latex code blocks to new functions and remove outdated TODOs.
Mar 15, 2023
6cd43dc
Test five good programs from quilc good-test-files.
Mar 15, 2023
5dfafec
Add error handling for unsupported programs.
Mar 15, 2023
772c2ad
Merge branch 'to-latex' into to-latex-docs
Mar 15, 2023
37dbb7f
Update docs including supported programs.
Mar 16, 2023
645b787
Tighten visibility of crate.
Mar 16, 2023
5d7940c
Fix docs and remove deadcode.
Mar 16, 2023
11cd5c5
Replace explicit Clone impl for Parameter with derived trait.
Mar 16, 2023
5dbaaad
Refactor get_symbol into ToString for Symbol and Parameter.
Mar 16, 2023
0b9213d
Apply docs suggestions from code review.
hiyaryan Mar 16, 2023
10fe4f3
Fix Quantikz link in documentation.
Mar 16, 2023
025f7fa
Add clippy fixes.
Mar 16, 2023
af07939
Apply clippy fixes, doc fixes, rename Settings to RenderSettings.
Mar 16, 2023
63e13ea
Add manual clippy fixes and Clone or Copy to structs and make more id…
Mar 17, 2023
3677651
Remove supported program checking.
Mar 17, 2023
6a2fd9e
Use write! in Diagram fmt, reduce/reformat docs.
Mar 20, 2023
6ec15be
Merge branch 'rigetti:main' into to-latex
hiyaryan Mar 20, 2023
28f16e7
Decompose gates to canonical form.
Mar 21, 2023
7ab4b5a
Fill circuit using instruction in canonical form.
Mar 22, 2023
e37d83e
Debug PHASE, multiline, and CNOT 0 0 tests.
Mar 22, 2023
61f990a
Handle unsupported instructions and gates.
Mar 22, 2023
1af1a9f
Update docs and readability.
Mar 22, 2023
029f05b
Refactor gate setting from parse_gate into separate method set_gate.
Mar 23, 2023
421e7eb
Rename feature to ToLatex, set_qw to set_empty, make enums more idiom…
Mar 23, 2023
cab0caf
Update doctest examples.
Mar 23, 2023
571c497
Rename err variant FoundCNOTWithNoTarget to FoundTargetWithNoControl.
Mar 23, 2023
0af2769
Refactor impute_missing_qubits and set_empty to use built-ins from std.
hiyaryan Mar 24, 2023
712a24c
Add missing semicolon and fix format.
Mar 24, 2023
c353c3f
Organize code by restructuring design.
Mar 28, 2023
1ecc789
Make ToLatex a method of Program.
Mar 28, 2023
4e4ca5a
Add derive_more, change From<String> to FromStr, generify with From<S>.
Mar 28, 2023
46c024f
Apply suggestions to extend derived traits and shorten/simplify code.
Mar 28, 2023
30169aa
Update GateDefinition Instruction block in to_latex.
Mar 29, 2023
0b58d76
Change insert_gate to apply_gate and simplify using to_canonical.
Mar 30, 2023
8b74dbe
Refactor Wire.gate value type to String and remove CONTROLLED collect…
Mar 30, 2023
9b782fa
Rename set_modifiers to extract_daggers, Wire.modifiers to Wire.dagge…
Mar 30, 2023
2e4a67b
Show Display path from fmt module.
hiyaryan Mar 30, 2023
7a6f513
Move FromStr for Symbol into Symbol enum block using strum.
hiyaryan Mar 30, 2023
392e7af
Remove unused CompositeGate variant None.
hiyaryan Mar 30, 2023
f85ddbf
Make empty string with new instead of from.
hiyaryan Mar 30, 2023
935875a
Initial file restructuring.
Mar 30, 2023
90c3cb1
Move settings to latex crate.
Mar 30, 2023
ee42a06
Organize latex module into submodules and update docs.
Mar 30, 2023
6833a9b
Correct docs to m is row, n is column, for m x n matrix.
Mar 30, 2023
041b969
Move set_empty into gate block in to_latex.
Mar 31, 2023
f2d2733
Pull column from Wire methods, set as field, refactor set_empty to ap…
Mar 31, 2023
1464300
Refactor column from int to usize, track in wire, set total in diagram.
Mar 31, 2023
09d7fac
Refactor Diagram fmt to use enumerate and iters and expand comments.
Mar 31, 2023
3010fe8
Remove enumeration in Diagram fmt.
Mar 31, 2023
6808837
Add inner String type to Gate and Phase variants, and implement dagge…
Mar 31, 2023
07b6184
Remove verticals from Diagram, add Display for Wire, refactor Display…
Apr 1, 2023
f7e0409
Iterate over circuit instead of keys in Diagram fmt and update docs.
Apr 1, 2023
08a6013
Move unwrap for last item in collections to outside of loop.
Apr 3, 2023
68cc16e
Use regex for canonical gate decomposition and handle unimplemented X…
Apr 3, 2023
4f9cae4
Merge targ and ctrl fields into single generic gates field.
Apr 4, 2023
755883d
Use the generic gate and counted modifiers, and catch unsupported gat…
Apr 5, 2023
8cf0073
Change parameters values from vector to single parameter.
Apr 5, 2023
11191e0
Change Lstick to LeftWireLabel.
Apr 5, 2023
05334d1
Update docs for Diagram circuit.
hiyaryan Apr 7, 2023
9487249
Apply review suggestions.
Apr 7, 2023
436ac86
Add QuantikzGate struct, unnest apply_gate
MarquessV Apr 7, 2023
41c2538
apply parameters inside of main apply_gate loop
MarquessV Apr 7, 2023
e835c61
Couple parameter with a specific cell in Column struct in a Wire of C…
Apr 11, 2023
04c2abf
Make enum-as-inner an optional dependency for latex feature.
Apr 11, 2023
b19f0aa
Use enum-as-inner to extract fixed qubits, reorganize code, update docs.
Apr 11, 2023
9e6d4bc
Revert "Make enum-as-inner an optional dependency for latex feature."
Apr 11, 2023
3aacde2
Remove wordy QuantikzGate doc, refactor if-let for instruction to match.
Apr 12, 2023
ea8cd05
Refactor using new data model WIP.
Apr 13, 2023
b07ff0f
Fix doc tests, remove dev notes.
Apr 13, 2023
12b5ee3
Fix doc tests, remove dev notes and template snapshot test.
Apr 13, 2023
69087b7
Merge branch 'to-latex' of https://github.com/hiyaryan/quil-rs into t…
Apr 13, 2023
b504fec
Add error handling, clean up code and comments, remove sorting with B…
Apr 15, 2023
c400135
Apply suggestions from code review.
hiyaryan Apr 24, 2023
a837cf4
Amend previous commit.
Apr 24, 2023
1a44a3a
Merge branch 'main' into to-latex
hiyaryan May 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
[package]
name = "quil-rs"
description = "Rust tooling for Quil (Quantum Instruction Language)"
version ="0.16.0-rc.1"
version = "0.16.0-rc.1"
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/rigetti/quil-rust"
keywords = ["Quil", "Quantum", "Rigetti"]
categories = ["parser-implementations", "science", "compilers", "emulators"]

[dependencies]
derive_more = { version = "0.99.17", optional = true }
dot-writer = { version = "0.1.2", optional = true }
indexmap = "1.6.1"
lexical = "6.1.1"
lexical = "6.1.1"
nom = "7.1.1"
nom_locate = "4.0.0"
num-complex = "0.4.0"
Expand All @@ -29,6 +30,7 @@ rstest = "0.15.0"

[features]
graphviz-dot = ["dot-writer"]
latex = ["derive_more"]

[profile.release]
lto = true
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
//! [constructor for timing graphs]: crate::program::graph::ScheduledProgram#method.get_dot_format
//! [expressions]: crate::expression::Expression
//! [instructions]: crate::instruction::Instruction
//! [latex]: crate::program::latex::Latex#method.to_latex
//! [parser]: crate::program::Program#method.from_str
//! [programs]: crate::program::Program
//! [serializer]: crate::program::Program#method.to_string
Expand Down
174 changes: 174 additions & 0 deletions src/program/latex/diagram/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
use std::{
collections::{BTreeMap, HashSet},
fmt,
str::FromStr,
};

use crate::instruction::{Gate, Qubit};

use self::wire::Wire;
use super::{LatexGenError, RenderCommand, RenderSettings};

pub(crate) mod wire;

/// Gates written in shorthand notation, i.e. composite form, that may be
/// decomposed into modifiers and single gate instructions, i.e. canonical form.
#[derive(Clone, Debug, strum::EnumString, derive_more::Display, PartialEq, Eq, Hash)]
#[strum(serialize_all = "UPPERCASE")]
enum CompositeGate {
/// `CNOT` is `CONTROLLED X`
#[display(fmt = "X")]
Cnot,
/// `CCNOT` is `CONTROLLED CONTROLLED X`
#[display(fmt = "X")]
Ccnot,
/// `CPHASE` is `CONTROLLED PHASE`
#[display(fmt = "PHASE")]
Cphase,
/// `CZ` is `CONTROLLED Z`
#[display(fmt = "Z")]
Cz,
}
hiyaryan marked this conversation as resolved.
Show resolved Hide resolved

/// A Diagram represents a collection of wires in a Circuit. The size of the
/// Diagram can be measured by multiplying the number of Instructions in a
/// Program with the length of the Circuit. This is an [m x n] matrix where n,
/// is the number of Quil instructions (or columns), and m, is the number of
/// wires (or rows). Each individual element of the matrix represents an item
/// that is serializable into LaTeX using the ``Quantikz`` RenderCommands.
#[derive(Clone, Debug, Default)]
pub(super) struct Diagram {
/// customizes how the diagram renders the circuit
pub(crate) settings: RenderSettings,
/// a BTreeMap of wires with the name of the wire as the key
hiyaryan marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) circuit: BTreeMap<u64, Box<Wire>>,
}

impl Diagram {
/// Compares qubits from a single instruction associated with a column on
/// the circuit to all of the qubits used in the quil program. If a qubit
/// from the quil program is not found in the qubits in the single
/// instruction line, then an empty slot is added to that column on the
/// qubit wire of the circuit indicating a "do nothing" at that column.
///
/// # Arguments
/// `qubits` - exposes the qubits used in the Program
/// `instruction` - exposes the qubits in a single Instruction
pub(crate) fn apply_empty(&mut self, qubits: &HashSet<Qubit>, gate: &Gate) {
qubits
.difference(&gate.qubits.iter().cloned().collect())
.filter_map(|q| match q {
Qubit::Fixed(index) => Some(index),
_ => None,
})
.for_each(|index| {
if let Some(wire) = self.circuit.get_mut(index) {
// increment the wire column
wire.column += 1;
wire.set_empty()
}
});
}

/// Applies a gate from an instruction to the wires on the circuit
/// associate with the qubits from the gate. If the gate name matches a
/// composite gate, then the composite gate is applied to the circuit,
/// otherwise, the original gate is applied to the circuit.
///
/// # Arguments
/// `gate` - the Gate of the Instruction from `to_latex`.
pub(crate) fn apply_gate(&mut self, gate: &Gate) -> Result<(), LatexGenError> {
// for each fixed qubit in the gate
for qubit in &gate.qubits {
if let Qubit::Fixed(qubit) = qubit {
if let Some(wire) = self.circuit.get_mut(qubit) {
// increment the wire column
wire.column += 1;

// set modifiers at this column for all qubits
wire.set_daggers(&gate.modifiers)?;

// set parameters at this column for all qubits
for expression in &gate.parameters {
wire.set_param(expression, self.settings.texify_numerical_constants);
}
}
}
}

// get display of gate name from composite gate or original gate
let canonical_gate = CompositeGate::from_str(&gate.name)
.map(|g| g.to_string())
hiyaryan marked this conversation as resolved.
Show resolved Hide resolved
.unwrap_or(gate.name.clone());

// get the names of the qubits in the circuit before circuit is borrowed as mutable
let circuit_qubits: Vec<u64> = self.circuit.keys().cloned().collect();
hiyaryan marked this conversation as resolved.
Show resolved Hide resolved

// set gate for each qubit in the instruction
for qubit in &gate.qubits {
if let Qubit::Fixed(instruction_qubit) = qubit {
if let Some(wire) = self.circuit.get_mut(instruction_qubit) {
// set the control and target qubits
if gate.qubits.len() > 1 || canonical_gate == "PHASE" {
// set the target qubit if the qubit is equal to the last qubit in gate
if qubit == gate.qubits.last().unwrap() {
wire.set_targ();
// otherwise, set the control qubit
} else {
wire.set_ctrl(qubit, gate.qubits.last().unwrap(), &circuit_qubits);
}
hiyaryan marked this conversation as resolved.
Show resolved Hide resolved
} else if wire.parameters.get(&wire.column).is_some() {
// parameterized single qubit gates are unsupported
return Err(LatexGenError::UnsupportedGate {
gate: gate.name.clone(),
});
}

// set modifiers at this column for all qubits
wire.gates.insert(wire.column, canonical_gate.clone());
}
}
}

Ok(())
}
}

impl fmt::Display for Diagram {
/// Returns a result containing the body of the Document.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write a newline between the body and the Document header
writeln!(f)?;

// write the LaTeX string for each wire in the circuit
for (qubit, wire) in &self.circuit {
// are labels on in settings?
if self.settings.label_qubit_lines {
// write the label to the left side of wire
write!(f, "{}", RenderCommand::Lstick(*qubit))?;
} else {
// write an empty column buffer as the first column
write!(f, "{}", RenderCommand::Qw)?;
}

// write the LaTeX string for the wire
write!(f, "{wire}")?;

// chain an empty column to the end of the line
write!(f, " & ")?;
write!(f, "{}", &RenderCommand::Qw)?;
hiyaryan marked this conversation as resolved.
Show resolved Hide resolved

// if this is the last iteration, omit a new row from the end of the line
if *qubit != *self.circuit.keys().last().unwrap() {
hiyaryan marked this conversation as resolved.
Show resolved Hide resolved
// otherwise, write a new row to the end of the line
write!(f, " ")?;
write!(f, "{}", &RenderCommand::Nr)?;
}

// write a newline between each row and or the body and the document footer
writeln!(f)?;
}

Ok(())
}
}
Loading