Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
232 changes: 162 additions & 70 deletions source/compiler/qsc/src/interpret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,49 @@ pub enum SimType {
Clifford(usize),
}

macro_rules! with_fresh_simulator {
($sim_type:expr, $noise:expr, $qubit_loss:expr, $noise_config:expr, |$sim:ident| $body:expr) => {{
let noise = $noise;
let noise_config = $noise_config;
let qubit_loss = if noise_config.is_none() {
$qubit_loss
} else {
None
};

match $sim_type {
SimType::Sparse => {
let mut $sim = match noise {
Some(noise) => SparseSim::new_with_noise(&noise),
None => match noise_config {
Some(config) => SparseSim::new_with_noise_config(config.into()),
None => SparseSim::new(),
},
};
if let Some(loss) = qubit_loss {
$sim.set_loss(loss);
}
$body
}
SimType::Clifford(num_qubits) => {
let mut $sim = match noise {
Some(noise) => CliffordSim::new_with_pauli_noise(num_qubits, &noise),
None => match noise_config {
Some(config) => {
CliffordSim::new_with_noise_config(num_qubits, config.into())
}
None => CliffordSim::new(num_qubits),
},
};
if let Some(loss) = qubit_loss {
$sim.set_loss(loss);
}
$body
}
}
}};
}

