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

Further tests and reading loading config checks #89

Merged
merged 20 commits into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/prepare-nightly.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
import os
import re

_PATH_INIT = os.path.join(os.getcwd(), 'c3', '__init__.py')
_PATH_SETUP = os.path.join(os.getcwd(), 'setup.py')
_PATH_INIT = os.path.join(os.getcwd(), "c3", "__init__.py")
_PATH_SETUP = os.path.join(os.getcwd(), "setup.py")

# get today date
now = datetime.datetime.now()
now_date = now.strftime("%Y%m%d")

print(f"Update init - replace version by {now_date}")
with open(_PATH_INIT, 'r') as fp:
with open(_PATH_INIT, "r") as fp:
init = fp.read()
init = re.sub(r'__version__ = [\d\.\w\'"]+', f'__version__ = "{now_date}"', init)
with open(_PATH_INIT, 'w') as fp:
with open(_PATH_INIT, "w") as fp:
fp.write(init)

print(f"Update setup - replace version by {now_date} and update to c3-toolset-nightly")
with open(_PATH_SETUP, 'r') as fp:
with open(_PATH_SETUP, "r") as fp:
setup = fp.read()
setup = re.sub(r'version=[\d\.\w\'"]+', f'version="{now_date}"', setup)
setup = re.sub(r'name="c3-toolset"', f'name="c3-toolset-nightly"', setup)
with open(_PATH_SETUP, 'w') as fp:
fp.write(setup)
setup = re.sub(r'name="c3-toolset"', 'name="c3-toolset-nightly"', setup)
with open(_PATH_SETUP, "w") as fp:
fp.write(setup)
58 changes: 56 additions & 2 deletions c3/c3objs.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ def __init__(self, name, desc="", comment="", params=None):
raise exception

def __str__(self) -> str:
return hjson.dumps(self.asdict())
return hjson.dumps(self.asdict(), default=hjson_encode)

def asdict(self) -> dict:
params = {}
for key, item in self.params.items():
params[key] = item.asdict()
return {"c3type": self.__class__.__name__, "params": params}
return {"c3type": self.__class__.__name__, "params": params, "name": self.name}


class Quantity:
Expand Down Expand Up @@ -308,3 +308,57 @@ def set_limits(self, min_val, max_val):
self.offset = np.array(min_val) * self.pref
self.scale = np.abs(np.array(max_val) - np.array(min_val)) * self.pref
self.set_value(val)


def jsonify_list(data, transform_arrays=True):
# try:
if data is None:
return
if isinstance(data, dict):
return {str(k): jsonify_list(v) for k, v in data.items()}
elif isinstance(data, list):
return [jsonify_list(v) for v in data]
elif isinstance(data, tuple):
return tuple(jsonify_list(v) for v in data)
elif isinstance(data, np.ndarray) and transform_arrays:
return data.tolist()
elif isinstance(data, ops.EagerTensor) and transform_arrays:
return data.numpy().tolist()
elif isinstance(data, C3obj) or isinstance(data, Quantity):
return data.asdict()
elif (
isinstance(data, str)
or isinstance(data, bool)
or isinstance(data, float)
or isinstance(data, int)
):
return data
else:
return data


def hjson_encode(z):
if isinstance(z, complex):
return {"__complex__": str(z)}
elif isinstance(z, np.ndarray):
return {"__array__": (z.tolist())}
elif isinstance(z, tf.Tensor) or isinstance(z, ops.EagerTensor):
return {"__array__": (z.numpy().tolist())}
elif isinstance(z, Quantity):
return {"__quantity__": z.asdict()}
elif isinstance(z, C3obj):
return z.asdict()
elif isinstance(z, dict) and np.any([not isinstance(k, str) for k in z.keys()]):
return {str(k): v for k, v in z.items()}
return z


