Skip to content

Commit

Permalink
Merge 5b174f1 into 1cd6e24
Browse files Browse the repository at this point in the history
  • Loading branch information
minestarks authored Apr 5, 2024
2 parents 1cd6e24 + 5b174f1 commit a1f1266
Show file tree
Hide file tree
Showing 17 changed files with 467 additions and 183 deletions.
6 changes: 1 addition & 5 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,11 +450,7 @@ def use_python_env(folder):
for dp, _, filenames in os.walk(samples_src)
for f in filenames
if f.endswith(".ipynb")
and not (
f.startswith("sample.")
or f.startswith("azure_submission.")
or f.startswith("circuits.")
)
and not (f.startswith("sample.") or f.startswith("azure_submission."))
]
python_bin = use_python_env(samples_src)

Expand Down
92 changes: 68 additions & 24 deletions compiler/qsc/src/interpret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,19 +205,7 @@ impl Interpreter {
fir_store,
lowerer: qsc_eval::lower::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 @@ -365,16 +353,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 @@ -386,13 +374,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 @@ -421,6 +419,36 @@ 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 {
(self.package, self.compile_entry_expr(&entry_expr)?.into())
} else {
(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 @@ -500,6 +528,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
48 changes: 27 additions & 21 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 Down Expand Up @@ -320,7 +320,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 +349,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 +380,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 +418,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 +458,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 +488,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 +535,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 +587,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 +611,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 +652,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 +688,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 +737,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 @@ -78,6 +78,7 @@ export interface ICompiler {
getCircuit(
config: ProgramConfig,
target: TargetProfile,
simulate: boolean,
operation?: IOperationInfo,
): Promise<CircuitData>;

Expand Down Expand Up @@ -223,13 +224,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 a1f1266

Please sign in to comment.