Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Adding support for FreeParameterExpression #85

Merged
merged 18 commits into from
Aug 28, 2024

Conversation

Fe-r-oz
Copy link

@Fe-r-oz Fe-r-oz commented Jun 5, 2024

Resolving Issue #82

Description of changes: Replicate the Python SDK support for FreeParameterExpression, allowing gates like Rx( 2 * alpha /3), thus creating FreeParameterExpression

Note: New to git secrets, so bear with me please!

Testing done:

julia> using Test

julia> α = FreeParameter(:alpha)
alpha

julia> θ = FreeParameter(:theta)
theta

julia> gate = FreeParameterExpression("α + 2*θ")
α + 2θ

julia> circ = Circuit()
T : |Result Types|
                  
T : |Result Types|


julia> circ = H(circ, 0)
T  : |0|Result Types|
                     
q0 : -H--------------
                     
T  : |0|Result Types|


julia> circ = Rx(circ, 1, gate)
T  : |    0     |Result Types|
                              
q0 : -H-----------------------
                              
q1 : -Rx(α + 2θ)--------------
                              
T  : |    0     |Result Types|


julia> circ = Ry(circ, 0, θ)
T  : |    0     |    1    |Result Types|
                                        
q0 : -H----------Ry(theta)--------------
                                        
q1 : -Rx(α + 2θ)------------------------
                                        
T  : |    0     |    1    |Result Types|

Unassigned parameters: theta


julia> circ = Ry(circ, 0, θ)
T  : |    0     |    1    |    2    |Result Types|
                                                  
q0 : -H----------Ry(theta)-Ry(theta)--------------
                                                  
q1 : -Rx(α + 2θ)----------------------------------
                                                  
T  : |    0     |    1    |    2    |Result Types|

Unassigned parameters: theta


julia> circ = Probability(circ)
         #substitution
T  : |    0     |    1    |    2    |Result Types|
                                                  
q0 : -H----------Ry(theta)-Ry(theta)-Probability--
                                     |            
q1 : -Rx(α + 2θ)---------------------Probability--
                                                  
T  : |    0     |    1    |    2    |Result Types|

Unassigned parameters: theta


julia> d = FreeParameterExpression("2*α + 3*θ")
2α + 3θ

julia> e = subs(d, Dict(:α => 1.0, :θ => 2.0))

┌ Warning: Variable{T}(name, idx...) is deprecated, use variable(name, idx...; T=T)
│   caller = Variable at variable.jl:663 [inlined]
└ @ Core ~/.julia/packages/Symbolics/Eas9m/src/variable.jl:663
┌ Warning: Variable{T}(name, idx...) is deprecated, use variable(name, idx...; T=T)
│   caller = Variable at variable.jl:663 [inlined]
└ @ Core ~/.julia/packages/Symbolics/Eas9m/src/variable.jl:663
8.0

julia> @test e == 8.0
Test Passed

Local Testing