/// A Q# interpreter.
pub struct Interpreter {
/// The incremental Q# compiler.
Expand Down Expand Up @@ -910,40 +953,35 @@ impl Interpreter {
seed: Option<u64>,
sim_type: SimType,
) -> InterpretResult {
let qubit_loss = if noise_config.is_none() {
qubit_loss
} else {
None
};
with_fresh_simulator!(sim_type, noise, qubit_loss, noise_config, |sim| self
.invoke_with_seeded_sim(&mut sim, receiver, callable, args, seed))
}

match sim_type {
SimType::Sparse => {
let mut sim = match noise {
Some(noise) => SparseSim::new_with_noise(&noise),
None => match noise_config {
Some(config) => SparseSim::new_with_noise_config(config.into()),
None => SparseSim::new(),
},
};
if let Some(loss) = qubit_loss {
sim.set_loss(loss);
}
if seed.is_some() {
sim.set_seed(seed);
}
self.invoke_with_sim(&mut sim, receiver, callable, args, seed)
}
SimType::Clifford(num_qubits) => {
let mut sim = match noise_config {
Some(config) => CliffordSim::new_with_noise_config(num_qubits, config.into()),
None => CliffordSim::new(num_qubits),
};
if seed.is_some() {
sim.set_seed(seed);
}
self.invoke_with_sim(&mut sim, receiver, callable, args, seed)
}
fn invoke_with_seeded_sim(
&mut self,
sim: &mut impl Backend,
receiver: &mut impl Receiver,
callable: Value,
args: Value,
seed: Option<u64>,
) -> InterpretResult {
if seed.is_some() {
sim.set_seed(seed);
}
self.invoke_with_sim(sim, receiver, callable, args, seed)
}

/// Runs the package entry point on a fresh simulator configured with the given noise, if any.
pub fn eval_entry_with_noise(
&mut self,
receiver: &mut impl Receiver,
noise: Option<PauliNoise>,
qubit_loss: Option<f64>,
noise_config: Option<NoiseConfig<f64, f64>>,
sim_type: SimType,
) -> InterpretResult {
with_fresh_simulator!(sim_type, noise, qubit_loss, noise_config, |sim| self
.eval_entry_with_sim(&mut sim, receiver))
}

/// Runs the given entry expression on a new instance of the environment and simulator,
Expand All @@ -959,33 +997,8 @@ impl Interpreter {
seed: Option<u64>,
sim_type: SimType,
) -> InterpretResult {
let qubit_loss = if noise_config.is_none() {
qubit_loss
} else {
None
};
match sim_type {
SimType::Sparse => {
let mut sim = match noise {
Some(noise) => SparseSim::new_with_noise(&noise),
None => match noise_config {
Some(config) => SparseSim::new_with_noise_config(config.into()),
None => SparseSim::new(),
},
};
if let Some(loss) = qubit_loss {
sim.set_loss(loss);
}
self.run_with_sim(&mut sim, receiver, expr, seed)
}
SimType::Clifford(num_qubits) => {
let mut sim = match noise_config {
Some(config) => CliffordSim::new_with_noise_config(num_qubits, config.into()),
None => CliffordSim::new(num_qubits),
};
self.run_with_sim(&mut sim, receiver, expr, seed)
}
}
with_fresh_simulator!(sim_type, noise, qubit_loss, noise_config, |sim| self
.run_with_sim(&mut sim, receiver, expr, seed))
}

/// Gets the current quantum state of the simulator.
Expand Down Expand Up @@ -1838,13 +1851,28 @@ pub struct PackageGlobal {
/// and inspecting state in the interpreter.
pub struct Debugger {
interpreter: Interpreter,
simulator: DebuggerSimulator,
/// The encoding (utf-8 or utf-16) used for character offsets
/// in line/character positions returned by the Interpreter.
position_encoding: Encoding,
/// The current state of the evaluator.
state: State,
}

enum DebuggerSimulator {
Sparse,
Clifford(Box<CliffordSim>),
}

impl From<SimType> for DebuggerSimulator {
fn from(sim_type: SimType) -> Self {
match sim_type {
SimType::Sparse => Self::Sparse,
SimType::Clifford(num_qubits) => Self::Clifford(Box::new(CliffordSim::new(num_qubits))),
}
}
}

impl Debugger {
pub fn new(
sources: SourceMap,
Expand All @@ -1853,6 +1881,26 @@ impl Debugger {
language_features: LanguageFeatures,
store: PackageStore,
dependencies: &Dependencies,
) -> std::result::Result<Self, Vec<Error>> {
Self::new_with_sim(
sources,
capabilities,
position_encoding,
language_features,
store,
dependencies,
SimType::Sparse,
)
}

pub fn new_with_sim(
sources: SourceMap,
capabilities: TargetCapabilityFlags,
position_encoding: Encoding,
language_features: LanguageFeatures,
store: PackageStore,
dependencies: &Dependencies,
sim_type: SimType,
) -> std::result::Result<Self, Vec<Error>> {
let interpreter = Interpreter::with_debug(
sources,
Expand All @@ -1869,6 +1917,7 @@ impl Debugger {
let entry_exec_graph = unit.entry_exec_graph.clone();
Ok(Self {
interpreter,
simulator: sim_type.into(),
position_encoding,
state: State::new(
source_package_id,
Expand All @@ -1881,11 +1930,20 @@ impl Debugger {
}

pub fn from(interpreter: Interpreter, position_encoding: Encoding) -> Self {
Self::from_with_sim(interpreter, position_encoding, SimType::Sparse)
}

pub fn from_with_sim(
interpreter: Interpreter,
position_encoding: Encoding,
sim_type: SimType,
) -> Self {
let source_package_id = interpreter.source_package;
let unit = interpreter.fir_store.get(source_package_id);
let entry_exec_graph = unit.entry_exec_graph.clone();
Self {
interpreter,
simulator: sim_type.into(),
position_encoding,
state: State::new(
source_package_id,
Expand All @@ -1906,8 +1964,8 @@ impl Debugger {
breakpoints: &[StmtId],
step: StepAction,
) -> std::result::Result<StepResult, Vec<Error>> {
self.state
.eval(
let result = match &mut self.simulator {
DebuggerSimulator::Sparse => self.state.eval(
&self.interpreter.fir_store,
&mut self.interpreter.env,
&mut TracingBackend::new(
Expand All @@ -1917,15 +1975,28 @@ impl Debugger {
receiver,
breakpoints,
step,
),
DebuggerSimulator::Clifford(simulator) => self.state.eval(
&self.interpreter.fir_store,
&mut self.interpreter.env,
&mut TracingBackend::new(
simulator.as_mut(),
self.interpreter.circuit_tracer.as_mut(),
),
receiver,
breakpoints,
step,
),
};

result.map_err(|(error, call_stack)| {
eval_error(
self.interpreter.compiler.package_store(),
&self.interpreter.fir_store,
call_stack,
error,
)
.map_err(|(error, call_stack)| {
eval_error(
self.interpreter.compiler.package_store(),
&self.interpreter.fir_store,
call_stack,
error,
)
})
})
}

#[must_use]
Expand Down Expand Up @@ -1960,8 +2031,29 @@ impl Debugger {
.collect()
}

#[allow(clippy::type_complexity)]
pub fn try_capture_quantum_state(
&mut self,
) -> std::result::Result<(Vec<(BigUint, Complex<f64>)>, usize), String> {
match &self.simulator {
DebuggerSimulator::Sparse => Ok(self.interpreter.get_quantum_state()),
DebuggerSimulator::Clifford(_) => Err(
"quantum state visualization is not supported in Clifford simulation".to_string(),
),
}
}

#[must_use]
pub fn supports_quantum_state_capture(&self) -> bool {
match &self.simulator {
DebuggerSimulator::Sparse => self.interpreter.sim.supports_quantum_state_capture(),
DebuggerSimulator::Clifford(simulator) => simulator.supports_quantum_state_capture(),
}
}

pub fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex<f64>)>, usize) {
self.interpreter.get_quantum_state()
self.try_capture_quantum_state()
.expect("debugger simulator should support quantum state capture")
}

pub fn circuit(&self) -> Circuit {
Expand Down
Loading