PhotonSharp

PhotonSharp / Language documentation

λλ Extended Language Guide

Learn the small core language first. Then add optical components to the same signal-flow expressions, with connections generated from your code.

01 / Orientation

What a .photon program describes

A λλ program describes how optical signals move through operations. Each expression consumes its input signal and produces a new value. The compiler generates components and wires from that flow. You do not specify canvas coordinates, component IDs, or connect() calls in the source.

Start by editing program.photon in the Code tab. The program is the source of the generated circuit; canvas positions and chip-layer metadata are kept separately. The current evaluator is an idealized optical model, not a foundry process simulation.

PhotonSharp code editor with a .photon program and compiler feedback
The Code tab shows source, compiler feedback, and the active .photon file.

Reading path: Sections 02–06 teach the core language with no extended tools. Sections 07–08 add PhotonSharp optical operations. Use the searchable API reference for signatures, units, return types, and valid ranges.

02 / Core λλ

Your first program

// One input, one phase shift, one output.
let light = input(0) in
let shifted = phase(pi, light) in
output(1) <- shifted;
()
  1. input(0) creates an optical input at port 0. The ideal test bench defaults to 1 mW at 1550 nm.
  2. let light = … in gives the resulting signal a name for the rest of the expression.
  3. phase(pi, light) consumes light and returns a signal shifted by π radians.
  4. output(1) <- shifted; consumes the final signal at output port 1.
  5. () is the Unit value that ends the program.

Click Run program for ideal output readings. Open Circuit Designer to see the inferred graph. If you edit the source, the graph is regenerated from the new signal flow.

03 / Core λλ

How expressions are written

FormPurpose
let name = value in bodyBind one value and continue in the body. Nest bindings to build a chain.
unpack pair as (a, b) in bodyGive both signals from a two-output operation separate names.
(a, b)Supply an ordered optical pair to a two-input operation.
output(port) <- signal; bodyConsume a signal at a numbered output and continue.
()Unit value, normally the end of the program.

Names begin with a letter or underscore and then contain letters, digits, or underscores. Port numbers are nonnegative integers. Whitespace and line breaks separate tokens but do not define blocks. Comments start with // or # and run to the end of a line.

Numeric parameters

Write decimal or scientific notation, such as 0.5, -1, or 1e-3. pi and π are equivalent. Parameter expressions allow multiplication and division, for example pi / 2 or 2 * pi. The parameter parser does not implement general addition, subtraction, or numeric variables; calculate those values before inserting them into the source. Parameters must be finite.

Operations use positional arguments. The language currently has no imports, user-defined functions, classes, methods, loops, or conditionals. For a complete alphabetical inventory of the actual surface, use the API reference.

04 / Core λλ

Types and the one-use signal rule

The main types are Optical (one signal), (Optical, Optical) (a pair of signals), Real (a numeric literal or parameter), and Unit (() or a consuming operation). A signal is linear: after an operation uses it, that name cannot be used again. Every produced optical signal must reach exactly one consumer.

// split creates two new signals; both must be consumed.
let light = input(0) in
unpack split(0.5, light) as (upper, lower) in
output(1) <- upper;
output(2) <- lower;
()

The two branches are not references to the same signal. If you need another branch, use another two-output operation. Do not reuse light after split, and do not leave lower unused. Each input(port) and output(port) number must be unique within a program.

05 / Core λλ

The complete core operation set

OperationSignal flowWhat it computes
input(port)none → OpticalRead one test-bench optical input.
phase(radians, signal)Optical → OpticalRotate the complex field by the given angle.
split(ratio, signal)Optical → pairCore amplitude split: fields become ratio × E and (1 − ratio) × E. At 0.5, each output has one quarter of the input power.
unitary(matrix, (a, b))pair → pairApply a 2×2 unitary transform. Matrix can be H, I, or an explicit 2×2 matrix.
output(port) <- signal;Optical → consumedExpose one optical output for evaluation.

For an explicit matrix, write [[a, b], [c, d]]. Entries can be real numbers or complex(real, imag). The compiler rejects a matrix that is not unitary. The built-in H is the normalized Hadamard matrix; I is identity.

06 / Core λλ

Two inputs through a unitary

// H mixes two optical inputs and produces two outputs.
let a = input(0) in
let b = input(1) in
unpack unitary(H, (a, b)) as (upper, lower) in
output(2) <- upper;
output(3) <- lower;
()

Both inputs are consumed by the pair, and unpack names both results. Change one input’s phase to see how coherent fields interfere. This is still core λλ; no extended component is required.

07 / PhotonSharp optical operations

Use the extended toolset

PhotonSharp adds typed optical operations to the same expression syntax. Enable Extended Lambda Lambda Toolset in Marketplace for a workspace; new projects include it by default. You still write signal expressions, and the compiler still infers components and connections.