Test Summary: | Pass  Total   Time
AHS           | 1051   1051  50.0s
Test Summary: | Pass  Total  Time
utils         |   13     13  3.6s
Test Summary:      | Pass  Total  Time
QubitSet and Qubit |   14     14  0.3s
Test Summary:       | Pass  Total  Time
Dwave Device Schema |   15     15  2.3s
Test Summary:      | Pass  Total  Time
Ionq Device Schema |    4      4  0.9s
Test Summary:  | Pass  Total  Time
Rigetti device |    4      4  1.1s
Test Summary:               | Pass  Total  Time
Gate model simulator device |    3      3  0.2s
Test Summary:     | Pass  Total  Time
OQC device schema |    4      4  1.1s
Test Summary: | Pass  Total  Time
Quera device  |    2      2  0.6s
Test Summary: | Pass  Total  Time
Xanadu device |    3      3  0.7s
Test Summary:  | Pass  Total   Time
IR Translation |  132    132  20.5s
Test Summary: | Pass  Total     Time
Schemas       |  219    219  2m23.7s
Test Summary: | Pass  Total  Time
Devices       |   54     54  6.1s
Test Summary: | Pass  Total   Time
Circuit       |  284    284  33.4s
Test Summary:    | Pass  Total  Time
Measure operator |    7      7  0.0s
Test Summary:   | Pass  Total  Time
Free parameters |    6      6  1.1s
Test Summary:              | Pass  Total  Time
Free parameter expressions |    3      3  2.2s
Test Summary: | Pass  Total   Time
Gates         |  349    349  11.1s
Test Summary: | Pass  Total  Time
Observables   |  250    250  5.5s
Test Summary: | Pass  Total  Time
Noises        |   62     62  1.9s
Test Summary: | Pass  Total  Time
Noise models  |   90     90  4.1s
Test Summary:              | Pass  Total  Time
Compiler directives basics |    8      8  0.1s
Test Summary:              | Pass  Total   Time
GateModelQuantumTaskResult |   11     11  19.2s
Test Summary:               | Pass  Total  Time
shots>0 results computation |   11     11  3.9s
Test Summary:             | Pass  Total  Time
PhotonicQuantumTaskResult |    5      5  5.8s
Test Summary:              | Pass  Broken  Total   Time
AnnealingQuantumTaskResult |   13       1     14  20.8s
Test Summary: | Pass  Total   Time
Tasks         |  128    128  10.8s
Test Summary: | Pass  Total  Time
Batched tasks |   42     42  4.4s
Test Summary: | Pass  Total  Time
Local Jobs    |  101    101  9.1s
Test Summary: | Pass  Total  Time
Job macro     |    4      4  6.1s

Test Summary: | Pass  Total     Time
Jobs          |   55     55  1m44.8s
     Testing Braket tests passed 

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

Tests

  • [] I have installed and run git secrets to make sure I did not commit any sensitive information (passwords or credentials) (not sure, I created gpg key and tried to initialize git-secret)
  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
    By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@Fe-r-oz
Copy link
Author

Fe-r-oz commented Jun 8, 2024

Hi, @kshyatt-aws, I hope that we can complete the PRs and close the issue before the deadline! Thanks!

I have also added a subs function for FreeParameterExpression so that we can substitue values. Also, use Symbolics.simplify show that we can simplify the expression! Looking forward to the review!

@Fe-r-oz
Copy link
Author

Fe-r-oz commented Jun 8, 2024

Please checkout the subs of FreeParameterExpression in action!

julia> α = FreeParameter(:alpha)
alpha

julia> θ = FreeParameter(:theta)
theta

julia> gate = FreeParameterExpression("α + 2*θ")
α + 2θ

julia> gsub = subs(gate, Dict(:α => 2.0, :θ => 2.0))
6.0

julia> circ = Circuit()
T : |Result Types|
                  
T : |Result Types|


julia> circ = H(circ, 0)
T  : |0|Result Types|
                     
q0 : -H--------------
                     
T  : |0|Result Types|


julia> circ = Rx(circ, 1, gsub)
T  : |   0   |Result Types|
                           
q0 : -H--------------------
                           
q1 : -Rx(6.0)--------------
                           
T  : |   0   |Result Types|


julia> circ = Ry(circ, 0, θ)
T  : |   0   |    1    |Result Types|
                                     
q0 : -H-------Ry(theta)--------------
                                     
q1 : -Rx(6.0)------------------------
                                     
T  : |   0   |    1    |Result Types|

Unassigned parameters: theta


julia> circ = Probability(circ)
T  : |   0   |    1    |Result Types|
                                     
q0 : -H-------Ry(theta)-Probability--
                        |            
q1 : -Rx(6.0)-----------Probability--
                                     
T  : |   0   |    1    |Result Types|

Unassigned parameters: theta


julia> new_circ = circ(6.0)
T  : |   0   |   1   |Result Types|
                                   
q0 : -H-------Ry(6.0)-Probability--
                      |            
q1 : -Rx(6.0)---------Probability--
                                   
T  : |   0   |   1   |Result Types|


julia> non_para_circ = Circuit() |> (ci->H(ci, 0)) |> (ci->Rx(ci, 1, gsub)) |> (ci->Ry(ci, 0, 6.0)) |> Probability
T  : |   0   |   1   |Result Types|
                                   
