tequila-basic 1.9.10__tar.gz → 1.9.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. {tequila_basic-1.9.10/src/tequila_basic.egg-info → tequila_basic-1.9.11}/PKG-INFO +1 -1
  2. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/__init__.py +1 -0
  3. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/__init__.py +1 -1
  4. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/_gates_impl.py +1 -1
  5. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/circuit.py +3 -0
  6. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/gates.py +5 -6
  7. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/hamiltonian/paulis.py +1 -1
  8. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/hamiltonian/qubit_hamiltonian.py +4 -1
  9. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/objective/braket.py +1 -1
  10. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/optimizers/optimizer_gd.py +1 -1
  11. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/quantumchemistry/encodings.py +1 -1
  12. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/quantumchemistry/orbital_optimizer.py +12 -2
  13. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/quantumchemistry/pyscf_interface.py +117 -13
  14. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/quantumchemistry/qc_base.py +33 -7
  15. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_api.py +66 -11
  16. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_base.py +81 -3
  17. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_pyquil.py +1 -1
  18. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_qiskit.py +86 -6
  19. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/version.py +1 -1
  20. tequila_basic-1.9.11/src/tequila/wavefunction/density_matrix.py +229 -0
  21. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/wavefunction/qubit_wavefunction.py +38 -1
  22. {tequila_basic-1.9.10 → tequila_basic-1.9.11/src/tequila_basic.egg-info}/PKG-INFO +1 -1
  23. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila_basic.egg-info/SOURCES.txt +1 -0
  24. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_chemistry.py +101 -0
  25. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_simulator_backends.py +66 -7
  26. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/LICENSE +0 -0
  27. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/MANIFEST.in +0 -0
  28. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/README.md +0 -0
  29. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/requirements.txt +0 -0
  30. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/setup.cfg +0 -0
  31. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/setup.py +0 -0
  32. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/_unary_state_prep_impl.py +0 -0
  33. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/adapt/__init__.py +0 -0
  34. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/adapt/adapt.py +0 -0
  35. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/krylov/__init__.py +0 -0
  36. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/krylov/krylov.py +0 -0
  37. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/robustness/__init__.py +0 -0
  38. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/robustness/helpers.py +0 -0
  39. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/robustness/interval.py +0 -0
  40. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/apps/unary_state_prep.py +0 -0
  41. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/autograd_imports.py +0 -0
  42. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/__init__.py +0 -0
  43. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/compiler.py +0 -0
  44. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/gradient.py +0 -0
  45. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/noise.py +0 -0
  46. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/postselection.py +0 -0
  47. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/pyzx.py +0 -0
  48. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/qasm.py +0 -0
  49. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/circuit/qpic.py +0 -0
  50. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/grouping/__init__.py +0 -0
  51. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/grouping/binary_rep.py +0 -0
  52. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/grouping/binary_utils.py +0 -0
  53. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/grouping/compile_groups.py +0 -0
  54. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/grouping/ev_utils.py +0 -0
  55. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/grouping/fermionic_functions.py +0 -0
  56. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/grouping/fermionic_methods.py +0 -0
  57. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/grouping/overlapping_methods.py +0 -0
  58. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/hamiltonian/__init__.py +0 -0
  59. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/hamiltonian/paulistring.py +0 -0
  60. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/ml/__init__.py +0 -0
  61. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/ml/interface_torch.py +0 -0
  62. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/ml/ml_api.py +0 -0
  63. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/ml/utils_ml.py +0 -0
  64. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/objective/__init__.py +0 -0
  65. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/objective/objective.py +0 -0
  66. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/objective/qtensor.py +0 -0
  67. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/optimizers/__init__.py +0 -0
  68. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/optimizers/_containers.py +0 -0
  69. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/optimizers/optimizer_base.py +0 -0
  70. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/optimizers/optimizer_gpyopt.py +0 -0
  71. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/optimizers/optimizer_scipy.py +0 -0
  72. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/quantumchemistry/__init__.py +0 -0
  73. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/quantumchemistry/chemistry_tools.py +0 -0
  74. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/quantumchemistry/madness_interface.py +0 -0
  75. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/quantumchemistry/psi4_interface.py +0 -0
  76. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/__init__.py +0 -0
  77. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_aqt.py +0 -0
  78. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_cirq.py +0 -0
  79. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_cudaq.py +0 -0
  80. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_ddsim.py +0 -0
  81. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_mqp.py +0 -0
  82. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_qibo.py +0 -0
  83. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_qiskit_gpu.py +0 -0
  84. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_qlm.py +0 -0
  85. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_qulacs.py +0 -0
  86. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_qulacs_gpu.py +0 -0
  87. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_spex.py +0 -0
  88. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/simulator_symbolic.py +0 -0
  89. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/simulators/test_spex_simulator.py +0 -0
  90. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/tools/__init__.py +0 -0
  91. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/tools/convenience.py +0 -0
  92. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/tools/qng.py +0 -0
  93. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/tools/random_generators.py +0 -0
  94. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/utils/__init__.py +0 -0
  95. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/utils/bitstrings.py +0 -0
  96. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/utils/exceptions.py +0 -0
  97. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/utils/joined_transformation.py +0 -0
  98. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/utils/keymap.py +0 -0
  99. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/utils/misc.py +0 -0
  100. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila/wavefunction/__init__.py +0 -0
  101. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila_basic.egg-info/dependency_links.txt +0 -0
  102. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila_basic.egg-info/requires.txt +0 -0
  103. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/src/tequila_basic.egg-info/top_level.txt +0 -0
  104. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_TrotErr.py +0 -0
  105. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_adapt.py +0 -0
  106. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_array.py +0 -0
  107. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_binary_pauli.py +0 -0
  108. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_binary_utils.py +0 -0
  109. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_bitstrings.py +0 -0
  110. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_braket.py +0 -0
  111. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_chemistry_f12.py +0 -0
  112. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_chemistry_madness.py +0 -0
  113. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_circuit_unitary.py +0 -0
  114. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_circuits.py +0 -0
  115. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_fermionic_meas.py +0 -0
  116. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_gates.py +0 -0
  117. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_gd_optimizer.py +0 -0
  118. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_gpyopt.py +0 -0
  119. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_gradient.py +0 -0
  120. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_hamiltonian_arithmetic.py +0 -0
  121. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_krylov.py +0 -0
  122. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_mappings.py +0 -0
  123. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_noise.py +0 -0
  124. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_noise_opt.py +0 -0
  125. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_objectives.py +0 -0
  126. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_postselection.py +0 -0
  127. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_pyzx.py +0 -0
  128. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_qasm.py +0 -0
  129. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_qtensor.py +0 -0
  130. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_recompilation_routines.py +0 -0
  131. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_scipy.py +0 -0
  132. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_symbolic_simulator.py +0 -0
  133. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_torch_interface.py +0 -0
  134. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_unary_state_prep.py +0 -0
  135. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_variable.py +0 -0
  136. {tequila_basic-1.9.10 → tequila_basic-1.9.11}/tests/test_zzz_cleanup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tequila-basic
