|
| 1 | +using LinearAlgebra: diag, qr |
| 2 | + |
1 | 3 | struct OpName{Name,Params} |
2 | | - function OpName{Name,Params}(params::NamedTuple) where {Name,Params} |
3 | | - return new{Name,(; Params..., params...)}() |
| 4 | + params::Params |
| 5 | + function OpName{N}(params::NamedTuple) where {N} |
| 6 | + return new{N,typeof(params)}(params) |
4 | 7 | end |
5 | 8 | end |
6 | 9 | name(::OpName{Name}) where {Name} = Name |
7 | | -params(::OpName{<:Any,Params}) where {Params} = Params |
| 10 | +params(n::OpName) = getfield(n, :params) |
8 | 11 |
|
9 | 12 | Base.getproperty(n::OpName, name::Symbol) = getfield(params(n), name) |
10 | 13 |
|
11 | | -OpName{Name,Params}(; kwargs...) where {Name,Params} = OpName{Name,Params}((; kwargs...)) |
12 | | - |
13 | | -OpName{N}(params::NamedTuple) where {N} = OpName{N,params}() |
14 | 14 | OpName{N}(; kwargs...) where {N} = OpName{N}((; kwargs...)) |
15 | 15 |
|
16 | 16 | # This compiles operator expressions, such as: |
@@ -461,35 +461,84 @@ end |
461 | 461 | alias(::OpName"√iSWAP") = √(OpName"iSWAP"()) |
462 | 462 | @op_alias "√iSwap" "√iSWAP" |
463 | 463 |
|
464 | | -## TODO: Bring back these definitions. |
465 | | -## function default_random_matrix(eltype::Type, s::Index...) |
466 | | -## n = prod(dim.(s)) |
467 | | -## return randn(eltype, n, n) |
468 | | -## end |
469 | | -## |
470 | | -## # Haar-random unitary |
471 | | -## # |
472 | | -## # Reference: |
473 | | -## # Section 4.6 |
474 | | -## # http://math.mit.edu/~edelman/publications/random_matrix_theory.pdf |
475 | | -## function op!( |
476 | | -## o::ITensor, |
477 | | -## ::OpName"RandomUnitary", |
478 | | -## ::SiteType"Generic", |
479 | | -## s1::Index, |
480 | | -## sn::Index...; |
481 | | -## eltype=ComplexF64, |
482 | | -## random_matrix=default_random_matrix(eltype, s1, sn...), |
483 | | -## ) |
484 | | -## s = (s1, sn...) |
485 | | -## Q, _ = NDTensors.qr_positive(random_matrix) |
486 | | -## t = itensor(Q, prime.(s)..., dag.(s)...) |
487 | | -## return settensor!(o, tensor(t)) |
488 | | -## end |
489 | | -## |
490 | | -## function op!(o::ITensor, ::OpName"randU", st::SiteType"Generic", s::Index...; kwargs...) |
491 | | -## return op!(o, OpName("RandomUnitary"), st, s...; kwargs...) |
492 | | -## end |
| 464 | +# TODO: Generalize to more controlled operators, right now |
| 465 | +# there is only one controlled operator that is enabled when |
| 466 | +# all of the last values of each sites/Qudit is set. See: |
| 467 | +# https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.UCGate |
| 468 | +# https://arxiv.org/abs/2410.05122 |
| 469 | +nsites(n::OpName"Controlled") = get(params(n), :ncontrol, 1) + nsites(n.arg) |
| 470 | +function (n::OpName"Controlled")(domain...) |
| 471 | + # Number of target sites. |
| 472 | + nt = nsites(n.arg) |
| 473 | + # Number of control sites. |
| 474 | + nc = get(params(n), :ncontrol, length(domain) - nt) |
| 475 | + @assert length(domain) == nc + nt |
| 476 | + d_control = prod(to_dim.(domain[1:nc])) |
| 477 | + return cat(I(d_control), n.arg(domain[(nc + 1):end]...); dims=(1, 2)) |
| 478 | +end |
| 479 | +@op_alias "CNOT" "Controlled" op = OpName"X"() |
| 480 | +@op_alias "CX" "Controlled" op = OpName"X"() |
| 481 | +@op_alias "CY" "Controlled" op = OpName"Y"() |
| 482 | +@op_alias "CZ" "Controlled" op = OpName"Z"() |
| 483 | +function alias(n::OpName"CPhase") |
| 484 | + return controlled(OpName"Phase"(; params(n)...)) |
| 485 | +end |
| 486 | +@op_alias "CPHASE" "CPhase" |
| 487 | +@op_alias "Cphase" "CPhase" |
| 488 | +function alias(n::OpName"CRx") |
| 489 | + return controlled(OpName"Rx"(; params(n)...)) |
| 490 | +end |
| 491 | +@op_alias "CRX" "CRx" |
| 492 | +function alias(::OpName"CRy") |
| 493 | + return controlled(OpName"Ry"(; params(n)...)) |
| 494 | +end |
| 495 | +@op_alias "CRY" "CRy" |
| 496 | +function alias(::OpName"CRz") |
| 497 | + return controlled(OpName"Rz"(; params(n)...)) |
| 498 | +end |
| 499 | +@op_alias "CRZ" "CRz" |
| 500 | +function alias(::OpName"CRn") |
| 501 | + return controlled(; arg=OpName"Rn"(; params(n)...)) |
| 502 | +end |
| 503 | +@op_alias "CRn̂" "CRn" |
| 504 | + |
| 505 | +@op_alias "CCNOT" "Controlled" ncontrol = 2 op = OpName"X"() |
| 506 | +@op_alias "Toffoli" "CCNOT" |
| 507 | +@op_alias "CCX" "CCNOT" |
| 508 | +@op_alias "TOFF" "CCNOT" |
| 509 | + |
| 510 | +@op_alias "CSWAP" "Controlled" ncontrol = 2 op = OpName"SWAP"() |
| 511 | +@op_alias "Fredkin" "CSWAP" |
| 512 | +@op_alias "CSwap" "CSWAP" |
| 513 | +@op_alias "CS" "CSWAP" |
| 514 | + |
| 515 | +@op_alias "CCCNOT" "Controlled" ncontrol = 3 op = OpName"X"() |
| 516 | + |
| 517 | +# Version of `sign` that returns one |
| 518 | +# if `x == 0`. |
| 519 | +function nonzero_sign(x) |
| 520 | + iszero(x) && return one(x) |
| 521 | + return sign(x) |
| 522 | +end |
| 523 | + |
| 524 | +function qr_positive(M::AbstractMatrix) |
| 525 | + Q, R = qr(M) |
| 526 | + Q′ = typeof(R)(Q) |
| 527 | + signs = nonzero_sign.(diag(R)) |
| 528 | + Q′ = Q′ * Diagonal(signs) |
| 529 | + R = Diagonal(conj.(signs)) * R |
| 530 | + return Q′, R |
| 531 | +end |
| 532 | + |
| 533 | +# TODO: Store a random matrix or seed as a parameter |
| 534 | +# of the `OpName`? |
| 535 | +function (n::OpName"RandomUnitary")(domain...) |
| 536 | + d = prod(to_dim.(domain)) |
| 537 | + elt = get(params(n), :eltype, Complex{Float64}) |
| 538 | + Q, _ = qr_positive(randn(elt, (d, d))) |
| 539 | + return Q |
| 540 | +end |
| 541 | +@op_alias "randU" "RandomUnitary" |
493 | 542 |
|
494 | 543 | # Expand the operator in a new basis. |
495 | 544 | using LinearAlgebra: ⋅ |
|
0 commit comments