def hjson_decode(z):
if len(z) == 1:
if z[0][0] == "__complex__":
return complex(z[0][1])
elif z[0][0] == "__array__":
return np.array(z[0][1])
elif z[0][0] == "__quantity__":
return Quantity(**z[0][1])
return dict(z)
24 changes: 20 additions & 4 deletions c3/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from typing import Dict
import time

from c3.c3objs import hjson_encode, hjson_decode
from c3.generator.generator import Generator
from c3.parametermap import ParameterMap
from c3.signal.gates import Instruction
Expand Down Expand Up @@ -94,7 +95,7 @@ def load_quick_setup(self, filepath: str) -> None:

"""
with open(filepath, "r") as cfg_file:
cfg = hjson.loads(cfg_file.read())
cfg = hjson.loads(cfg_file.read(), object_pairs_hook=hjson_decode)
self.quick_setup(cfg)

def quick_setup(self, cfg) -> None:
Expand Down Expand Up @@ -176,21 +177,30 @@ def read_config(self, filepath: str) -> None:

"""
with open(filepath, "r") as cfg_file:
cfg = hjson.loads(cfg_file.read())
cfg = hjson.loads(cfg_file.read(), object_pairs_hook=hjson_decode)
self.from_dict(cfg)

def from_dict(self, cfg: dict) -> None:
"""
Load experiment from dictionary
"""
model = Model()
model.fromdict(cfg["model"])
generator = Generator()
generator.fromdict(cfg["generator"])
pmap = ParameterMap(model=model, generator=generator)
pmap.fromdict(cfg["instructions"])
if "options" in cfg:
for k, v in cfg["options"].items():
self.__dict__[k] = v
self.pmap = pmap

def write_config(self, filepath: str) -> None:
"""
Write dictionary to a HJSON file.
"""
with open(filepath, "w") as cfg_file:
hjson.dump(self.asdict(), cfg_file)
hjson.dump(self.asdict(), cfg_file, default=hjson_encode)

def asdict(self) -> dict:
"""
Expand All @@ -202,10 +212,16 @@ def asdict(self) -> dict:
exp_dict["instructions"][name] = instr.asdict()
exp_dict["model"] = self.pmap.model.asdict()
exp_dict["generator"] = self.pmap.generator.asdict()
exp_dict["options"] = {
"propagate_batch_size": self.propagate_batch_size,
"use_control_fields": self.use_control_fields,
"overwrite_propagators": self.overwrite_propagators,
"stop_partial_propagator_gradient": self.stop_partial_propagator_gradient,
}
return exp_dict

def __str__(self) -> str:
return hjson.dumps(self.asdict())
return hjson.dumps(self.asdict(), default=hjson_encode)

def evaluate_legacy(self, sequences):
"""
Expand Down
16 changes: 11 additions & 5 deletions c3/generator/devices.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import os
import tempfile
import warnings
import hjson
from typing import Callable, Dict, Any
import tensorflow as tf
import numpy as np
from c3.signal.pulse import Envelope, Carrier
from c3.signal.gates import Instruction
from c3.c3objs import Quantity, C3obj
from c3.c3objs import Quantity, C3obj, hjson_encode
from c3.utils.tf_utils import tf_convolve

devices = dict()
Expand Down Expand Up @@ -51,7 +52,7 @@ def write_config(self, filepath: str) -> None:
Write dictionary to a HJSON file.
"""
with open(filepath, "w") as cfg_file:
hjson.dump(self.asdict(), cfg_file)
hjson.dump(self.asdict(), cfg_file, default=hjson_encode)

def asdict(self) -> Dict[str, Any]:
params = {}
Expand All @@ -66,7 +67,7 @@ def asdict(self) -> Dict[str, Any]:
}

def __str__(self) -> str:
return hjson.dumps(self.asdict())
return hjson.dumps(self.asdict(), default=hjson_encode)

def calc_slice_num(self, t_start: np.float64, t_end: np.float64) -> None:
"""
Expand Down Expand Up @@ -469,6 +470,7 @@ def frequency(self, signal: tf.float64) -> tf.constant:
return self.freq


