Skip to content
Draft
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
5 changes: 5 additions & 0 deletions docs/source/run_card.rst
Original file line number Diff line number Diff line change
Expand Up @@ -589,3 +589,8 @@ function. The activations are ``relu``, ``leaky_relu``, ``elu``, ``gelu``, ``sig

``cwnet_hidden_dim`` (``"auto"``), ``cwnet_layers`` (``3``), ``cwnet_activation`` (``"leaky_relu"``)
Size and activation of the channel weight network.

``nn_float_precision`` (``"float64"``)
Floating-point precision of the weights and the evaluation of all networks,
``float32`` or ``float64``. The rest of the computation, including the network
outputs, softmaxes and Jacobians, stays in double precision.
9 changes: 9 additions & 0 deletions madgraph/iolibs/template_files/mg7/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3056,6 +3056,7 @@ def build_madnis(self, phasespace: PhaseSpace) -> PhaseSpace:
subnet_layers=self.madnis_settings["flow_layers"],
subnet_activation=self.activation(madnis_args["flow_activation"]),
invert_spline=madnis_args["flow_invert_spline"],
subnet_precision=self.nn_precision(),
)
if channel.adaptive_mapping is None:
flow.initialize_globals(self.process.contexts[0], seed)
Expand All @@ -3078,6 +3079,7 @@ def build_madnis(self, phasespace: PhaseSpace) -> PhaseSpace:
subnet_hidden_dim=self.madnis_settings["discrete_hidden_dim"],
subnet_layers=madnis_args["discrete_layers"],
subnet_activation=self.activation(madnis_args["discrete_activation"]),
subnet_precision=self.nn_precision(),
)
discrete_sym.initialize_globals(self.process.contexts[0], seed)
cond_dim += perm_count
Expand All @@ -3092,6 +3094,7 @@ def build_madnis(self, phasespace: PhaseSpace) -> PhaseSpace:
subnet_hidden_dim=self.madnis_settings["discrete_hidden_dim"],
subnet_layers=madnis_args["discrete_layers"],
subnet_activation=self.activation(madnis_args["discrete_activation"]),
subnet_precision=self.nn_precision(),
)
discrete_flavor.initialize_globals(self.process.contexts[0], seed)