3
- Version: 1.9.10
3
+ Version: 1.9.11
4
4
  Summary: A High-Level Abstraction Framework for Quantum Algorithms
5
5
  Home-page: https://github.com/tequilahub/tequila
6
6
  Author: Tequila Developers
@@ -18,6 +18,7 @@ from tequila.optimizers import minimize, minimize_scipy, minimize_gd, optimizer_
18
18
 
19
19
  from tequila.simulators.simulator_api import (
20
20
  simulate,
21
+ simulate_density,
21
22
  compile,
22
23
  compile_to_function,
23
24
  draw,
@@ -10,7 +10,7 @@ def gem(circuits: list, H, variables: dict = None, *args, **kwargs):
10
10
  with matries Hij = <i|H|j>, Sij = <i|j> and states |i> = U[i]|00...0> generated by the circuits given
11
11
  returns the energies E and vectors C (same format as numpy.linalg.eigh)
12
12
  the total wavefunction for energy i is then:
13
- |E(i)> = \sum_k C[k,i] U[k] |0..0>
13
+ |E(i)> = \\sum_k C[k,i] U[k] |0..0>
14
14
 
15
15
  Parameters
16
16
  ----------
@@ -242,7 +242,7 @@ class DifferentiableGateImpl(ParametrizedGateImpl):
242
242
  -------
243
243
  List of Tuples: [(weight, shifted_gate), (weight, shifted_gate)]
244
244
  The gradient compiler will assemble this the following way
245
- <H>_U with U = AU(a)B --> \sum weight <H>_V , with V=A shifted_gate
245
+ <H>_U with U = AU(a)B --> \\sum weight <H>_V , with V=A shifted_gate
246
246
 
247
247
  shifted_gate can also be a whole circuit (either as QCircuit object or a list of gates)
248
248
  """
@@ -63,6 +63,9 @@ class QCircuit:
63
63
  moms = []
64
64
  moms.append(Moment())
65
65
  for g in self.gates:
66
+ if not g.qubits:
67
+ continue
68
+
66
69
  qus = g.qubits
67
70
  spots = [table[q] for q in qus]
68
71
 
@@ -89,13 +89,12 @@ def I(
89
89
  target: typing.Union[list, int], control: typing.Union[list, int] = None, power=None, angle=None, *args, **kwargs
90
90
  ) -> QCircuit:
91
91
  """
92
- This function provides a primitive implementation of the identity gate as a place
93
- holder gate with no physical effect.
94
- The user can add this gate into the circuit and get it also when
95
- calling functions regarding the circuit's stats (i.e. gates included)
92
+ A gate that has no effect, but can be used to mark a qubit as being used.
93
+ This can for example be useful if a qubit is only being used in some variants
94
+ of a circuit, but you want to prevent the output format from changing.
96
95
  """
97
- # zero generator for a no-operation gate
98
- generator = lambda q: 0
96
+ # Ugly workaround, we can't simply use paulis.Zero() because this wouldn't mark the qubit as being used.
97
+ generator = lambda q: 0 * paulis.X(q)
99
98
  return _initialize_power_gate(
100
99
  name="I", power=power, angle=angle, target=target, control=control, generator=generator, *args, **kwargs
101
100
  )
@@ -296,7 +296,7 @@ def KetBra(
296
296
  QubitWaveFunction which defines the bra element
297
297
  can also be given as string or array or integer
298
298
  hermitian: bool: (Default False)
299
- if True the hermitian version H + H^\dagger is returned
299
+ if True the hermitian version H + H^\\dagger is returned
300
300
  threshold: float: (Default 1.e-6)
301
301
  elements smaller than the threshold will be ignored
302
302
  n_qubits: only needed if ket and/or bra are passed down as integers
@@ -591,7 +591,7 @@ class QubitHamiltonian:
591
591
  Take for example X(1).
592
592
  If False the operator X(1) will get mapped to X(0)
593
593
  and the function will return the matrix for X(0)
594
- otherwise the function will return the matrix 1 \otimes X(1)
594
+ otherwise the function will return the matrix 1 \\otimes X(1)
595
595
 
596
596
  :return: np.ndarray(2**N, 2**N) with type complex
597
597
 
@@ -676,3 +676,6 @@ class QubitHamiltonian:
676
676
  if not p.is_all_z():
677
677
  return False
678
678
  return True
679
+
680
+ def constant(self):
681
+ return self._qubit_operator.constant
@@ -18,7 +18,7 @@ def Fidelity(bra, ket, *args, **kwargs):
18
18
  -----
19
19
  Initializes an ancilla free implementation using:
20
20
  |<A|B>|^2 = <A|B><B|A><0|VAUB|0><0|VBUA|0> = <P0>_{VBAU}
21
- VA = U^\dagger_A
21
+ VA = U^\\dagger_A
22
22
  P0 = |0..0><0..0|
23
23
  see: https://arxiv.org/abs/2006.03075 Eq.3-4
24
24
  or https://arxiv.org/abs/2011.05938 Eq.57
@@ -404,7 +404,7 @@ class OptimizerGD(Optimizer):
404
404
  comp = self.compile_objective(objective=objective)
405
405
  for arg in comp.args:
406
406
  if hasattr(arg, "U"):
407
- if arg.U.device is not None:
407
+ if hasattr(arg.U, "device") and arg.U.device is not None:
408
408
  # don't retrieve computer 100 times; pyquil errors out if this happens!
409
409
  self.device = arg.U.device
410
410
  break
@@ -213,7 +213,7 @@ class JordanWigner(EncodingBase):
213
213
  return openfermion.jordan_wigner(fermion_operator, *args, **kwargs)
214
214
 
215
215
  def map_state(self, state: list, *args, **kwargs):
216
- state = state + [0] * (self.n_orbitals - len(state))
216
+ state = state + [0] * (2 * self.n_orbitals - len(state))
217
217
  result = [0] * len(state)
218
218
  if self.up_then_down:
219
219
  return [state[2 * i] for i in range(self.n_orbitals)] + [state[2 * i + 1] for i in range(self.n_orbitals)]
@@ -50,6 +50,8 @@ def optimize_orbitals(
50
50
  molecule_factory=None,
51
51
  molecule_arguments=None,
52
52
  restrict_to_active_space=True,
53
+ read_chkfile: str = None,
54
+ save_chkfile: str = None,
53
55
  *args,
54
56
  **kwargs,
55
57
  ):
@@ -76,6 +78,8 @@ def optimize_orbitals(
76
78
  This initialized a random guess using numpy.random.normal(loc=X, scale=Y) with X=0.0 and Y=0.1 as defaults
77
79
  return_mcscf: return the PySCF MCSCF structure after optimization
78
80
  molecule_arguments: arguments to pass to molecule_factory or default molecule constructor | only change if you know what you are doing
81
+ read_chkfile: checkpoint file to read the initial orbitals during the CASSCF optimization. For more info see https://pyscf.org/_modules/pyscf/mcscf/chkfile.html#load_mcscf
82
+ save_chkfile: checkpoint file to save the intermediate orbitals during the CASSCF optimization.
79
83
  args: just here for convenience
80
84
  kwargs: just here for conveniece
81
85
 
@@ -85,7 +89,7 @@ def optimize_orbitals(
85
89
  """
86
90
 
87
91
  try:
88
- from pyscf import mcscf
92
+ from pyscf import mcscf, lib
89
93
  from . import QuantumChemistryPySCF
90
94
  except Exception as exception:
91
95
  raise Exception("{}\noptimize_orbitals: Need pyscf to run (pip install pyscf)".format(str(exception)))
@@ -103,6 +107,8 @@ def optimize_orbitals(
103
107
  result = OptimizeOrbitalsResult()
104
108
  mc = mcscf.CASSCF(mf, pyscf_molecule.n_orbitals, pyscf_molecule.n_electrons)
105
109
  mc.callback = result
110
+ if save_chkfile is not None:
111
+ mc.chkfile = save_chkfile
106
112
  c = pyscf_molecule.compute_constant_part()
107
113
 
108
114
  if circuit is None and vqe_solver is None:
@@ -170,10 +176,14 @@ def optimize_orbitals(
170
176
  assert initial_guess.shape[1] == no
171
177
  initial_guess = mcscf.project_init_guess(mc, initial_guess)
172
178
  mc.kernel(mo_coeff=initial_guess)
179
+ elif read_chkfile is not None:
180
+ chk = lib.chkfile.load(read_chkfile, "mcscf")
181
+ mo_coeff = chk["mo_coeff"]
182
+ ci0 = chk["ci"] if "ci" in chk.keys() else None
183
+ mc.kernel(mo_coeff=mo_coeff, ci0=ci0)
173
184
  else:
174
185
  mc.kernel()
175
186
  # make new molecule
176
-
177
187
  mo_coeff = mc.mo_coeff
178
188
  transformed_molecule = pyscf_molecule.transform_orbitals(orbital_coefficients=mo_coeff, name="optimized")
179
189
  result.molecule = transformed_molecule
@@ -1,12 +1,15 @@
1
1
  from tequila import TequilaException, QubitWaveFunction
2
2
  from tequila.quantumchemistry.qc_base import QuantumChemistryBase
3
+ from tequila.quantumchemistry.encodings import JordanWigner
3
4
  from tequila.quantumchemistry import ParametersQC, NBodyTensor
4
5
  import pyscf
6
+ from pyscf import fci
5
7
 
6
8
  from .chemistry_tools import OrbitalData
7
9
 
8
10
  import numpy
9
11
  import typing
12
+ import warnings
10
13
 
11
14
 
12
15
  def _merge_alpha_beta_strs(alpha_str, beta_str, norb):
@@ -112,27 +115,128 @@ class QuantumChemistryPySCF(QuantumChemistryBase):
112
115
 
113
116
  super().__init__(parameters=parameters, transformation=transformation, orbitals=orbitals, *args, **kwargs)
114
117
 
115
- def compute_fci(self, get_wfn=False, **kwargs):
116
- from pyscf import fci
117
-
118
+ def compute_fci(self, get_wfn=False, ci0=None, use_hcb=False, **kwargs):
118
119
  c, h1, h2 = self.get_integrals(ordering="chem")
119
120
  norb = self.n_orbitals
120
121
  nelec = self.n_electrons
121
- e, fcivec = fci.direct_spin1.kernel(h1, h2.elems, norb, nelec, **kwargs)
122
+
123
+ if ci0 is not None:
124
+ ci0 = self.create_ci_vector(ci0, use_hcb=use_hcb)
125
+
126
+ e, fcivecs = fci.direct_spin1.kernel(
127
+ h1, h2.elems, norb, nelec, max_cycle=2000, max_space=100, ci0=ci0, **kwargs
128
+ )
122
129
 
123
130
  if get_wfn:
124
- alpha_strs = fci.cistring.make_strings(range(norb), nelec // 2)
125
- beta_strs = alpha_strs.copy()
126
- wfn_dim = 2 ** (2 * norb)
127
- wfn = numpy.zeros(wfn_dim)
128
- for i, alpha_str in enumerate(alpha_strs):
129
- for j, beta_str in enumerate(beta_strs):
130
- merged_str, phase = _merge_alpha_beta_strs(alpha_str, beta_str, norb)
131
- wfn[merged_str] = phase * fcivec[i, j]
132
- return e + c, QubitWaveFunction.from_array(wfn)
131
+ if not ("nroots" in kwargs and kwargs["nroots"] > 1):
132
+ fcivecs = [fcivecs]
133
+ e = [e]
134
+ wfns = []
135
+ energies = [x + c for x in e]
136
+ for fcivec in fcivecs:
137
+ if use_hcb:
138
+ alpha_strs = fci.cistring.make_strings(range(norb), nelec // 2)
139
+ wfn_dim = 2**norb
140
+ wfn = numpy.zeros(wfn_dim)
141
+ for i, alpha_str in enumerate(alpha_strs):
142
+ alpha_str_b = bin(alpha_str)[2:].zfill(norb)[::-1]
143
+ merged_str = int(alpha_str_b, 2)
144
+ wfn[merged_str] = fcivec[i, i]
145
+ else:
146
+ alpha_strs = fci.cistring.make_strings(range(norb), nelec // 2)
147
+ beta_strs = alpha_strs.copy()
148
+ wfn_dim = 2 ** (2 * norb)
149
+ wfn = numpy.zeros(wfn_dim)
150
+ for i, alpha_str in enumerate(alpha_strs):
151
+ for j, beta_str in enumerate(beta_strs):
152
+ if not self.transformation.up_then_down:
153
+ merged_str, phase = _merge_alpha_beta_strs(alpha_str, beta_str, norb)
154
+ wfn[merged_str] = phase * fcivec[i, j]
155
+ else:
156
+ alpha_str_b = bin(alpha_str)[2:].zfill(norb)
157
+ beta_str_b = bin(beta_str)[2:].zfill(norb)
158
+ merged_str_b = (alpha_str_b + beta_str_b)[::-1]
159
+ merged_str = int(merged_str_b, 2)
160
+ wfn[merged_str] = fcivec[i, j]
161
+ wfns.append(QubitWaveFunction.from_array(wfn))
162
+ if not ("nroots" in kwargs and kwargs["nroots"] > 1):
163
+ return energies[0], wfns[0]
164
+ return energies, wfns
133
165
 
134
166
  return e + c
135
167
 
168
+ def create_ci_vector(self, wfn, use_hcb=False):
169
+ """
170
+ Reduces a full Fock space CI vector to the (na, nb) vector
171
+ required by fci.direct_spin1.
172
+
173
+ Args:
174
+ wfn (np.ndarray): 1D vector of length 2**(2*norb).
175
+
176
+ Returns:
177
+ ci_vector (np.ndarray): 2D vector of shape (na, nb).
178
+ """
179
+ if not isinstance(wfn, numpy.ndarray):
180
+ if hasattr(wfn, "to_array"):
181
+ wfn = wfn.to_array()
182
+ else:
183
+ raise TequilaException(
184
+ f"create_ci_vector received wfn object of type {type(wfn)} but needs numpy.ndarray or tq.QubitWaveFunction\nwfn={wfn}"
185
+ )
186
+
187
+ wfn = numpy.real_if_close(wfn).astype(numpy.float64)
188
+
189
+ if type(self.transformation).__name__ != "JordanWigner":
190
+ warnings.warn("!!!guess wavefunctions for pyscf need to be in JordanWigner encoding!!!")
191
+
192
+ norb = self.n_orbitals
193
+ nelec = self.n_electrons
194
+ neleca = (nelec + 1) // 2
195
+ nelecb = nelec // 2
196
+
197
+ expected_len = 2 ** (2 * norb) if not use_hcb else 2**norb
198
+ if wfn.size != expected_len:
199
+ raise ValueError(
200
+ f"Input vector has length {wfn.size}, "
201
+ f"but expected full Fock space length {expected_len} (for {norb} orbitals)."
202
+ )
203
+
204
+ na = fci.cistring.num_strings(norb, neleca)
205
+ nb = fci.cistring.num_strings(norb, nelecb)
206
+ ci_vector = numpy.zeros((na, nb), dtype=wfn.dtype)
207
+
208
+ for i in range(expected_len):
209
+ if use_hcb:
210
+ alpha_str = bin(i)[2:].zfill(norb)
211
+ beta_str = alpha_str
212
+ else:
213
+ state_binary_str = bin(i)[2:].zfill(2 * norb)
214
+ if not self.transformation.up_then_down:
215
+ alpha_str = state_binary_str[0::2]
216
+ beta_str = state_binary_str[1::2]
217
+ else:
218
+ alpha_str = state_binary_str[0 : len(state_binary_str) // 2]
219
+ beta_str = state_binary_str[len(state_binary_str) // 2 : len(state_binary_str)]
220
+
221
+ # Only keep states that are particle-number conserving
222
+ if alpha_str.count("1") == neleca and beta_str.count("1") == nelecb:
223
+ alpha_int = int(alpha_str, 2)
224
+ beta_int = int(beta_str, 2)
225
+
226
+ # Compute the phase as in _merge_alpha_beta_strs(alpha_str, beta_str, norb)
227
+ set_bits_beta = [i for i in range(norb) if (beta_int >> i) & 1]
228
+ try:
229
+ phase = (-1) ** sum([(alpha_int & 2**i - 1).bit_count() for i in set_bits_beta])
230
+ except AttributeError as error:
231
+ phase = (-1) ** sum([bin(alpha_int & 2**i - 1).count("1") for i in set_bits_beta])
232
+
233
+ coeff = wfn[i]
234
+ row_idx = fci.cistring.str2addr(norb, neleca, alpha_int)
235
+ col_idx = fci.cistring.str2addr(norb, nelecb, beta_int)
236
+ ci_vector[row_idx, col_idx] = phase * coeff
237
+
238
+ return ci_vector
239
+
136
240
  def compute_energy(self, method: str, *args, **kwargs) -> float:
137
241
  method = method.lower()
138
242
 
@@ -140,6 +140,7 @@ class QuantumChemistryBase:
140
140
  if transformation is None:
141
141
  transformation = molecule.transformation
142
142
  parameters = molecule.parameters
143
+ parameters.frozen_core = False
143
144
  return cls(
144
145
  nuclear_repulsion=c,
145
146
  one_body_integrals=h1,
@@ -238,7 +239,7 @@ class QuantumChemistryBase:
238
239
  Notes
239
240
  ----------
240
241
  Creates the transformed hermitian generator of UCC type unitaries:
241
- M(a^\dagger_{a_0} a_{i_0} a^\dagger{a_1}a_{i_1} ... - h.c.)
242
+ M(a^\\dagger_{a_0} a_{i_0} a^\\dagger{a_1}a_{i_1} ... - h.c.)
242
243
  where the qubit map M depends is self.transformation
243
244
 
244
245
  Parameters
@@ -818,6 +819,7 @@ class QuantumChemistryBase:
818
819
  frozen_orbitals=core,
819
820
  orbital_coefficients=coeff,
820
821
  overlap_integrals=s,
822
+ orbital_type="orthonormalized-{}-basis".format(self.integral_manager._basis_name),
821
823
  )
822
824
  return self
823
825
  else:
@@ -830,6 +832,7 @@ class QuantumChemistryBase:
830
832
  frozen_orbitals=core,
831
833
  orbital_coefficients=coeff,
832
834
  overlap_integrals=s,
835
+ orbital_type="orthonormalized-{}-basis".format(self.integral_manager._basis_name),
833
836
  )
834
837
  parameters = copy.deepcopy(self.parameters)
835
838
  result = QuantumChemistryBase(
@@ -914,7 +917,7 @@ class QuantumChemistryBase:
914
917
  Compute annihilation operator on spin-orbital in qubit representation
915
918
  Spin-orbital order is always (up,down,up,down,...)
916
919
  """
917
- assert orbital <= self.n_orbitals * 2
920
+ assert orbital < self.n_orbitals * 2
918
921
  aop = openfermion.ops.FermionOperator(f"{orbital}", coefficient)
919
922
  return self.transformation(aop)
920
923
 
@@ -923,7 +926,7 @@ class QuantumChemistryBase:
923
926
  Compute creation operator on spin-orbital in qubit representation
924
927
  Spin-orbital order is always (up,down,up,down,...)
925
928
  """
926
- assert orbital <= self.n_orbitals * 2
929
+ assert orbital < self.n_orbitals * 2
927
930
  cop = openfermion.ops.FermionOperator(f"{orbital}^", coefficient)
928
931
  return self.transformation(cop)
929
932
 
@@ -2190,7 +2193,7 @@ class QuantumChemistryBase:
2190
2193
  raise TequilaException("Need to specify a Quantum Circuit.")
2191
2194
 
2192
2195
  def _get_hcb_op(op_tuple):
2193
- """Build the hardcore boson operators: b^\dagger_ib_j + h.c. in qubit encoding"""
2196
+ """Build the hardcore boson operators: b^\\dagger_ib_j + h.c. in qubit encoding"""
2194
2197
  if len(op_tuple) == 2:
2195
2198
  return 2 * Sm(op_tuple[0][0]) * Sp(op_tuple[1][0])
2196
2199
  elif len(op_tuple) == 4:
@@ -2623,7 +2626,14 @@ class QuantumChemistryBase:
2623
2626
  circuit += gates.GeneralizedRotation(generator=n_down, angle=-2 * phi)
2624
2627
  return circuit
2625
2628
 
2626
- def get_givens_circuit(self, unitary, tol=1e-12, ordering=OPTIMIZED_ORDERING):
2629
+ def get_givens_circuit(
2630
+ self,
2631
+ unitary,
2632
+ tol=1e-12,
2633
+ ordering=OPTIMIZED_ORDERING,
2634
+ fix: bool = True,
2635
+ label=None,
2636
+ ) -> QCircuit:
2627
2637
  """
2628
2638
  Constructs a quantum circuit from a given real unitary matrix using Givens rotations.
2629
2639
 
@@ -2634,6 +2644,8 @@ class QuantumChemistryBase:
2634
2644
  - unitary (numpy.array): A real unitary matrix representing the transformation to implement.
2635
2645
  - tol (float): A tolerance threshold below which matrix elements are considered zero.
2636
2646
  - ordering (list of tuples or 'Optimized'): Custom ordering of indices for Givens rotations or 'Optimized' to generate them automatically.
2647
+ - fix (bool): whether to set the angle as fixed or as inial value as: angle=tq.Variable(idx) + value. Useful to let further relaxation to the basis change
2648
+ - label: can be passed instead of angle to have auto-naming with label ("R",i,j,label) useful for repreating gates with individual variables
2637
2649
 
2638
2650
  Returns:
2639
2651
  - QCircuit: A quantum circuit implementing the series of rotations decomposed from the unitary.
@@ -2646,11 +2658,25 @@ class QuantumChemistryBase:
2646
2658
 
2647
2659
  # Add all Rz (phase) rotations to the circuit.
2648
2660
  for phi in phi_list:
2649
- circuit += self.n_rotation(phi[1], phi[0])
2661
+ if fix:
2662
+ circuit += self.n_rotation(i=phi[1], phi=phi[0])
2663
+ else:
2664
+ circuit += self.n_rotation(
2665
+ i=phi[1],
2666
+ phi=phi[0] + Variable(f"Ph({phi[1]}" + ("," + str(label)) * (label is not None) + ")"),
2667
+ )
2650
2668
 
2651
2669
  # Add all Givens rotations to the circuit.
2652
2670
  for theta in reversed(theta_list):
2653
- circuit += self.UR(theta[1], theta[2], theta[0] * 2)
2671
+ if fix:
2672
+ circuit += self.UR(i=theta[1], j=theta[2], angle=theta[0] * 2)
2673
+ else:
2674
+ circuit += self.UR(
2675
+ i=theta[1],
2676
+ j=theta[2],
2677
+ angle=(theta[0] * 2)
2678
+ + Variable(f"UR({theta[1]},{theta[2]}" + ("," + str(label)) * (label is not None) + ")"),
2679
+ )
2654
2680
 
2655
2681
  return circuit
2656
2682
 
@@ -2,16 +2,16 @@ from collections import namedtuple
2
2
  import typing
3
3
  import warnings
4
4
  import numpy
5
+ from importlib.metadata import version, PackageNotFoundError
5
6
  from numbers import Real as RealNumber
6
7
  from typing import Dict, Union, Hashable
7
- import pkg_resources
8
- from pkg_resources import DistributionNotFound
9
8
  from tequila.objective import Objective, Variable, assign_variable, format_variable_dictionary, QTensor
10
9
  from tequila.utils.exceptions import TequilaException, TequilaWarning
11
10
  from tequila.simulators.simulator_base import BackendCircuit, BackendExpectationValue
12
11
  from tequila.circuit.noise import NoiseModel
13
12
  from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
14
13
  from tequila.simulators.simulator_symbolic import BackendCircuitSymbolic, BackendExpectationValueSymbolic
14
+ from tequila.wavefunction.density_matrix import DensityMatrix
15
15
 
16
16
  SUPPORTED_BACKENDS = [
17
17
  "qulacs",
@@ -30,6 +30,7 @@ SUPPORTED_BACKENDS = [
30
30
  "cudaq",
31
31
  ]
32
32
 
33
+ SUPPORTED_DENSITY_BACKENDS = ["qiskit"]
33
34
  # TODO: Reenable noise for Qiskit
34
35
  SUPPORTED_NOISE_BACKENDS = ["cirq", "pyquil"] # qulacs removed in v.1.9
35
36
 
@@ -102,7 +103,7 @@ except ImportError:
102
103
  HAS_QISKIT_NOISE = False
103
104
 
104
105
  try:
105
- pkg_resources.require("qiskit-aer-gpu")
106
+ version("qiskit-aer-gpu")
106
107
  from tequila.simulators.simulator_qiskit_gpu import BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu
107
108
 
108
109
  HAS_QISKIT_GPU = True
@@ -112,7 +113,7 @@ try:
112
113
 
113
114
  if HAS_QISKIT_GPU_NOISE:
114
115
  INSTALLED_NOISE_SAMPLERS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
115
- except (ImportError, DistributionNotFound):
116
+ except (ImportError, PackageNotFoundError):
116
117
  HAS_QISKIT_GPU = False
117
118
  HAS_QISKIT_GPU_NOISE = False
118
119
 
@@ -142,7 +143,6 @@ except ImportError:
142
143
  HAS_CIRQ = False
143
144
 
144
145
  try:
145
- pkg_resources.require("qulacs")
146
146
  import qulacs
147
147
  from tequila.simulators.simulator_qulacs import BackendCircuitQulacs, BackendExpectationValueQulacs
148
148
 
@@ -156,11 +156,10 @@ try:
156
156
  INSTALLED_NOISE_SAMPLERS["qulacs"] = BackendTypes(
157
157
  CircType=BackendCircuitQulacs, ExpValueType=BackendExpectationValueQulacs
158
158
  )
159
- except (ImportError, DistributionNotFound):
159
+ except ImportError:
160
160
  HAS_QULACS = False
161
161
 
162
162
  try:
163
- # pkg_resources.require("qulacs-gpu")
164
163
  from qulacs import QuantumStateGpu
165
164
  from tequila.simulators.simulator_qulacs_gpu import BackendCircuitQulacsGpu, BackendExpectationValueQulacsGpu
166
165
 
@@ -174,7 +173,7 @@ try:
174
173
  INSTALLED_NOISE_SAMPLERS["qulacs_gpu"] = BackendTypes(
175
174
  CircType=BackendCircuitQulacsGpu, ExpValueType=BackendExpectationValueQulacsGpu
176
175
  )
177
- except (ImportError, DistributionNotFound):
176
+ except ImportError:
178
177
  HAS_QULACS_GPU = False
179
178
 
180
179
 
@@ -234,7 +233,12 @@ def show_available_simulators():
234
233
 
235
234
 
236
235
  def pick_backend(
237
- backend: str = None, samples: int = None, noise: NoiseModel = None, device=None, exclude_symbolic: bool = True
236
+ backend: str = None,
237
+ samples: int = None,
238
+ simulate_density: bool = False,
239
+ noise: NoiseModel = None,
240
+ device=None,
241
+ exclude_symbolic: bool = True,
238
242
  ) -> str:
239
243
  """
240
244
  choose, or verify, a backend for the user.
@@ -265,6 +269,12 @@ def pick_backend(
265
269
  raise TequilaException("device use requires backend specification!")
266
270
 
267
271
  if backend is None:
272
+ if simulate_density:
273
+ for f in SUPPORTED_DENSITY_BACKENDS:
274
+ if f in INSTALLED_SIMULATORS:
275
+ return f
276
+ raise TequilaException("Density simulators unavailable!")
277
+
268
278
  if noise is None:
269
279
  if samples is None:
270
280
  for f in SUPPORTED_BACKENDS:
@@ -320,6 +330,7 @@ def compile_objective(
320
330
  variables: typing.Dict["Variable", "RealNumber"] = None,
321
331
  backend: str = None,
322
332
  samples: int = None,
333
+ simulate_density: bool = False,
323
334
  device: str = None,
324
335
  noise: NoiseModel = None,
325
336
  *args,
@@ -350,7 +361,9 @@ def compile_objective(
350
361
  the compiled objective.
351
362
  """
352
363
 
353
- backend = pick_backend(backend=backend, samples=samples, noise=noise, device=device)
364
+ backend = pick_backend(
365
+ backend=backend, samples=samples, simulate_density=simulate_density, noise=noise, device=device
366
+ )
354
367
 
355
368
  # dummy variables
356
369
  if variables is None:
@@ -405,6 +418,7 @@ def compile_circuit(
405
418
  variables: typing.Dict["Variable", "RealNumber"] = None,
406
419
  backend: str = None,
407
420
  samples: int = None,
421
+ simulate_density: bool = False,
408
422
  noise: NoiseModel = None,
409
423
  device: str = None,
410
424
  *args,
@@ -516,6 +530,7 @@ def simulate(
516
530
  compiled_objective = compile(
517
531
  objective=objective,
518
532
  samples=samples,
533
+ simulate_density=False,
519
534
  variables=variables,
520
535
  backend=backend,
521
536
  noise=noise,
@@ -527,6 +542,43 @@ def simulate(
527
542
  return compiled_objective(variables=variables, samples=samples, initial_state=initial_state, *args, **kwargs)
528
543
 
529
544
 
545
+ def simulate_density(
546
+ objective: typing.Union["Objective", "QCircuit", "QTensor"],
547
+ variables: Dict[Union[Variable, Hashable], RealNumber] = None,
548
+ backend: str = None,
549
+ noise: NoiseModel = None,
550
+ device: str = None,
551
+ *args,
552
+ **kwargs,
553
+ ) -> Union[RealNumber, DensityMatrix]:
554
+ variables = format_variable_dictionary(variables)
555
+
556
+ if variables is None and not (len(objective.extract_variables()) == 0):
557
+ raise TequilaException(
558
+ "You called simulate for a parametrized type but forgot to pass down the variables: {}".format(
559
+ objective.extract_variables()
560
+ )
561
+ )
562
+
563
+ if backend is None:
564
+ backend = "qiskit" # currently only permissible backend!
565
+ elif backend.lower() != "qiskit":
566
+ TequilaException("Density matrix simulation currently works with only qiskit backend!")
567
+
568
+ compiled_objective = compile(
569
+ objective=objective,
570
+ samples=None,
571
+ simulate_density=True,
572
+ variables=variables,
573
+ backend=backend,
574
+ noise=noise,
575
+ device=device,
576
+ *args,
577
+ **kwargs,
578
+ )
579
+ return compiled_objective(variables=variables, samples=None, simulate_density=True, *args, **kwargs)
580
+
581
+
530
582
  def draw(objective, variables=None, backend: str = None, name=None, *args, **kwargs):
531
583
  """
532
584
  Pretty output (depends on installed backends) for jupyter notebooks
@@ -625,6 +677,7 @@ def compile(
625
677
  objective: typing.Union["Objective", "QCircuit", "QTensor"],
626
678
  variables: Dict[Union["Variable", Hashable], RealNumber] = None,
627
679
  samples: int = None,
680
+ simulate_density: bool = False,
628
681
  backend: str = None,
629
682
  noise: NoiseModel = None,
630
683
  device: str = None,
@@ -655,7 +708,9 @@ def compile(
655
708
 
656
709
  """
657
710
 
658
- backend = pick_backend(backend=backend, noise=noise, samples=samples, device=device)
711
+ backend = pick_backend(
712
+ backend=backend, noise=noise, samples=samples, simulate_density=simulate_density, device=device
713
+ )
659
714
 
660
715
  if variables is not None:
661
716
  # allow hashable types as keys without casting it to variables