q0 : -H-------Ry(6.0)-Probability--
                      |            
q1 : -Rx(6.0)---------Probability--
                                   
T  : |   0   |   1   |Result Types|

src/Braket.jl Outdated Show resolved Hide resolved
src/Braket.jl Outdated Show resolved Hide resolved
src/Braket.jl Outdated Show resolved Hide resolved
@rmshaffer rmshaffer linked an issue Jun 10, 2024 that may be closed by this pull request
@Fe-r-oz Fe-r-oz requested a review from kshyatt-aws June 10, 2024 16:30
@Fe-r-oz
Copy link
Author

Fe-r-oz commented Jun 10, 2024

Thanks a lot for your comments! Tested locally as well!

@Fe-r-oz
Copy link
Author

Fe-r-oz commented Jun 11, 2024

Hi, @kshyatt-aws, Kindly Please check out the changes here as well! I hope you like it!

@Fe-r-oz
Copy link
Author

Fe-r-oz commented Jun 11, 2024

Hi, @kshyatt-aws, A small reminder to please close the FreeParameterExpression issue by 12th! Thank you for your time today!

@Fe-r-oz Fe-r-oz changed the title Feature: Adding support for FreeParameterExpression, resolving Issue #82 feature: Adding support for FreeParameterExpression Jun 14, 2024
src/Braket.jl Outdated
FreeParameterExpression
FreeParameterExpression(expr::Union{FreeParameterExpression, Number, Symbolics.Num, String})

Struct representing a Free Parameter expression, which can be used in symbolic computations.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FreeParameter here should get a doc link

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might need to add this docstring to the relevant docs/src file explicitly

src/Braket.jl Outdated
FreeParameterExpression(expr) = throw(ArgumentError("Unsupported expression type"))

function FreeParameterExpression(expr::String)
parsed_expr = parse_expr_to_symbolic(Meta.parse(expr), @__MODULE__)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very suspect to me... is there a less sketchy way to accomplish this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally directly calling Meta.parse can be pretty risky!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your concern. In order to alleviate it, Added a function validate_expr that checks for each char in string and make sure that error is thrown for suspect and unknown characters. This approach ensures that input expressions are strictly validated before parsing with Meta.parse.

Tried a bunch of different ways without Meta.parse. , but other approaches were errorsome. Checked out Link: https://github.com/JuliaSymbolics/Symbolics.jl/blob/master/src/parsing.jl and ExprTools as well.

# Function to validate the input expression string
function validate_expr(expr::String)
    allowed_chars = Set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-*/^()αβγδεζηθικλμνξοπρςστυφχψω ")
    for char in expr
        if !(char in allowed_chars)
            throw(ArgumentError("Unsupported character '$char' in expression. Only ASCII letters (a-z, A-Z), Greek letters (α-ω), digits (0-9), space( ), and basic mathematical symbols (+ - * / ^ ()) are allowed."))
        end
    end
    return expr
end

# Function to create FreeParameterExpression from a validated string
function FreeParameterExpression(expr::String)
    validated_expr = validate_expr(expr)
    parsed_expr = parse_expr_to_symbolic(Meta.parse(validated_expr), @__MODULE__)
    return FreeParameterExpression(parsed_expr)
end

function Base.:!=(fpe1::FreeParameterExpression, fpe2::FreeParameterExpression)
return !(isequal(fpe1.expression, fpe2.expression))
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could slim all these down by making them one liners like:

Base.:^(fpe1::Union{Number, Symbolics.Num}, fpe2::FreeParameterExpression) = FreeParameterExpression(fpe1 ^ fpe2.expression)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was giving error

Base.:^(fpe1::Union{Number, Symbolics.Num}, fpe2::FreeParameterExpression) = FreeParameterExpression(fpe1 ^ fpe2.expression)

julia> result = fpe1 ^ fpe2
ERROR: MethodError: no method matching ^(::FreeParameterExpression, ::FreeParameterExpression)