OperationSignal flowPurpose
source(power_mw, phase_rad)none → OpticalCreate a coherent model source. Zero power supplies a vacuum input.
waveguide(length_um, loss_db_cm, effective_index, signal)Optical → OpticalApply propagation phase and distributed loss.
attenuate(loss_db, signal)Optical → OpticalApply lumped optical loss.
kerr(gamma, signal)Optical → OpticalApply illustrative power-dependent phase; optional loss_db can be inserted before the signal.
power_split(ratio, signal)Optical → pairSplit by power fraction, unlike core split.
coupler(ratio, (a, b))pair → pairMix two coherent fields with an ideal directional coupler.
mzi(phase_rad, coupling_ratio, (a, b))pair → pairModel a two-coupler Mach–Zehnder interferometer.
probe(threshold_mw, signal)Optical → OpticalRecord a threshold bit while passing the optical signal onward.
detect(responsivity_a_w, signal);Optical → consumedTerminate a signal and report modeled photocurrent.

Two kinds of split: split(0.5, E) yields fields of 0.5 E each, so each has 25% of input power. power_split(0.5, E) yields fields of approximately 0.707 E, so each has 50% of input power. Both require unpack and two consumers.

probe produces a measurement bit, not a new Boolean optical signal. detect ends the optical path. All parameter units and ranges are listed in the reference table.

08 / Worked example

Mix, observe, and terminate two branches

// Two coherent sources enter a 50:50 coupler.
let a = source(1, 0) in
let b = source(0, 0) in
unpack coupler(0.5, (a, b)) as (upper, lower) in
let observed = probe(0.25, upper) in
output(1) <- observed;
detect(0.8, lower);
()

The upper branch is measured and sent to an output; the lower branch ends at a detector. Try changing b to source(1, pi / 2) to examine interference. A detector can appear before the next expression separated by a semicolon; arbitrary semicolon sequencing of live optical values is not supported.

Generated circuit graph in PhotonSharp Circuit Designer
Circuit Designer shows the components and connections inferred from the expressions.
09 / In PhotonSharp

From source to readings

  1. Create or open a project. Select the active .photon file in Explorer. The home marker identifies the program being run.
  2. Edit and check. Enter a complete expression ending in (). Compiler problems identify source locations.
  3. Inspect the graph. Open Program flow for an expression overview and Circuit Designer for components and inferred wires. Use Fit to view for larger designs.
  4. Run the program. Program results shows ideal output power, phase, and complex amplitude. Probe and detector readings are separate model measurements.
  5. Review physical assumptions. Layout and Fabrication do not turn a valid λλ program into a foundry-approved mask. Choose process data and run appropriate physical checks before manufacturing claims.
PhotonSharp Program results showing optical output measurements
Program results presents ideal optical outputs; measurements depend on the configured model and inputs.
10 / Troubleshooting

Common errors and current limits

Message or symptomWhat to check
Unbound variableA signal name was misspelled or used before its let or unpack binding.
Not consumed / used twiceRoute every optical result once. Use unpack for both branches and end each branch with an output or detector.
Expected a pair / one signalTwo-input operations need (a, b); one-input operations need a single Optical value.
Invalid ratio or negative lossRatios must be from 0 to 1. Powers, lengths, losses, thresholds, and detector responsivity must be nonnegative; effective index must be positive.
Non-unitary matrixUse H, I, or a valid 2×2 unitary matrix.
Unexpected tokenCheck parentheses, commas, in, the output arrow <-, and the semicolon before the next expression.

IDE limits are 1 MB of source, 20,000 tokens, and 200 levels of expression nesting. The ideal λλ evaluator uses a single-wavelength, coherent, forward-only compact model. Circuit Designer also offers a separate Spectral analysis solver for linear scattering networks with wavelength sweeps, reflected waves, feedback, and supplied dispersion/thermal coefficients. Automatic setup detects the source wavelength and selects a bounded sample count; advanced controls remain available. Layout → Generic PDK layout automatically maps supported cells, routes optical ports and runs basic waveguide polygon checks, with GDSII/OASIS export for development review. See Advanced analysis for model scope and examples. The ideal evaluator does not model feedback, reflections, timing, noise, thermal effects, dispersion, or process variation. The separate spectral solver has its own scope described in Help. Kerr behavior is illustrative. Passing a truth table is not evidence of a fabricated device’s performance.

11 / Larger project

Study the optical ALU experiment

The complete one-bit ALU .photon file uses optical source powers for A, B, and Cin. It combines couplers, probes, detectors, and a Kerr element; the compiler generates the graph from the source. Follow the ALU walkthrough and eight-row truth table to reproduce its nominal tests.

// Excerpt only — use the complete file above to compile.
let A = source(1, 0) in
let vac_A = source(0, 0) in
unpack coupler(0.5, (A, vac_A)) as (split_A_a, split_A_b) in
// Continue consuming both branches in the full example.

The excerpt is intentionally incomplete. The full example is an idealized threshold-measurement experiment: probe bits do not regenerate optical logic levels, and operation selection remains external. The probe reference describes the exact measurement behavior.

PhotonSharp's generated one-bit optical ALU circuit
The full ALU graph is generated from λλ signal expressions.