Skip to content
Merged
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
2 changes: 1 addition & 1 deletion npm/qsharp/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export type {
export { default as openqasm_samples } from "./openqasm-samples.generated.js";
export type { ProjectLoader } from "./project.js";
export { default as samples } from "./samples.generated.js";
export type { CircuitGroup as CircuitData } from "./shared/circuit.js";
export type { CircuitGroup as CircuitData } from "./data-structures/circuit.js";
export * as utils from "./utils.js";
export { log, type LogLevel, type ProjectType, type TargetProfile };
export type {
Expand Down
2 changes: 1 addition & 1 deletion npm/qsharp/src/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import {
CURRENT_VERSION,
type CircuitGroup as CircuitData,
} from "../shared/circuit.js";
} from "../data-structures/circuit.js";
import {
IDocFile,
IOperationInfo,
Expand Down
1 change: 1 addition & 0 deletions npm/qsharp/src/data-structures/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This directory contains the data structures used in both `qsharp/src/` and `qsharp/ux/`.
272 changes: 272 additions & 0 deletions npm/qsharp/src/data-structures/circuit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { isRegister, Register } from "./register.js";

/**
* Current format version.
*/
export const CURRENT_VERSION = 1;

export interface CircuitGroup {
circuits: Circuit[];
version: number;
}

/**
* Runtime check: is this a valid CircuitGroup?
*/
export function isCircuitGroup(obj: any): obj is CircuitGroup {
return (
obj &&
typeof obj === "object" &&
typeof obj.version === "number" &&
Array.isArray(obj.circuits) &&
obj.circuits.length > 0 &&
obj.circuits.every(isCircuit)
);
}

/**
* Circuit to be visualized.
*/
export interface Circuit {
/** Array of qubit resources. */
qubits: Qubit[];
componentGrid: ComponentGrid;
}

/**
* Runtime check: is this a valid Circuit?
*/
export function isCircuit(obj: any): obj is Circuit {
return (
obj &&
typeof obj === "object" &&
Array.isArray(obj.qubits) &&
obj.qubits.every(isQubit) &&
Array.isArray(obj.componentGrid) &&
obj.componentGrid.every(isColumn)
);
}

export type ComponentGrid = Column[];

export interface Column {
components: Component[];
}

/**
* Runtime check: is this a valid Column?
*/
export function isColumn(obj: any): obj is Column {
return (
obj &&
typeof obj === "object" &&
Array.isArray(obj.components) &&
obj.components.every(isOperation)
);
}

/**
* Represents a component of a circuit. Currently, the only component is an operation.
* In the future, this may be extended to include other components.
*/
export type Component = Operation;

/**
* Represents a unique qubit resource bit.
*/
export interface Qubit {
/** Qubit ID. */
id: number;
/** Number of measurement results associated to the qubit. */
numResults?: number;
}

/**
* Runtime check: is this a valid Qubit?
*/
export function isQubit(obj: any): obj is Qubit {
return (
obj &&
typeof obj === "object" &&
typeof obj.id === "number" &&
// numResults is optional, but if present must be a number
(obj.numResults === undefined || typeof obj.numResults === "number")
);
}

/**
* Base type for operations.
*/
export interface BaseOperation {
/** Gate label. */
gate: string;
/** Formatted gate arguments. */
args?: string[];
/** The parameters expected for the operation. */
params?: Parameter[];
/** Nested operations within this operation. */
children?: ComponentGrid;

/** Custom data attributes to attach to gate element.
Note that this is never written to file, so it is not part of the circuit schema */
dataAttributes?: DataAttributes;

/** Whether gate is a conditional operation. */
isConditional?: boolean;
/** Specify conditions on when to render operation. */
conditionalRender?: ConditionalRender;
}

/**
* Runtime check: is this a valid BaseOperation?
*/
function isBaseOperation(obj: any): obj is BaseOperation {
return (
obj &&
typeof obj === "object" &&
typeof obj.gate === "string" &&
// args is optional, but if present must be an array of strings
(obj.args === undefined ||
(Array.isArray(obj.args) &&
obj.args.every((arg: any) => typeof arg === "string"))) &&
// params is optional, but if present must be an array of Parameter
(obj.params === undefined ||
(Array.isArray(obj.params) && obj.params.every(isParameter))) &&
// children is optional, but if present must be a ComponentGrid
(obj.children === undefined ||
(Array.isArray(obj.children) && obj.children.every(isColumn))) &&
// dataAttributes is optional, but if present must be an object with string values
(obj.dataAttributes === undefined ||
(typeof obj.dataAttributes === "object" &&
obj.dataAttributes !== null &&
Object.values(obj.dataAttributes).every(
(val) => typeof val === "string",
))) &&
// isConditional is optional, but if present must be boolean
(obj.isConditional === undefined ||
typeof obj.isConditional === "boolean") &&
// conditionalRender is optional, but if present must be a valid enum value
(obj.conditionalRender === undefined ||
Object.values(ConditionalRender).includes(obj.conditionalRender))
);
}

/**
* Represents a measurement operation and the registers it acts on.
*/
export interface Measurement extends BaseOperation {
/** Discriminator for the Operation type */
kind: "measurement";
/** The qubit registers the gate measures. */
qubits: Register[];
/** The classical registers the gate writes to. */
results: Register[];
}

/**
* Represents a unitary operation and the registers it acts on.
*/
export interface Unitary extends BaseOperation {
/** Discriminator for the Operation type */
kind: "unitary";
/** Target registers the gate acts on. */
targets: Register[];
/** Control registers the gate acts on. */
controls?: Register[];
/** Whether gate is an adjoint operation. */
isAdjoint?: boolean;
}

/**
* Represents a gate that sets its targets to a specific state.
*/
export interface Ket extends BaseOperation {
/** Discriminator for the Operation type */
kind: "ket";
/** Target registers the gate acts on. */
targets: Register[];
}

/**
* Union type for operations.
*/
export type Operation = Unitary | Measurement | Ket;

/**
* Runtime check: is this a valid Operation?
*/
export function isOperation(obj: any): obj is Operation {
if (!isBaseOperation(obj)) return false;
// Re-cast to any so we can check discriminated fields without narrowing
const op: any = obj;
if (op.kind === undefined || typeof op.kind !== "string") return false;
switch (op.kind) {
case "unitary":
return (
Array.isArray(op.targets) &&
op.targets.every(isRegister) &&
// controls is optional
(op.controls === undefined ||
(Array.isArray(op.controls) && op.controls.every(isRegister))) &&
// isAdjoint is optional
(op.isAdjoint === undefined || typeof op.isAdjoint === "boolean")
);
case "measurement":
return (
Array.isArray(op.qubits) &&
op.qubits.every(isRegister) &&
Array.isArray(op.results) &&
op.results.every(isRegister)
);
case "ket":
return Array.isArray(op.targets) && op.targets.every(isRegister);
default:
return false;
}
}

/**
* A parameter for an operation.
*/
export interface Parameter {
/** Parameter name. */
name: string;
/** Parameter's Q# type. */
type: string;
}

/**
* Runtime check: is this a valid Parameter?
*/
export function isParameter(obj: any): obj is Parameter {
return (
obj &&
typeof obj === "object" &&
typeof obj.name === "string" &&
typeof obj.type === "string"
);
}

/**
* Conditions on when to render the given operation.
*/
export enum ConditionalRender {
/** Always rendered. */
Always,
/** Render classically-controlled operation when measurement is a zero. */
OnZero,
/** Render classically-controlled operation when measurement is a one. */
OnOne,
/** Render operation as a group of its nested operations. */
AsGroup,
}

/**
* Custom data attributes (e.g. data-{attr}="{val}")
*/
export interface DataAttributes {
[attr: string]: string;
}
Loading
Loading