mod compiler;
mod timing;
use super::{Fabric, NodeId, ResourceState};
use crate::{Error, Result};
pub use compiler::{CompileOptions, CompiledFusionNetwork, RoutedPhoton, ScheduledFusion};
use serde::{Deserialize, Serialize};
pub use timing::{SchedulePolicy, TimingOptions};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PhotonId(pub usize);
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ResourceInstance {
pub state: ResourceState,
pub source: NodeId,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FusionRequest {
pub photons: [PhotonId; 2],
pub at: NodeId,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct ProgramData {
version: u32,
resources: Vec<ResourceInstance>,
fusions: Vec<FusionRequest>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "ProgramData", into = "ProgramData")]
pub struct FusionProgram {
pub(crate) resources: Vec<ResourceInstance>,
pub(crate) fusions: Vec<FusionRequest>,
photons: usize,
consumed: Vec<bool>,
}
impl FusionProgram {
pub fn new() -> Self {
Self::default()
}
pub fn add_resource(&mut self, state: ResourceState, source: NodeId) -> Result<Vec<PhotonId>> {
let end = self
.photons
.checked_add(state.qubits())
.ok_or(Error::Overflow)?;
if end > 1_048_576 {
return Err(Error::ResourceLimit {
resource: "program photons",
required: end,
limit: 1_048_576,
});
}
let ids = (self.photons..end).map(PhotonId).collect();
self.resources.push(ResourceInstance { state, source });
self.consumed.resize(end, false);
self.photons = end;
Ok(ids)
}
pub fn fuse(&mut self, left: PhotonId, right: PhotonId, at: NodeId) -> Result<&mut Self> {
if left == right
|| left.0 >= self.photons
|| right.0 >= self.photons
|| self.consumed[left.0]
|| self.consumed[right.0]
{
return Err(Error::Wires(
"fusion references an absent, repeated or consumed photon".into(),
));
}
self.fusions.push(FusionRequest {
photons: [left, right],
at,
});
self.consumed[left.0] = true;
self.consumed[right.0] = true;
Ok(self)
}
pub fn num_photons(&self) -> usize {
self.photons
}
pub fn num_fusions(&self) -> usize {
self.fusions.len()
}
pub fn resources(&self) -> &[ResourceInstance] {
&self.resources
}
pub fn fusions(&self) -> &[FusionRequest] {
&self.fusions
}
pub fn compile_timed(
&self,
fabric: &Fabric,
options: CompileOptions,
timing: TimingOptions,
) -> Result<CompiledFusionNetwork> {
compiler::compile_timed(self, fabric, options, timing)
}
pub fn compile_finite(
&self,
fabric: &Fabric,
options: CompileOptions,
timing: TimingOptions,
constraints: crate::photonic::fabric::FabricConstraints,
fusion_ticks: &[u64],
) -> Result<CompiledFusionNetwork> {
compiler::compile_finite(self, fabric, options, timing, constraints, fusion_ticks)
}
pub fn compile(
&self,
fabric: &Fabric,
options: CompileOptions,
) -> Result<CompiledFusionNetwork> {
compiler::compile(self, fabric, options)
}
}
impl TryFrom<ProgramData> for FusionProgram {
type Error = Error;
fn try_from(data: ProgramData) -> Result<Self> {
if data.version != 1 {
return Err(Error::Unsupported(
"unknown fusion program schema version".into(),
));
}
let mut program = Self::new();
for resource in data.resources {
program.add_resource(resource.state, resource.source)?;
}
for fusion in data.fusions {
program.fuse(fusion.photons[0], fusion.photons[1], fusion.at)?;
}
Ok(program)
}
}
impl From<FusionProgram> for ProgramData {
fn from(v: FusionProgram) -> Self {
Self {
version: 1,
resources: v.resources,
fusions: v.fusions,
}
}
}