Skip to content

Commit

Permalink
feat: add methods for quil-c conjugate_pauli_by_clifford and `gener…
Browse files Browse the repository at this point in the history
…ate_randomized_benchmarking_sequence` (#280)

* feat: copy over quilc methods

* chore: add tests

* chore: try to fix tests

* chore: fix types

* chore: fix gateset

* chore: fix clippy

* chore: clean up api

* chore: python implementation

* chore: test python quilc methods

* chore: clippy fixy

* fix: docs

* fix: actually fix the docs
  • Loading branch information
jselig-rigetti authored Apr 5, 2023
1 parent 3f31b93 commit 6cefbea
Show file tree
Hide file tree
Showing 4 changed files with 582 additions and 7 deletions.
179 changes: 179 additions & 0 deletions crates/lib/src/compiler/quilc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,142 @@ pub fn get_version_info(client: &Qcs) -> Result<String, Error> {
}
}

/// Pauli Term
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, PartialOrd)]
#[serde(tag = "_type")]
pub struct PauliTerm {
/// Qubit indices onto which the factors of the Pauli term are applied.
pub indices: Vec<u64>,

/// Ordered factors of the Pauli term.
pub symbols: Vec<String>,
}

/// Request to conjugate a Pauli Term by a Clifford element.
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, PartialOrd)]
#[serde(tag = "_type")]
pub struct ConjugateByCliffordRequest {
/// Pauli Term to conjugate.
pub pauli: PauliTerm,

/// Clifford element.
pub clifford: String,
}

/// The "outer" request shape for a `conjugate_pauli_by_clifford` request.
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, PartialOrd)]
struct ConjugatePauliByCliffordRequest {
#[serde(rename = "*args")]
args: [ConjugateByCliffordRequest; 1],
}

impl From<ConjugateByCliffordRequest> for ConjugatePauliByCliffordRequest {
fn from(value: ConjugateByCliffordRequest) -> Self {
Self { args: [value] }
}
}

/// Conjugate Pauli by Clifford response.
#[derive(Clone, Deserialize, Debug, PartialEq, PartialOrd)]
pub struct ConjugatePauliByCliffordResponse {
/// Encoded global phase factor on the emitted Pauli.
pub phase: i64,

/// Description of the encoded Pauli.
pub pauli: String,
}

/// Given a circuit that consists only of elements of the Clifford group,
/// return its action on a `PauliTerm`.
/// In particular, for Clifford ``C``, and Pauli ``P``, this returns the Pauli Term
/// representing ``CPC^{\dagger}``.
pub fn conjugate_pauli_by_clifford(
client: &Qcs,
request: ConjugateByCliffordRequest,
) -> Result<ConjugatePauliByCliffordResponse, Error> {
#[cfg(feature = "tracing")]
tracing::debug!("requesting quilc conjugate_pauli_by_clifford");

let config = client.get_config();
let endpoint = config.quilc_url();
let request: ConjugatePauliByCliffordRequest = request.into();
let request = rpcq::RPCRequest::new("conjugate_pauli_by_clifford", &request);
let rpcq_client = rpcq::Client::new(endpoint)
.map_err(|source| Error::from_quilc_error(endpoint.into(), source))?;
match rpcq_client.run_request::<_, ConjugatePauliByCliffordResponse>(&request) {
Ok(response) => Ok(response),
Err(source) => Err(Error::from_quilc_error(endpoint.into(), source)),
}
}

/// Request to generate a randomized benchmarking sequence.
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, PartialOrd)]
#[serde(tag = "_type")]
pub struct RandomizedBenchmarkingRequest {
/// Depth of the benchmarking sequence.
pub depth: u64,

/// Number of qubits involved in the benchmarking sequence.
pub qubits: u64,

/// List of Quil programs, each describing a Clifford.
pub gateset: Vec<String>,

/// PRNG seed. Set this to guarantee repeatable results.
pub seed: Option<u64>,

/// Fixed Clifford, specified as a Quil string, to interleave through an RB sequence.
pub interleaver: Option<String>,
}

/// The "outer" request shape for a `generate_rb_sequence` request.
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, PartialOrd)]
struct GenerateRandomizedBenchmarkingSequenceRequest {
#[serde(rename = "*args")]
args: [RandomizedBenchmarkingRequest; 1],
}

impl From<RandomizedBenchmarkingRequest> for GenerateRandomizedBenchmarkingSequenceRequest {
fn from(value: RandomizedBenchmarkingRequest) -> Self {
Self { args: [value] }
}
}

/// Randomly generated benchmarking sequence response.
#[derive(Clone, Deserialize, Debug, PartialEq, PartialOrd)]
pub struct GenerateRandomizedBenchmarkingSequenceResponse {
/// List of Cliffords, each expressed as a list of generator indices.
pub sequence: Vec<Vec<i64>>,
}

/// Construct a randomized benchmarking experiment on the given qubits, decomposing into
/// gateset. If interleaver is not provided, the returned sequence will have the form
/// ```C_1 C_2 ... C_(depth-1) C_inv ,```
///
/// where each C is a Clifford element drawn from gateset, ``C_{< depth}`` are randomly selected,
/// and ``C_inv`` is selected so that the entire sequence composes to the identity. If an
/// interleaver ``G`` (which must be a Clifford, and which will be decomposed into the native
/// gateset) is provided, then the sequence instead takes the form
/// ```C_1 G C_2 G ... C_(depth-1) G C_inv .```
pub fn generate_randomized_benchmarking_sequence(
client: &Qcs,
request: RandomizedBenchmarkingRequest,
) -> Result<GenerateRandomizedBenchmarkingSequenceResponse, Error> {
#[cfg(feature = "tracing")]
tracing::debug!("requesting quilc generate_randomized_benchmarking_sequence");

let config = client.get_config();
let endpoint = config.quilc_url();
let request: GenerateRandomizedBenchmarkingSequenceRequest = request.into();
let request = rpcq::RPCRequest::new("generate_rb_sequence", &request);
let rpcq_client = rpcq::Client::new(endpoint)
.map_err(|source| Error::from_quilc_error(endpoint.into(), source))?;
match rpcq_client.run_request::<_, GenerateRandomizedBenchmarkingSequenceResponse>(&request) {
Ok(response) => Ok(response),
Err(source) => Err(Error::from_quilc_error(endpoint.into(), source)),
}
}

/// All of the errors that can occur within this module.
#[derive(Debug, thiserror::Error)]
pub enum Error {
Expand Down Expand Up @@ -296,4 +432,47 @@ MEASURE 1 ro[1]
let semver_re = Regex::new(r"^([0-9]+)\.([0-9]+)\.([0-9]+)$").unwrap();
assert!(semver_re.is_match(&version));
}

#[tokio::test]
async fn test_conjugate_pauli_by_clifford() {
let client = Qcs::load().await.unwrap_or_default();