# Depreciated use ResponseFFT instead
@dev_reg_deco
class Response(Device):
"""Make the AWG signal physical by convolution with a Gaussian to limit bandwith.
Expand All @@ -483,6 +485,9 @@ def __init__(self, **props):
super().__init__(**props)
self.inputs = props.pop("inputs", 1)
self.outputs = props.pop("outputs", 1)
warnings.warn(
"use ResponseFFT for faster signal generation instead", DeprecationWarning
)

def convolve(self, signal: list, resp_shape: list):
"""
Expand Down Expand Up @@ -895,7 +900,8 @@ def __init__(self, **props):
self.inputs = props.pop("inputs", 1)
self.outputs = props.pop("outputs", 1)
self.signal = None
self.params["noise_amp"] = props.pop("noise_amp")
if "noise_amp" in props:
self.params["noise_amp"] = props.pop("noise_amp")

def get_noise(self, sig):
noise_amp = self.params["noise_amp"].get_value()
Expand Down Expand Up @@ -1237,6 +1243,6 @@ def log_shapes(self):
signal = {}
for key in self.signal:
signal[key] = self.signal[key].numpy().tolist()
logfile.write(hjson.dumps(signal))
logfile.write(hjson.dumps(signal, default=hjson_encode))
logfile.write("\n")
logfile.flush()
7 changes: 4 additions & 3 deletions c3/generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import hjson
import numpy as np
import tensorflow as tf
from c3.c3objs import hjson_decode, hjson_encode
from c3.signal.gates import Instruction
from c3.generator.devices import devices as dev_lib

Expand Down Expand Up @@ -68,7 +69,7 @@ def read_config(self, filepath: str) -> None:

"""
with open(filepath, "r") as cfg_file:
cfg = hjson.loads(cfg_file.read())
cfg = hjson.loads(cfg_file.read(), object_pairs_hook=hjson_decode)
self.fromdict(cfg)

def fromdict(self, cfg: dict) -> None:
Expand All @@ -84,7 +85,7 @@ def write_config(self, filepath: str) -> None:
Write dictionary to a HJSON file.
"""
with open(filepath, "w") as cfg_file:
hjson.dump(self.asdict(), cfg_file)
hjson.dump(self.asdict(), cfg_file, default=hjson_encode)

def asdict(self) -> dict:
"""
Expand All @@ -96,7 +97,7 @@ def asdict(self) -> dict:
return {"Devices": devices, "Chains": self.chains}

def __str__(self) -> str:
return hjson.dumps(self.asdict())
return hjson.dumps(self.asdict(), default=hjson_encode)

def generate_signals(self, instr: Instruction) -> dict:
"""
Expand Down
44 changes: 28 additions & 16 deletions c3/libraries/chip.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Component class and subclasses for the components making up the quantum device."""
import copy
import warnings

import numpy as np
Expand Down Expand Up @@ -59,12 +60,13 @@ def get_transformed_hamiltonians(self, transform: tf.Tensor = None):

"""
if transform is None:
return self.Hs
return copy.deepcopy(self.Hs)
transformed_Hs = dict()
for key, ham in self.Hs.items():
transformed_Hs[key] = tf.matmul(
tf.matmul(transform, self.Hs["freq"], adjoint_a=True), transform
tf.matmul(transform, self.Hs[key], adjoint_a=True), transform
)
return transformed_Hs

