Skip to content

feat(python): load visual circuits directly in Context#3291

Open
tzh476 wants to merge 3 commits into
microsoft:mainfrom
tzh476:feat/load-visual-circuit
Open

feat(python): load visual circuits directly in Context#3291
tzh476 wants to merge 3 commits into
microsoft:mainfrom
tzh476:feat/load-visual-circuit

Conversation

@tzh476
Copy link
Copy Markdown

@tzh476 tzh476 commented Jun 4, 2026

Fixes #3232.

Summary

  • add Context.load_circuit(path, *, index=0) for standalone .qsc visual circuit files
  • support selecting a circuit from multi-circuit .qsc files with index=0 by default
  • reuse the existing visual-circuit-to-Q# converter through the native Python module
  • register the generated Q# in the same Context and return a zero-argument callable suitable for ctx.run(...) and ctx.circuit(...)
  • cover the direct-load path with a Python test using the existing in-memory filesystem fixture

AI Assistance Disclosure

I used Codex to help inspect the existing QDK visual-circuit conversion path, draft the Python/Rust integration changes, and prepare tests. I manually reviewed the patch and verified it locally with the checks below.

Testing

cargo fmt --all -- --check
cargo check -p qdk
cargo clippy -p qdk --all-targets --all-features -- -D warnings
maturin develop --skip-install
git diff --check
python3 -m py_compile source/qdk_package/qdk/_context.py source/qdk_package/tests/test_project.py
python -m pytest source/qdk_package/tests/test_project.py::test_load_circuit -q
python -m pytest source/qdk_package/tests/test_project.py -q

test_load_circuit passed, and all 12 tests in test_project.py passed.

@tzh476
Copy link
Copy Markdown
Author

tzh476 commented Jun 4, 2026

@microsoft-github-policy-service agree

@tzh476
Copy link
Copy Markdown
Author

tzh476 commented Jun 4, 2026

Hi @billti @idavis @minestarks, I have signed the CLA. Could you please approve the pending workflows and review when convenient? Thanks!

Change-Id: I3b8a9eadb78dbe1f61b1ebb058a78bc1b0985a29
@tzh476 tzh476 force-pushed the feat/load-visual-circuit branch from f084b04 to e70656b Compare June 4, 2026 14:39
Comment thread source/qdk_package/qdk/_context.py Fixed
Change-Id: Id6234644a0f42eab2b6e6472df0464521947c71a
@tzh476
Copy link
Copy Markdown
Author

tzh476 commented Jun 4, 2026

I addressed the GitHub Advanced Security / DevSkim review in c53b195 by documenting the generated-source boundary and adding the targeted DS189424 suppression on the vetted Q# eval line. The review thread is resolved now. Could you please approve the refreshed workflows when convenient? Thanks!

@tzh476
Copy link
Copy Markdown
Author

tzh476 commented Jun 4, 2026

All refreshed checks are passing now, including Build and test, Rust Unit tests, Integration tests, DevSkim, and the CLA check. The prior GitHub Advanced Security / DevSkim review thread is resolved as well.

Could you please review this when convenient? Thanks!

@minestarks
Copy link
Copy Markdown
Member

Thank you @tzh476 ! I'm taking a look now - will post comments shortly.

Copy link
Copy Markdown
Member

@minestarks minestarks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice contribution, thank you! Just a couple of requests, please.

Comment thread source/qdk_package/qdk/_context.py Outdated
if getattr(struct_type, "_qdk_context") is not self:
raise QSharpError("This struct belongs to a different Context. ")

def load_circuit(self, path: str, *, index: int = 0) -> Callable:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussing this within the team, we landed on import_circuit as a better name for the public API. (It mirrors import_openqasm , which has more or less the same semantics). Would you mind renaming it?

raise QSharpError("This struct belongs to a different Context. ")

def load_circuit(self, path: str, *, index: int = 0) -> Callable:
"""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also after team discussion: it would be valuable for this public API to take a program_type parameter (ProgramType.File | ProgramType.Operation) to mirror how import_openqasm treats its input. The ProgramType enum should be accessible here via ._native

  • File (current behavior): treats the circuit as a standalone program, the returned callable takes no arguments and can be invoked directly from Python.

  • Operation: would skip the wrapper and register the circuit operation itself (which takes a Qubit[] parameter). This is useful for composing circuits inside larger Q# programs. However, since qubits have no Python representation, the callable could only be invoked via a string entry expression (e.g. ctx.run("{ use qs = Qubit[N]; myCircuit(qs) }", shots)), not via ctx.code.myCircuit(...). You can add a unit test for this to validate behavior. See how import_openqasm with ProgramType.Operation works for the precedent.

The default should be File if no program type is passed.

Sorry if this seems like scope creep - when we filed the issue, we hadn't yet ironed out all open questions, but now that we see the API all fleshed out, it became clear that having both options would be useful.

Thank you!

Change-Id: I962310d4ae6aaf624aee2632d0d0f5bb7b589328
@tzh476
Copy link
Copy Markdown
Author

tzh476 commented Jun 5, 2026

Thanks for the review. I pushed cc107d855 to address both requests:

  • renamed the visual circuit API to import_circuit;
  • added program_type with ProgramType.File as the default wrapper behavior and ProgramType.Operation for registering the circuit operation itself for Q# entry-expression composition;
  • updated the existing visual-circuit tests and added an Operation-mode test using a string entry expression.

Local validation:

python3 -m py_compile source/qdk_package/qdk/_context.py source/qdk_package/tests/test_project.py
git diff --check

I attempted the targeted pytest locally, but this checkout cannot resolve the repository-local qsharp==0.0.0 dependency through uv outside the full build environment, so I am relying on CI for the native-backed test run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow running visual circuit files (.qsc) directly from Python

3 participants