Closest candidates are:
  ^(::Number, ::FreeParameterExpression)
   @ Braket ~/Desktop/New/braket/2/Braket.jl/src/Braket.jl:211
  ^(::SymbolicUtils.Symbolic{<:Number}, ::Any)
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/qyMYa/src/types.jl:1179

We can slim it down like this:


Base.:+(fpe1::FreeParameterExpression, fpe2::Union{FreeParameterExpression, Number, Symbolics.Num}) = 
    FreeParameterExpression((fpe1 isa FreeParameterExpression ? fpe1.expression : fpe1) + 
                            (fpe2 isa FreeParameterExpression ? fpe2.expression : fpe2))

gsub = subs(gate, Dict(:α => 2.0, :θ => 2.0))
circ = Circuit()
circ = H(circ, 0)
circ = Rx(circ, 1, gsub)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But here gsub is still the FPE with the values subbed in. What happens if I do:

circ = Rx(circ, 1, gate)

?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will show the FPE before evaluating it via substitute at the backend wrapped as subs:

julia> circ = Circuit()

T : |Result Types|
                
T : |Result Types|

julia> circ = H(circ, 0)
T  : |0|Result Types|
                     
q0 : -H--------------
                     
T  : |0|Result Types|


julia> circ = Rx(circ, 1, gate)
T  : |    0     |Result Types|
                              
q0 : -H-----------------------
                              
q1 : -Rx(α + 2θ)--------------
                              
T  : |    0     |Result Types|


@Fe-r-oz Fe-r-oz requested a review from kshyatt-aws June 19, 2024 05:58
@kshyatt-aws
Copy link
Member

OK, this looks good to let CI run! If it doesn't pass, that's ok, I can merge into a feature branch and the contribution will still count for UnitaryHack.

@Fe-r-oz
Copy link
Author

Fe-r-oz commented Jun 19, 2024

I think approving the PR can also work since UnitaryHack website page for Maintainers says PR merged (or approved).

This error was not occuring locally!

ERROR: LoadError: Unsatisfiable requirements detected for package Symbolics [0c5d862f]:
 Symbolics [0c5d862f] log:
 ├─possible versions are: 0.1.0-5.30.3 or uninstalled
 ├─restricted to versions 5.28.0-5 by Braket [19504a0f], leaving only versions: 5.28.0-5.30.3
 │ └─Braket [19[50](https://github.com/amazon-braket/Braket.jl/actions/runs/9577390287/job/26431269558?pr=85#step:4:53)4a0f] log:
 │   ├─possible versions are: 0.9.0 or uninstalled
 │   └─Braket [19504a0f] is fixed to version 0.9.0
 └─restricted by julia compatibility requirements to versions: 0.1.0-5.14.1 or uninstalled — no versions left
  1. docs/circuits.md has FreeParameters, maybe better to write them as Braket.FreeParameter and Braket.FreeParameterExpression
Error: Cannot resolve @ref for md"[`FreeParameters`](@ref)" in src/circuits.md.
- No docstring found in doc for binding `Braket.FreeParameters`.
- No docstring found in doc for binding `Main.FreeParameters`.
CheckDocument: running document checks.

Anyhow, Thank you for the round of reviews! It's been fun working for the two packages!

@kshyatt-aws
Copy link
Member

Were you running the whole workflow locally? You can try to do so with https://github.com/nektos/act to test the full CI pipeline.

@Fe-r-oz
Copy link
Author

Fe-r-oz commented Jun 20, 2024

Please run this CI, I fixed the compatibility issue with Symbolics! I hope the CI passes now!

Project.toml Outdated
@@ -64,6 +66,8 @@ SparseArrays = "1.6"
StaticArrays = "=1.9.3"
Statistics = "1.6"
StructTypes = "=1.10.0"
Symbolics = "=5.30.3"
SymbolicUtils = "2.0.2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might need an = here too

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright. I will do it like SymbolicUtils = "=2.0.2"

@kshyatt-aws kshyatt-aws changed the base branch from main to ksh/feature_bump August 28, 2024 17:02
@kshyatt-aws kshyatt-aws merged commit 608532d into amazon-braket:ksh/feature_bump Aug 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for symbolic expressions in FreeParameters
2 participants