Open wwitzel opened 10 months ago
Comment for Testing purposes.
I see your comment. Your test worked, Sudhindu. 😊
Test comment.
Note that I modified steps 1 and 2 above, @sudhindu. In the previous instructions, I wasn't appreciating the subtle distinction between a Bra and the Qmult of a Bra (they are logically equal to each other but different expressions). I hope the instructions are clear. I did test it out, so it should work if I've managed to explain it correctly. You'll have to undo some of the changes you made before, but it shouldn't be too bad. Good luck and best wishes.
And here's a bonus as step 5.
As step 6, I suggest that you add "no_cloning" as a theorem in proveit.physics.quantum.algebra. Go to the proveit.physics.quantum.algebra theorems page and insert a cell for specifying the "no_cloning" between %begin and %end. This step is just to formulate the expression for the "nocloning", not to prove it just yet. I'm thinking this expression would look something like forall{N in NaturalPos} notexists{U in Unitary(N)} forall{a, b | |a> in C^N, |b> in C^N} ... Think carefully about how this should be stated as a formal expression.
As a next step for the "no_cloning", you may want to add the following to the quantum.algebra package: adjoint_binarydistribution = ∀{H, X, Y ∈c HilbertSpaces} ∀{A, B | [A] ∈ LinMap(X, Y), [B] ∈ LinMap(H, X)} Adj(Qmult(A, B)) = Qmult(Adj(B), Adj(A))
Just copy qmult_op_op for everything except the equality instance expression.
If you are implementing the Wootters and Zurek proof of the no-cloning theorem, this step may not be necessary. Feel free to skip it if you don't need it. But if you do not it, the following describes how to implement a method for applying this theorem.
To apply this theorem, you could make a "@equality_prover('distributed', 'distribute')" method in the Adj class called "distribution". The Adj class is in the linear_algebra package (under inner_products). The theorem we want to apply is a Qmult theorem. We don't want the linear_algebra package to refer to the quantum_algebra package because linear algebra should be independent of anything quantum. But we can exploit Python's duck-typing flexibility -- we don't need to know what class something is to call one of its methods. In the Adj.distribution method we can check to see if the operand has a method called "adjoint_distribution" via hasattr. If it does, have it return "self.operand.adjoint_distribution()". Then make an "@equality_prover('adjoint_distributed', 'adjoint_distribute')" method in the Qmult class called "adjoint_distribution" that does the actual instantiation of adjoint_binary_distribution. I think you can use "containing_hilbert_space_linmap_sets" to get the H, X, Y values for instantiation (from the "to_vspace" and "from_vspace" attributes); you can look at examples in qmult.py for how to do this (search form "containing_hilbert_space_linmap_sets").
Another step that we discussed when we met last is a quantum algebra version of tensor_prod_of_linear_maps (which is a theorem in proveit.linear_algebra.tensors). The quantum algebra theorem form would use Qmult in place of Function: "Qmult(TensorProd(A_1_to_n), TensorProd(v_1_to_n))" instead of "Function(TensorProd(A_1_to_n), TensorProd(v_1_to_n))" and "Qmult(A_i, v_i)" instead of "Function(A_i, v_i)". As an exercise, you may want to prove this quantum algebra form from the linear_algebra using qmult_op_is_linmap and qmult_op_ket (I think).
We'll want an equality_prover method to apply this theorem. I'm not sure what a good name might be. I kind of think of it as a kind of distribution, but they may not be an appropriate term for this way of pairing up tensor product operands. I'll leave this name (along with the different tense forms for the @equality_prover) up for debate. As for its implementation, TensorProd.compute_norm might be a reasonably good example to follow and adapt. It's an example of dealing with any number of operands. In this case, we have two operands, but they both have some arbitrary number of TensorProd operands. To instantiate the theorem, you need get this 'n' by calling num_elements() on one of the two TensorProd operands. You'd get V1, ..., Vn from VecSpaces.known_vec_spaces, and you'd get W1, ..., Wn from "containing_hilbert_space_linmap_sets", the "to_vspace" attribute in particular. You can look at examples in qmult.py for how to use "containing_hilbert_space_linmap_sets".
This is going to be a little more challenging than previous exercises. Dealing with arbitrary numbers of operands takes some practice and there are some nuances. I expect there to be questions and roadblocks. Try not to get discourages. We are happy to help and understand that this takes practice.
@wwitzel thanks for your suggestions, I will do it.
When we met last @sudhindu and Akshaya were noting an error occuring when running their demonstrations notebook in the quantum algebra package. The error was in proving a certain Qmult is logically in the QmultCodomain class (note that class membership is more general than set membership -- all mathematical objects in a class share certain properties but you can't always form subclasses in the same way that you can form subsets). We dug into what was happening into the failing call to InClass.conclude and compared it to the old version that succeeds. What we found was that InClass.conclude "tries" Relation.conclude and if that fails with a ProofFailure then it moves on to try "self.membership_object.conclude()" which succeeds. In @sudhindu's version, however, the Relation.conclude method fails with a different error than a ProofFailure. There are two ways we can fix this. One way is to implement "_readily_provable" in the QmultCodomainMembership class (Python 'class', not logical 'class') in qmult.py. The other is to make sure that the Relation.conclude method fails with a ProofFailure. The former has the benefit that it won't bother trying Relation.conclude to prove such things in the first place. But it wouldn't be so easy to implement this. One would have to go through all of the cases that are handled in QmultCodomainMembership.conclude and mirror them in QmultCodomainMembership._readily_provable and return True for all of the cases that would succeed when calling that conclude method (without actually executing the instantiation of a Prove-It judgment). It may not be terrible but would require some thought. Perhaps I'll do it in the near future, but it may not be this weekend. The latter fix should be simple and is what I recommend for @sudhindu. The reason Relation.conclude is failing with something other than ProofFailure is because linmap_reductionin qmult.py returns None in certain instances when it isn't applicable. Let's do this:
@wwitzel I followed your instructions as you directed. I inserted the "raise ValueError("%s does not evaluate to a linear map" % self)" statement at the end of the Qmult.linmap_reduction method. The linmap_reduction() method is not directly called from Qmult.shallow_simplification(). Instead, within the shallow_simplification() method, the projection() method is invoked, and within the projection() method, the linmap_reduction() is implemented. Consequently, I enclosed the exception handler in the Qmult.shallow_simplification() method, specifically where the projection() method is called [at line number 159 of Qmult.py]. Despite these changes, I am still encountering an error, and the error message reads: "ValueError: [|varphi〉] does not evaluate to a linear map.
Make enhancements and fixes to the quantum algebra package, including the following as a first batch of updates:
a) Replace bra_def with this form (and move the 'different conventions' comment to above 'bra_def') EXCEPT remove the Qmult(..) wrapper around Bra(varphi). b) KEEP qmult_of_bra_as_map in the quantum algebra theorems.
c) RESTORE occurrences of 'qmult_of_bra_as_map' (e.g., in qmult.py).
In the process of proving that 'self' is in QmultCodomain,
d) Execute "linmap_eq = Qmult(M).linmap_reduction()" e) import and instantiate 'qmult_op_ket ' with {A:Qmult(<φ|), Hspace: known Hilbert space of |psi>, X: Complex, var_ket_psi: |ψ⟩}. Assign to 'qmult_eq'. NOTE CHANGE: where 'Qmult' now appears. f) return linmap_eq.sub_right_side_into(qmult_eq.inner_expr().rhs.operator) g) NOTE CHANGE: no bra_def instantiation. Test on the demonstrations page.