///|
/// An immutable unitary effect.
struct Gate {
label : String
shape : Array[UInt32]
/// Immutable numerical kernel, shared by derived values when possible.
kernel_value : &UnitaryKernel
size : Int32
}
///|
pub fn Gate::name(self : Gate) -> String {
self.label
}
///|
pub fn Gate::dimensions(self : Gate) -> Array[UInt32] {
self.shape.copy()
}
///|
pub fn Gate::arity(self : Gate) -> Int32 {
self.shape.length()
}
///|
pub fn Gate::dimension(self : Gate) -> Int32 {
self.size
}
///|
/// Return the immutable unitary kernel for this gate.
pub fn Gate::kernel(self : Gate) -> &UnitaryKernel {
self.kernel_value
}
///|
/// Check shape, finite entries, and U†U before accepting a matrix.
/// max_dimension bounds the cubic validation work and quadratic storage.
/// Entries are in row order: row * dimension + column.
/// Tolerance must be finite and in (0, 1e-6]; it bounds absolute errors in
/// column inner products. Accepted entries are kept without normalization.
pub fn Gate::unitary(
name : String,
dimensions : Array[UInt32],
matrix : Array[@maths.Complex],
tolerance~ : Double = 1.0e-10,
max_dimension~ : Int32 = 256,
) -> Gate raise QuantumError {
if tolerance.is_nan() ||
tolerance.is_inf() ||
tolerance <= 0.0 ||
tolerance > 1.0e-6 {
raise InvalidGate("tolerance must be finite and in (0, 1e-6]")
}
let size = space_size(dimensions, max_size=max_dimension)
if size > 46340 || matrix.length() != size * size {
raise InvalidGate("matrix size does not match the shape")
}
for entry in matrix {
if !entry.is_finite() {
raise InvalidGate("matrix entries must be finite")
}
}
for col in 0.. tolerance ||
im.abs() > tolerance ||
re.is_nan() ||
re.is_inf() ||
im.is_nan() ||
im.is_inf() {
raise InvalidGate("matrix is not unitary")
}
}
}
{
label: name,
shape: dimensions.copy(),
kernel_value: matrix_kernel(matrix.copy(), size),
size,
}
}
///|
/// Bind a custom unitary kernel to a shape.
pub fn Gate::from_kernel(
name : String,
dimensions : Array[UInt32],
kernel : &UnitaryKernel,
max_dimension~ : Int32 = 1048576,
) -> Gate raise QuantumError {
let size = space_size(dimensions, max_size=max_dimension)
if kernel.dimension() != size {
raise InvalidGate("kernel dimension or workspace is invalid")
}
{ label: name, shape: dimensions.copy(), kernel_value: kernel, size, }
}
///|
/// Constructs a monomial unitary gate from a column permutation and phase factors.
pub fn Gate::monomial(
name : String,
dimensions : Array[UInt32],
columns : Array[Int32],
phases : Array[@maths.Complex],
max_dimension~ : Int32 = 1048576,
) -> Gate raise QuantumError {
let size = space_size(dimensions, max_size=max_dimension)
if columns.length() != size || phases.length() != size {
raise InvalidGate("permutation size does not match the shape")
}
let seen = Array::make(size, false)
for row, col in columns {
if col < 0 || col >= size || seen[col] {
raise InvalidGate("columns must be a permutation")
}
seen[col] = true
let phase = phases[row]
if !phase.is_finite() || (phase.norm_squared() - 1.0).abs() > 1.0e-10 {
raise InvalidGate("phases must have unit magnitude")
}
}
{
label: name,
shape: dimensions.copy(),
kernel_value: MonomialKernel::{
columns: columns.copy(),
phases: phases.copy(),
},
size,
}
}
///|
/// Create a diagonal unitary from one phase per basis state.
pub fn Gate::diagonal(
name : String,
dimensions : Array[UInt32],
phases : Array[@maths.Complex],
max_dimension~ : Int32 = 1048576,
) -> Gate raise QuantumError {
let size = space_size(dimensions, max_size=max_dimension)
Gate::monomial(
name,
dimensions,
Array::makei(size, fn(i) { i }),
phases,
max_dimension~,
)
}
///|
pub impl Debug for Gate with fn to_repr(self) {
Debug::to_repr((self.label, self.shape))
}
///|
pub extend Gate with Debug::{to_repr}