def get_Hamiltonian(
self, signal: Union[dict, bool] = None, transform: tf.Tensor = None
Expand Down Expand Up @@ -308,14 +310,14 @@ def __init__(
comment: str = None,
hilbert_dim: int = None,
freq: Quantity = None,
anhar: Quantity = None,
phi: Quantity = None,
phi_0: Quantity = None,
gamma: Quantity = None,
d: Quantity = None,
t1: Quantity = None,
t2star: Quantity = None,
temp: Quantity = None,
anhar: Quantity = None,
params=None,
):
super().__init__(
Expand Down Expand Up @@ -466,26 +468,35 @@ def get_Lindbladian(self, dims):
return tf.cast(sum(Ls), tf.complex128)


@dev_reg_deco
class TransmonExpanded(Transmon):
def init_Hs(self, ann_oper):
def get_Hs(self, ann_oper):
ann_oper = tf.constant(ann_oper, tf.complex128)
ann_oper_dag = tf.linalg.matrix_transpose(ann_oper, conjugate=True)
adag_plus_a = ann_oper_dag + ann_oper
sq_adag_plus_a = tf.linalg.matmul(adag_plus_a, adag_plus_a)
quartic_adag_plus_a = tf.linalg.matmul(sq_adag_plus_a, sq_adag_plus_a)
sextic_adag_plus_a = tf.linalg.matmul(quartic_adag_plus_a, sq_adag_plus_a)

self.Hs["quadratic"] = tf.linalg.matmul(ann_oper_dag, ann_oper) # + 1 / 2
self.Hs["quartic"] = quartic_adag_plus_a
self.Hs["sextic"] = sextic_adag_plus_a
Hs = dict()
Hs["quadratic"] = tf.linalg.matmul(ann_oper_dag, ann_oper) # + 1 / 2
Hs["quartic"] = quartic_adag_plus_a
Hs["sextic"] = sextic_adag_plus_a
return Hs

def init_Hs(self, ann_oper):
ann_oper_loc = np.diag(np.sqrt(range(1, int(np.max(ann_oper) ** 2))), k=1)
self.Hs = self.get_Hs(ann_oper)
self.Hs_local = self.get_Hs(ann_oper_loc)

if "EC" not in self.params:
self.energies_from_frequencies()

def get_prefactors(self, sig):
EC = self.params["EC"].get_value()
EJ = self.params["EJ"].get_value() * self.get_factor(sig) ** 2
EJ = self.params["EJ"].get_value() * self.get_factor(sig) ** 2.0
prefactors = dict()
prefactors["quadratic"] = tf.math.sqrt(8 * EC * EJ)
prefactors["quadratic"] = tf.math.sqrt(8.0 * EC * EJ)
prefactors["quartic"] = -EC / 12
prefactors["sextic"] = EJ / 720 * (2 * EC / EJ) ** (3 / 2)
return prefactors
Expand All @@ -508,9 +519,9 @@ def eval_func(x):
self.params["EC"].set_opt_value(EC)
self.params["EJ"].set_opt_value(EJ)
prefactors = self.get_prefactors(-phi)
h = tf.zeros_like(self.Hs["quadratic"])
h = tf.zeros_like(self.Hs_local["quadratic"])
for k in prefactors:
h += self.Hs[k] * tf.cast(prefactors[k], tf.complex128)
h += self.Hs_local[k] * tf.cast(prefactors[k], tf.complex128)
es = tf.linalg.eigvalsh(h)
es -= es[0]
freq_diff = tf.math.abs(tf.math.real(es[1] - es[0]) - freq)
Expand All @@ -520,19 +531,20 @@ def eval_func(x):
fmin(
eval_func,
x0=[self.params["EC"].get_opt_value(), self.params["EJ"].get_opt_value()],
)
print(
(float(EC_guess), float(EJ_guess)),
(float(self.params["EC"]), float(self.params["EJ"])),
disp=False,
xtol=1e-9,
)

def get_Hamiltonian(
self, signal: Union[dict, bool] = None, transform: tf.Tensor = None
):
Hs = self.get_transformed_hamiltonians(transform)

if isinstance(signal, dict):
sig = signal["values"]
sig = tf.reshape(sig, [sig.shape[0], 1, 1])

for k in Hs:
Hs[k] = tf.expand_dims(Hs[k], 0)
else:
sig = 0
prefactors = self.get_prefactors(sig)
Expand Down
Loading