Skip to content

Commit

Permalink
Merge e6806ea into 0b8e919
Browse files Browse the repository at this point in the history
  • Loading branch information
minestarks authored Apr 15, 2024
2 parents 0b8e919 + e6806ea commit 559edf7
Show file tree
Hide file tree
Showing 16 changed files with 674 additions and 284 deletions.
94 changes: 70 additions & 24 deletions compiler/qsc/src/interpret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,19 +210,7 @@ impl Interpreter {
fir_store,
lowerer: qsc_lowerer::Lowerer::new().with_debug(dbg),
env: Env::default(),
sim: BackendChain::new(
SparseSim::new(),
CircuitBuilder::new(CircuitConfig {
// When using in conjunction with the simulator,
// the circuit builder should *not* perform base profile
// decompositions, in order to match the simulator's behavior.
//
// Note that conditional compilation (e.g. @Config(Base) attributes)
// will still respect the selected profile. This also
// matches the behavior of the simulator.
base_profile: false,
}),
),
sim: sim_circuit_backend(),
quantum_seed: None,
classical_seed: None,
package: map_hir_package_to_fir(package_id),
Expand Down Expand Up @@ -412,16 +400,16 @@ impl Interpreter {
///
/// An operation can be specified by its name or a lambda expression that only takes qubits.
/// e.g. `Sample.Main` , `qs => H(qs[0])`
///
/// If `simulate` is specified, the program is simulated and the resulting
/// circuit is returned (a.k.a. trace mode). Otherwise, the circuit is generated without
/// simulation. In this case circuit generation may fail if the program contains dynamic
/// behavior (quantum operations that are dependent on measurement results).
pub fn circuit(
&mut self,
entry: CircuitEntryPoint,
simulate: bool,
) -> std::result::Result<Circuit, Vec<Error>> {
let mut sink = std::io::sink();
let mut out = GenericReceiver::new(&mut sink);
let mut sim = CircuitBuilder::new(CircuitConfig {
base_profile: self.capabilities.is_empty(),
});

let entry_expr = match entry {
CircuitEntryPoint::Operation(operation_expr) => {
let (item, functor_app) = self.eval_to_operation(&operation_expr)?;
Expand All @@ -433,13 +421,23 @@ impl Interpreter {
CircuitEntryPoint::EntryPoint => None,
};

if let Some(entry_expr) = entry_expr {
self.run_with_sim(&mut sim, &mut out, &entry_expr)?
let circuit = if simulate {
let mut sim = sim_circuit_backend();

self.run_with_sim_no_output(entry_expr, &mut sim)?;

sim.chained.finish()
} else {
self.eval_entry_with_sim(&mut sim, &mut out)
}?;
let mut sim = CircuitBuilder::new(CircuitConfig {
base_profile: self.capabilities.is_empty(),
});

self.run_with_sim_no_output(entry_expr, &mut sim)?;

sim.finish()
};

Ok(sim.finish())
Ok(circuit)
}

/// Runs the given entry expression on the given simulator with a new instance of the environment
Expand Down Expand Up @@ -468,6 +466,38 @@ impl Interpreter {
))
}

fn run_with_sim_no_output(
&mut self,
entry_expr: Option<String>,
sim: &mut impl Backend<ResultType = impl Into<val::Result>>,
) -> InterpretResult {
let mut sink = std::io::sink();
let mut out = GenericReceiver::new(&mut sink);

let (package_id, graph) = if let Some(entry_expr) = entry_expr {
// entry expression is provided
(self.package, self.compile_entry_expr(&entry_expr)?.0.into())
} else {
// no entry expression, use the entrypoint in the package
(self.source_package, self.get_entry_exec_graph()?)
};

if self.quantum_seed.is_some() {
sim.set_seed(self.quantum_seed);
}

eval(
package_id,
self.classical_seed,
graph,
self.compiler.package_store(),
&self.fir_store,
&mut Env::default(),
sim,
&mut out,
)
}

fn compile_entry_expr(
&mut self,
expr: &str,
Expand Down Expand Up @@ -581,6 +611,22 @@ impl Interpreter {
}
}

fn sim_circuit_backend() -> BackendChain<SparseSim, CircuitBuilder> {
BackendChain::new(
SparseSim::new(),
CircuitBuilder::new(CircuitConfig {
// When using in conjunction with the simulator,
// the circuit builder should *not* perform base profile
// decompositions, in order to match the simulator's behavior.
//
// Note that conditional compilation (e.g. @Config(Base) attributes)
// will still respect the selected profile. This also
// matches the behavior of the simulator.
base_profile: false,
}),
)
}

/// Describes the entry point for circuit generation.
pub enum CircuitEntryPoint {
/// An operation. This must be a callable name or a lambda
Expand Down
68 changes: 44 additions & 24 deletions compiler/qsc/src/interpret/circuit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn empty() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

expect![].assert_eq(&circ.to_string());
Expand All @@ -61,7 +61,7 @@ fn one_gate() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

expect![[r"
Expand All @@ -86,7 +86,7 @@ fn rotation_gate() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

// The wire isn't visible here since the gate label is longer
Expand Down Expand Up @@ -115,7 +115,7 @@ fn classical_for_loop() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

expect![[r"
Expand All @@ -142,7 +142,7 @@ fn m_base_profile() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -171,7 +171,7 @@ fn m_unrestricted_profile() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -199,7 +199,7 @@ fn mresetz_unrestricted_profile() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -227,7 +227,7 @@ fn mresetz_base_profile() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -265,7 +265,7 @@ fn unrestricted_profile_result_comparison() {
interpreter.set_quantum_seed(Some(2));

let circuit_err = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect_err("circuit should return error")
.pop()
.expect("error should exist");
Expand All @@ -283,9 +283,23 @@ fn unrestricted_profile_result_comparison() {
let mut out = std::io::sink();
let mut r = GenericReceiver::new(&mut out);

// Counterintuitive but expected: result comparisons
// are okay if calling get_circuit() after incremental
// evaluation, because we're using the current simulator
// Result comparisons are okay when tracing
// circuit with the simulator.
let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint, true)
.expect("circuit generation should succeed");

expect![[r"
q_0 ── H ──── M ──── X ─── |0〉 ─
╘═════════════════
q_1 ── H ──── M ─── |0〉 ────────
╘═════════════════
"]]
.assert_eq(&circ.to_string());

// Result comparisons are also okay if calling
// get_circuit() after incremental evaluation,
// because we're using the current simulator
// state.
interpreter
.eval_fragments(&mut r, "Test.Main();")
Expand Down Expand Up @@ -320,7 +334,7 @@ fn custom_intrinsic() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -349,7 +363,7 @@ fn custom_intrinsic_classical_arg() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

// A custom intrinsic that doesn't take qubits just doesn't
Expand Down Expand Up @@ -380,7 +394,7 @@ fn custom_intrinsic_one_classical_arg() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

// A custom intrinsic that doesn't take qubits just doesn't
Expand Down Expand Up @@ -418,7 +432,7 @@ fn custom_intrinsic_mixed_args() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::EntryPoint)
.circuit(CircuitEntryPoint::EntryPoint, false)
.expect("circuit generation should succeed");

// This is one gate that spans ten target wires, even though the
Expand Down Expand Up @@ -458,7 +472,7 @@ fn operation_with_qubits() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::Operation("Test.Test".into()))
.circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -488,7 +502,7 @@ fn operation_with_qubits_base_profile() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::Operation("Test.Test".into()))
.circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -535,7 +549,7 @@ fn operation_with_qubit_arrays() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::Operation("Test.Test".into()))
.circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -587,7 +601,10 @@ fn adjoint_operation() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::Operation("Adjoint Test.Foo".into()))
.circuit(
CircuitEntryPoint::Operation("Adjoint Test.Foo".into()),
false,
)
.expect("circuit generation should succeed");

expect![[r"
Expand All @@ -608,7 +625,7 @@ fn lambda() {
);

let circ = interpreter
.circuit(CircuitEntryPoint::Operation("q => H(q)".into()))
.circuit(CircuitEntryPoint::Operation("q => H(q)".into()), false)
.expect("circuit generation should succeed");

expect![[r"
Expand Down Expand Up @@ -649,7 +666,10 @@ fn controlled_operation() {
);

let circ_err = interpreter
.circuit(CircuitEntryPoint::Operation("Controlled Test.SWAP".into()))
.circuit(
CircuitEntryPoint::Operation("Controlled Test.SWAP".into()),
false,
)
.expect_err("circuit generation should fail");

// Controlled operations are not supported at the moment.
Expand Down Expand Up @@ -682,7 +702,7 @@ fn internal_operation() {
);

let circ_err = interpreter
.circuit(CircuitEntryPoint::Operation("Test.Test".into()))
.circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
.expect_err("circuit generation should fail");

expect![[r#"
Expand Down Expand Up @@ -731,7 +751,7 @@ fn operation_with_non_qubit_args() {
);

let circ_err = interpreter
.circuit(CircuitEntryPoint::Operation("Test.Test".into()))
.circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
.expect_err("circuit generation should fail");

expect![[r"
Expand Down
8 changes: 7 additions & 1 deletion compiler/qsc_circuit/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,13 @@ impl Backend for Builder {
Some(classical_args)
},
));
Some(Ok(Value::unit()))

match name {
// Special case this known intrinsic to match the simulator
// behavior, so that our samples will work
"BeginEstimateCaching" => Some(Ok(Value::Bool(true))),
_ => Some(Ok(Value::unit())),
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion npm/qsharp/src/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export interface ICompiler {
getCircuit(
config: ProgramConfig,
target: TargetProfile,
simulate: boolean,
operation?: IOperationInfo,
): Promise<CircuitData>;

Expand Down Expand Up @@ -236,13 +237,15 @@ export class Compiler implements ICompiler {
async getCircuit(
config: ProgramConfig,
target: TargetProfile,
simulate: boolean,
operation?: IOperationInfo,
): Promise<CircuitData> {
return this.wasm.get_circuit(
config.sources,
target,
operation,
config.languageFeatures || [],
simulate,
operation,
);
}

Expand Down
Loading

0 comments on commit 559edf7

Please sign in to comment.