Expand Down Expand Up @@ -3172,6 +3175,7 @@ def build_cwnet(self, channel_count: int) -> ms.ChannelWeightNetwork:
layers=madnis_args["cwnet_layers"],
activation=self.activation(madnis_args["cwnet_activation"]),
prefix=f"subproc{self.subproc_id}.cwnet",
precision=self.nn_precision(),
)
cwnet.initialize_globals(
self.process.contexts[0], self.process.run_seed
Expand All @@ -3189,6 +3193,11 @@ def t_channel_mode(self, name: str) -> ms.PhaseSpaceMapping.TChannelMode:
else:
raise ValueError(f"Invalid t-channel mode '{name}'")

def nn_precision(self) -> ms.DataType:
# one precision for all networks: the optimizer trains all of them together
# the run card only allows float32 and float64
return getattr(ms.DataType, self.process.run_card["madnis"]["nn_float_precision"])

def activation(self, name: str) -> ms.MLP.Activation:
activations = {
"relu": ms.MLP.relu,
Expand Down
3 changes: 3 additions & 0 deletions madgraph/iolibs/template_files/mg7/run_card.toml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ cwnet_hidden_dim = %(madnis.cwnet_hidden_dim)s
cwnet_layers = %(madnis.cwnet_layers)s
cwnet_activation = %(madnis.cwnet_activation)s # options: relu, leaky_relu, elu, gelu, sigmoid, softplus

# precision of all networks
nn_float_precision = %(madnis.nn_float_precision)s # options: float32, float64

# training parameters
train_batches = %(madnis.train_batches)s
log_interval = %(madnis.log_interval)s
Expand Down
3 changes: 3 additions & 0 deletions madgraph/various/banner.py
Original file line number Diff line number Diff line change
Expand Up @@ -6939,6 +6939,9 @@ def default_setup(self):
self.add_toml_param('madnis', 'cwnet_activation', "leaky_relu",
allowed=['relu', 'leaky_relu', 'elu', 'gelu', 'sigmoid', 'softplus'],
comment="activation function of the channel weight network")
self.add_toml_param('madnis', 'nn_float_precision', "float64",
allowed=['float32', 'float64'],
comment="floating-point precision of the weights and the evaluation of all networks")
self.add_toml_param('madnis', 'train_batches', 1000,
comment="number of training batches")
self.add_toml_param('madnis', 'log_interval', 100,
Expand Down
2 changes: 1 addition & 1 deletion madspace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ if(ENABLE_OPENBLAS)
-DCMAKE_INSTALL_LIBDIR=lib
-DBUILD_SHARED_LIBS=OFF
-DBUILD_WITHOUT_LAPACK=ON
-DBUILD_SINGLE=OFF
-DBUILD_SINGLE=ON
-DBUILD_DOUBLE=ON
-DBUILD_COMPLEX=OFF
-DBUILD_COMPLEX16=OFF
Expand Down
124 changes: 99 additions & 25 deletions madspace/generate_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def main():
commands = {}
for sec in data:
commands.update({key: value for key, value in sec.items() if key != "title"})
commands = expand_precisions(commands)
for i, cmd in enumerate(commands.values()):
cmd["opcode"] = i
sections = [
Expand All @@ -45,6 +46,51 @@ def main():
kernel_checks(commands, out_dir)


# C++ element type and kernel policy suffix for each float precision
PRECISIONS = {"float32": ("float", "32"), "float64": ("double", "")}


def expand_precisions(commands):
"""Split each instruction with a `precision` list into one instruction per
precision, named `<name>_<precision>`. In these, `float` in a type means the
instruction's precision. The expanded entries keep the original name in
`base_name` and the kernel policy suffix in `types_suffix`."""
expanded = {}
for name, cmd in commands.items():
precisions = cmd.get("precision")
if precisions is None:
cmd["base_name"] = name
cmd["types_suffix"] = ""
cmd["dispatch"] = None
expanded[name] = cmd
continue
for precision in precisions:
ctype, suffix = PRECISIONS[precision]
sub_cmd = dict(cmd)
for key in ("inputs", "outputs"):
if sub_cmd[key] == "any":
raise ValueError(f"{name}: precision needs explicit {key}")
sub_cmd[key] = [
{
**arg,
"type": (
[precision, *arg["type"][1:]]
if arg.get("type", [None])[0] == "float"
else arg.get("type")
),
}
for arg in cmd[key]
]
sub_cmd["base_name"] = name
sub_cmd["types_suffix"] = suffix
sub_cmd["ctype"] = ctype
sub_cmd["doc_cmd"] = cmd
# the builder method is generated once, for the first precision
sub_cmd["dispatch"] = precisions if precision == precisions[0] else []
expanded[f"{name}_{precision}"] = sub_cmd
return expanded


def write_autogen(f):
f.write(
"// This file was automatically generated based on instruction_set.yaml\n"
Expand Down Expand Up @@ -118,14 +164,23 @@ def function_builder_mixin(commands, out_dir):
write_autogen(f)
first = True
for name, cmd in commands.items():
if cmd["dispatch"] == []:
continue
if first:
first = False
else:
f.write("\n")
f.write(doc_comment(name, cmd))
base_name = cmd["base_name"]
f.write(doc_comment(base_name, cmd.get("doc_cmd", cmd)))
if cmd["inputs"] == "any":
parameters = "ValueVec args"
instruction_call = f'instruction("{name}", args)'
elif cmd["dispatch"]:
parameters = ", ".join(f"Value {arg['name']}" for arg in cmd["inputs"])
arguments = ", ".join(arg["name"] for arg in cmd["inputs"])
instruction_call = (
f'precision_instruction("{base_name}", {{{arguments}}})'
)
else:
parameters = ", ".join(f"Value {arg['name']}" for arg in cmd["inputs"])
arguments = ", ".join(arg["name"] for arg in cmd["inputs"])
Expand All @@ -152,7 +207,7 @@ def function_builder_mixin(commands, out_dir):
f" return {{{return_array}}};"
)

f.write(f"{return_type} {name}({parameters}) {{\n{func_body}\n}}\n")
f.write(f"{return_type} {base_name}({parameters}) {{\n{func_body}\n}}\n")


def instruction_set_python(commands, out_dir):
Expand All @@ -170,6 +225,9 @@ def instruction_set_python(commands, out_dir):
)

for name, cmd in commands.items():
if cmd["dispatch"] == []:
continue
name = cmd["base_name"]
f.write(f' fb.def("{name}", &FunctionBuilder::{name}')
if cmd["inputs"] == "any":
f.write(', py::arg("args")')
Expand All @@ -181,12 +239,16 @@ def instruction_set_python(commands, out_dir):
f.write("}\n}\n")


# yaml shorthands for the default element types
DTYPE_NAMES = {"float": "float64", "int": "int32"}


def format_type(data):
type = data["type"]
if type[0] == "size":
return f'{{DataType::dt_int, true, {{"{type[1]}"}}, true}}'
return f'{{DataType::dt_int32, true, {{"{type[1]}"}}, true}}'

dtype = f"DataType::dt_{type[0]}"
dtype = f"DataType::dt_{DTYPE_NAMES.get(type[0], type[0])}"
single = len(type) > 1 and type[1] == "single"
single_str = "true" if single else "false"
shape = ", ".join(
Expand Down Expand Up @@ -255,22 +317,28 @@ def runtime_mixin(commands, device, out_dir):

for name, cmd in commands.items():
opcode = cmd["opcode"]
base_name = cmd["base_name"]
suffix = cmd["types_suffix"]
if cmd.get("custom_op", False):
func = f"op_{name}"
func = f"op_{base_name}"
if "ctype" in cmd:
func += f"<{cmd['ctype']}>"
else:
n_inputs = len(cmd["inputs"])
n_outputs = len(cmd["outputs"])
dims = cmd.get("dims", 1)
vectorized = cmd.get("vectorized", True)

kernel_name = f"kernel_{base_name}"
if device == "cpu":
if vectorized:
kernel = f"kernel_{name}<CpuTypes>, kernel_{name}<SimdTypes>"
else:
kernel = f"kernel_{name}<CpuTypes>, kernel_{name}<CpuTypes>"
simd = "Simd" if vectorized else "Cpu"
kernel = (
f"{kernel_name}<CpuTypes{suffix}>, "
f"{kernel_name}<{simd}Types{suffix}>"
)
device_arg = ", DeviceType"
elif device == "gpu":
kernel = f"kernel_{name}<GpuTypes>"
kernel = f"{kernel_name}<GpuTypes{suffix}>"
device_arg = ""
foreach_func = (
f"tensor_foreach_dynamic<{kernel}, {n_inputs}, {n_outputs}{device_arg}>"
Expand All @@ -293,10 +361,15 @@ def runtime_backward_mixin(commands, device, out_dir):
if not cmd.get("differentiable", False):
continue
opcode = cmd["opcode"]
base_name = cmd["base_name"]
suffix = cmd["types_suffix"]
if cmd.get("custom_op", False):
func = f"backward_op_{base_name}"
if "ctype" in cmd:
func += f"<{cmd['ctype']}>"
f.write(
f"case {opcode}:\n"
f" backward_op_{name}(instr, locals, local_grads, device);\n"
f" {func}(instr, locals, local_grads, device);\n"
f" break;\n"
)
else:
Expand All @@ -318,20 +391,16 @@ def runtime_backward_mixin(commands, device, out_dir):
out_stored_str = ",".join(str(i) for i in out_stored)
vectorized = cmd.get("vectorized", True)

kernel_name = f"backward_kernel_{base_name}"
if device == "cpu":
if vectorized:
kernel = (
f"backward_kernel_{name}<CpuTypes>, "
f"backward_kernel_{name}<SimdTypes>"
)
else:
kernel = (
f"backward_kernel_{name}<CpuTypes>, "
f"backward_kernel_{name}<CpuTypes>"
)
simd = "Simd" if vectorized else "Cpu"
kernel = (
f"{kernel_name}<CpuTypes{suffix}>, "
f"{kernel_name}<{simd}Types{suffix}>"
)
device_arg = ", DeviceType"
elif device == "gpu":
kernel = f"backward_kernel_{name}<GpuTypes>"
kernel = f"{kernel_name}<GpuTypes{suffix}>"
device_arg = ""

dims = cmd.get("dims", 1)
Expand All @@ -354,8 +423,10 @@ def runtime_backward_mixin(commands, device, out_dir):


def is_single_input(arg):
return isinstance(arg.get("type"), list) and len(arg["type"]) > 1 and (
arg["type"][1] == "single"
return (
isinstance(arg.get("type"), list)
and len(arg["type"]) > 1
and (arg["type"][1] == "single")
)


Expand All @@ -369,7 +440,8 @@ def kernel_checks(commands, out_dir):
def check(kernel, index, single, what):
f.write(
f"static_assert(\n"
f" single_params<decltype(&{kernel}<SimdTypes>)>::value[{index}] == "
f" single_params<decltype(&{kernel}<SimdTypes{suffix}>)>"
f"::value[{index}] == "
f"{'true' if single else 'false'},\n"
f' "{kernel}: parameter {index} ({what}) must '
f'{"" if single else "not "}be single"\n'
Expand All @@ -379,6 +451,8 @@ def check(kernel, index, single, what):
for name, cmd in commands.items():
if cmd.get("custom_op", False) or not cmd.get("vectorized", True):
continue
suffix = cmd["types_suffix"]
name = cmd["base_name"]
inputs = cmd["inputs"]
if is_single_input(inputs[0]):
raise ValueError(
Expand Down
1 change: 1 addition & 0 deletions madspace/generate_pyi.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def main():
"--enum-class-locations=Activation:MLP.Activation",
"--enum-class-locations=CutMode:Cuts.CutMode",
"--enum-class-locations=LRSchedule:AdamOptimizer.LRSchedule",
"--enum-class-locations=DataType:DataType",
# Fail the build on unresolvable names/expressions (e.g. an enum
# default value needing its own --enum-class-locations entry)
# instead of silently emitting a stub with holes in it.
Expand Down
4 changes: 4 additions & 0 deletions madspace/include/madspace/compgraphs/function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ class FunctionBuilder {
ValueVec instruction(const std::string& name, const ValueVec& args);
/// Append @p instruction with arguments @p args; returns its outputs.
ValueVec instruction(InstructionPtr instruction, const ValueVec& args);
/// Calls the float32 or float64 variant `<name>_float32` / `<name>_float64` of
/// an instruction, chosen by the first non-literal float argument. Float64
/// literals are converted for a float32 call.
ValueVec precision_instruction(const std::string& name, const ValueVec& args);
/// Index of the execution stream new instructions are added to.
std::size_t current_stream() const { return _current_stream; }
/// Select the execution stream new instructions are added to.
Expand Down
Loading
Loading