Skip to main content

Custom Functions

You can extend the DSL with your own reusable, chainable routines using addFunction(). This lets you define named sub-algorithms that integrate naturally into the fluent API.

Defining a Custom Function

circuit({ qubits: 3 }, Q => {
Q.addFunction('bellState', (q, control, target) => {
q.bit(control).h().cx(q.bit(target));
});

// Call it through the fnc proxy
Q.fnc.bellState(0, 1);
Q.fnc.bellState(1, 2);
Q.all().measure();
});

The first argument to the function body is always the circuit (q). Any subsequent arguments are what you pass when calling it.

Reusing Across Circuits

Define your routines as plain functions and call addFunction in any circuit:

function registerRoutines(Q) {
Q.addFunction('qft2', (q) => {
q.bit(0).h();
q.bit(0).cp(q.bit(1), q.π.div(2));
q.bit(1).h();
});

Q.addFunction('bellState', (q, ctrl, tgt) => {
q.bit(ctrl).h().cx(q.bit(tgt));
});
}

const c1 = circuit({ qubits: 2 }, Q => {
registerRoutines(Q);
Q.fnc.qft2();
Q.all().measure();
});

const c2 = circuit({ qubits: 4 }, Q => {
registerRoutines(Q);
Q.fnc.bellState(0, 1);
Q.fnc.bellState(2, 3);
Q.all().measure();
});

Notes

  • Functions are registered on the circuit instance via addFunction and accessed through Q.fnc.
  • Q.fnc is a proxy that simply returns the circuit itself, so Q.fnc.myFunc(...) is equivalent to Q.myFunc(...) after registration.
  • The function body receives the circuit as its first argument, followed by any arguments you pass at the call site.