# ____________________________________________________________________________________
#
# Pyomo: Python Optimization Modeling Objects
# Copyright (c) 2008-2026 National Technology and Engineering Solutions of Sandia, LLC
# Under the terms of Contract DE-NA0003525 with National Technology and Engineering
# Solutions of Sandia, LLC, the U.S. Government retains certain rights in this
# software. This software is distributed under the 3-clause BSD License.
# ____________________________________________________________________________________
#
# Problem Writer for BARON .bar Format Files
#
import itertools
import logging
import math
from io import StringIO
from contextlib import nullcontext
from pyomo.common.collections import OrderedSet
from pyomo.opt import ProblemFormat
from pyomo.opt.base import AbstractProblemWriter, WriterFactory
from pyomo.core.expr.numvalue import (
value,
native_numeric_types,
native_types,
nonpyomo_leaf_types,
)
from pyomo.core.expr.visitor import _ToStringVisitor
import pyomo.core.expr as EXPR
from pyomo.core.base import (
SortComponents,
SymbolMap,
ShortNameLabeler,
NumericLabeler,
Constraint,
Objective,
Param,
)
from pyomo.core.base.component import ActiveComponent
# CLH: EXPORT suffixes "constraint_types" and "branching_priorities"
# pass their respective information to the .bar file
import pyomo.core.base.suffix
import pyomo.core.kernel.suffix
from pyomo.core.kernel.block import IBlock
from pyomo.repn.util import valid_expr_ctypes_minlp, valid_active_ctypes_minlp, ftoa
logger = logging.getLogger('pyomo.core')
def _handle_PowExpression(visitor, node, values):
# Per the BARON manual, x ^ y is allowed as long as x and y are not
# both variables. There is an issue that if one of the arguments
# contains "0*var", Pyomo will see that as fixed, but Baron will see
# it as variable. We will work around that by resolving any fixed
# expressions to their corresponding fixed value.
unfixed_count = 0
for i, arg in enumerate(node.args):
if type(arg) in native_types:
pass
elif arg.is_fixed():
values[i] = ftoa(value(arg), True)
else:
unfixed_count += 1
if unfixed_count < 2:
return f"{values[0]} ^ {values[1]}"
else:
return f"exp(({values[0]}) * log({values[1]}))"
_allowableUnaryFunctions = {'exp', 'log10', 'log', 'sqrt'}
_log10_e = ftoa(math.log10(math.e))
def _handle_UnaryFunctionExpression(visitor, node, values):
if node.name == "sqrt":
# Parens are necessary because sqrt() and "^" have different
# precedence levels. Instead of parsing the arg, be safe and
# explicitly add parens
return f"(({values[0]}) ^ 0.5)"
elif node.name == 'log10':
return f"({_log10_e} * log({values[0]}))"
elif node.name not in _allowableUnaryFunctions:
raise RuntimeError(
'The BARON .BAR format does not support the unary '
'function "%s".' % (node.name,)
)
return node._to_string(values, visitor.verbose, visitor.smap)
def _handle_AbsExpression(visitor, node, values):
# Parens are necessary because abs() and "^" have different
# precedence levels. Instead of parsing the arg, be safe and
# explicitly add parens
return f"((({values[0]}) ^ 2) ^ 0.5)"
_plusMinusOne = {-1, 1}
#
# A visitor pattern that creates a string for an expression
# that is compatible with the BARON syntax.
#
[docs]
class ToBaronVisitor(_ToStringVisitor):
_expression_handlers = {
EXPR.PowExpression: _handle_PowExpression,
EXPR.UnaryFunctionExpression: _handle_UnaryFunctionExpression,
EXPR.AbsExpression: _handle_AbsExpression,
}
[docs]
def __init__(self, variables, smap):
super(ToBaronVisitor, self).__init__(False, smap)
self.variables = variables
[docs]
def visiting_potential_leaf(self, node):
"""
Visiting a potential leaf.
Return True if the node is not expanded.
"""
if node.__class__ in native_types:
return True, ftoa(node, True)
if node.is_expression_type():
# Special handling if NPV and semi-NPV types:
if not node.is_potentially_variable():
return True, ftoa(node(), True)
if node.__class__ is EXPR.MonomialTermExpression:
return True, self._monomial_to_string(node)
if node.__class__ is EXPR.LinearExpression:
return True, self._linear_to_string(node)
# we will descend into this, so type checking will happen later
return False, None
if node.is_component_type():
if node.ctype not in valid_expr_ctypes_minlp:
# Make sure all components in active constraints
# are basic ctypes we know how to deal with.
raise RuntimeError(
"Unallowable component '%s' of type %s found in an active "
"constraint or objective.\nThe GAMS writer cannot export "
"expressions with this component type."
% (node.name, node.ctype.__name__)
)
if node.is_fixed():
return True, ftoa(node(), True)
else:
assert node.is_variable_type()
self.variables.add(id(node))
return True, self.smap.getSymbol(node)
def _monomial_to_string(self, node):
const, var = node.args
if const.__class__ not in native_types:
const = value(const)
if var.is_fixed():
return ftoa(const * var.value, True)
# Special handling: ftoa is slow, so bypass _to_string when this
# is a trivial term
if not const:
return '0'
self.variables.add(id(var))
if const in _plusMinusOne:
if const < 0:
return '-' + self.smap.getSymbol(var)
else:
return self.smap.getSymbol(var)
return ftoa(const, True) + '*' + self.smap.getSymbol(var)
def _var_to_string(self, node):
if node.is_fixed():
return ftoa(node.value, True)
self.variables.add(id(node))
return self.smap.getSymbol(node)
def _linear_to_string(self, node):
values = [
(
self._monomial_to_string(arg)
if arg.__class__ is EXPR.MonomialTermExpression
else (
ftoa(arg)
if arg.__class__ in native_numeric_types
else (
self._var_to_string(arg)
if arg.is_variable_type()
else ftoa(value(arg), True)
)
)
)
for arg in node.args
]
return node._to_string(values, False, self.smap)
[docs]
def expression_to_string(expr, variables, smap):
return ToBaronVisitor(variables, smap).dfs_postorder_stack(expr)
# TODO: The to_string function is handy, but the fact that
# it calls .name under the hood for all components
# everywhere they are used will present ENORMOUS
# overhead for components that have a large index set.
# It might be worth adding an extra keyword to that
# function that takes a "labeler" or "symbol_map" for
# writing non-expression components.
[docs]
@WriterFactory.register('bar', 'Generate the corresponding BARON BAR file.')
class ProblemWriter_bar(AbstractProblemWriter):
[docs]
def __init__(self):
AbstractProblemWriter.__init__(self, ProblemFormat.bar)
def _write_equations_section(
self,
model,
output_file,
all_blocks_list,
active_components_data_var,
symbol_map,
c_labeler,
output_fixed_variable_bounds,
skip_trivial_constraints,
sorter,
):
referenced_variable_ids = OrderedSet()
def _skip_trivial(constraint_data):
if skip_trivial_constraints:
if constraint_data._linear_canonical_form:
repn = constraint_data.canonical_form()
if (repn.variables is None) or (len(repn.variables) == 0):
return True
elif constraint_data.body.polynomial_degree() == 0:
return True
return False
#
# Check for active suffixes to export
#
if isinstance(model, IBlock):
suffix_gen = lambda b: (
(suf.storage_key, suf)
for suf in pyomo.core.kernel.suffix.export_suffix_generator(
b, active=True, descend_into=False
)
)
else:
suffix_gen = (
lambda b: pyomo.core.base.suffix.active_export_suffix_generator(b)
)
r_o_eqns = {}
c_eqns = {}
l_eqns = {}
branching_priorities_suffixes = []
for block in all_blocks_list:
for name, suffix in suffix_gen(block):
if name in {'branching_priorities', 'priority'}:
branching_priorities_suffixes.append(suffix)
elif name == 'constraint_types':
for constraint_data, constraint_type in suffix.items():
info = constraint_data.to_bounded_expression(True)
if not _skip_trivial(constraint_data):
if constraint_type.lower() == 'relaxationonly':
r_o_eqns[constraint_data] = info
elif constraint_type.lower() == 'convex':
c_eqns[constraint_data] = info
elif constraint_type.lower() == 'local':
l_eqns[constraint_data] = info
else:
raise ValueError(
"A suffix '%s' contained an invalid value: %s\n"
"Choices are: [relaxationonly, convex, local]"
% (suffix.name, constraint_type)
)
else:
if block is block.model():
if block.name == 'unknown':
_location = 'model'
else:
_location = "model '%s'" % (block.name,)
else:
_location = "block '%s'" % (block.name,)
raise ValueError(
"The BARON writer can not export suffix with name '%s'. "
"Either remove it from the %s or deactivate it."
% (name, _location)
)
non_standard_eqns = set()
non_standard_eqns.update(r_o_eqns)
non_standard_eqns.update(c_eqns)
non_standard_eqns.update(l_eqns)
#
# EQUATIONS
#
# Equation Declaration
n_roeqns = len(r_o_eqns)
n_ceqns = len(c_eqns)
n_leqns = len(l_eqns)
eqns = {}
# Alias the constraints by declaration order since Baron does not
# include the constraint names in the solution file. It is important
# that this alias not clash with any real constraint labels, hence
# the use of the ".c<integer>" template. It is not possible to declare
# a component having this type of name when using standard syntax.
# There are ways to do it, but it is unlikely someone will.
order_counter = 0
alias_template = ".c%d"
output_file.write('EQUATIONS ')
output_file.write("c_e_FIX_ONE_VAR_CONST__")
order_counter += 1
for block in all_blocks_list:
for constraint_data in block.component_data_objects(
Constraint, active=True, sort=sorter, descend_into=False
):
lb, body, ub = constraint_data.to_bounded_expression(True)
if lb is None and ub is None:
assert not constraint_data.equality
continue # non-binding, so skip
if (not _skip_trivial(constraint_data)) and (
constraint_data not in non_standard_eqns
):
eqns[constraint_data] = lb, body, ub
con_symbol = symbol_map.createSymbol(constraint_data, c_labeler)
assert not con_symbol.startswith('.')
assert con_symbol != "c_e_FIX_ONE_VAR_CONST__"
symbol_map.alias(constraint_data, alias_template % order_counter)
output_file.write(", " + str(con_symbol))
order_counter += 1
output_file.write(";\n\n")
if n_roeqns > 0:
output_file.write('RELAXATION_ONLY_EQUATIONS ')
for i, constraint_data in enumerate(r_o_eqns):
con_symbol = symbol_map.createSymbol(constraint_data, c_labeler)
assert not con_symbol.startswith('.')
assert con_symbol != "c_e_FIX_ONE_VAR_CONST__"
symbol_map.alias(constraint_data, alias_template % order_counter)
if i == n_roeqns - 1:
output_file.write(str(con_symbol) + ';\n\n')
else:
output_file.write(str(con_symbol) + ', ')
order_counter += 1
if n_ceqns > 0:
output_file.write('CONVEX_EQUATIONS ')
for i, constraint_data in enumerate(c_eqns):
con_symbol = symbol_map.createSymbol(constraint_data, c_labeler)
assert not con_symbol.startswith('.')
assert con_symbol != "c_e_FIX_ONE_VAR_CONST__"
symbol_map.alias(constraint_data, alias_template % order_counter)
if i == n_ceqns - 1:
output_file.write(str(con_symbol) + ';\n\n')
else:
output_file.write(str(con_symbol) + ', ')
order_counter += 1
if n_leqns > 0:
output_file.write('LOCAL_EQUATIONS ')
for i, constraint_data in enumerate(l_eqns):
con_symbol = symbol_map.createSymbol(constraint_data, c_labeler)
assert not con_symbol.startswith('.')
assert con_symbol != "c_e_FIX_ONE_VAR_CONST__"
symbol_map.alias(constraint_data, alias_template % order_counter)
if i == n_leqns - 1:
output_file.write(str(con_symbol) + ';\n\n')
else:
output_file.write(str(con_symbol) + ', ')
order_counter += 1
# Create a dictionary of baron variable names to match to the
# strings that constraint.to_string() prints. An important
# note is that the variable strings are padded by spaces so
# that whole variable names are recognized, and simple
# variable names are not identified inside longer names.
# Example: ' x[1] ' -> ' x3 '
# FIXME: 7/18/14 CLH: This may cause mistakes if spaces in
# variable names are allowed
if isinstance(model, IBlock):
mutable_param_gen = lambda b: b.components(ctype=Param, descend_into=False)
else:
def mutable_param_gen(b):
for param in block.component_objects(Param):
if param.mutable and param.is_indexed():
param_data_iter = (
param_data for index, param_data in param.items()
)
elif not param.is_indexed():
param_data_iter = iter([param])
else:
param_data_iter = iter([])
for param_data in param_data_iter:
yield param_data
# Equation Definition
output_file.write('c_e_FIX_ONE_VAR_CONST__: ONE_VAR_CONST__ == 1;\n')
for constraint_data, (lb, body, ub) in itertools.chain(
eqns.items(), r_o_eqns.items(), c_eqns.items(), l_eqns.items()
):
variables = OrderedSet()
# print(symbol_map.byObject.keys())
eqn_body = expression_to_string(body, variables, smap=symbol_map)
# print(symbol_map.byObject.keys())
referenced_variable_ids.update(variables)
if len(variables) == 0:
assert not skip_trivial_constraints
eqn_body += " + 0 * ONE_VAR_CONST__ "
# 7/29/14 CLH:
# FIXME: Baron doesn't handle many of the
# intrinsic_functions available in pyomo. The
# error message given by baron is also very
# weak. Either a function here to re-write
# unallowed expressions or a way to track solver
# capability by intrinsic_expression would be
# useful.
##########################
con_symbol = symbol_map.byObject[id(constraint_data)]
output_file.write(str(con_symbol) + ': ')
# Fill in the left and right hand side (constants) of
# the equations
# Equality constraint
if constraint_data.equality:
eqn_lhs = ''
eqn_rhs = ' == ' + ftoa(ub)
# Greater than constraint
elif ub is None:
eqn_rhs = ' >= ' + ftoa(lb)
eqn_lhs = ''
# Less than constraint
elif lb is None:
eqn_rhs = ' <= ' + ftoa(ub)
eqn_lhs = ''
# Double-sided constraint
elif lb is not None and ub is not None:
eqn_lhs = ftoa(lb) + ' <= '
eqn_rhs = ' <= ' + ftoa(ub)
eqn_string = eqn_lhs + eqn_body + eqn_rhs + ';\n'
output_file.write(eqn_string)
#
# OBJECTIVE
#
output_file.write("\nOBJ: ")
n_objs = 0
for block in all_blocks_list:
for objective_data in block.component_data_objects(
Objective, active=True, sort=sorter, descend_into=False
):
n_objs += 1
if n_objs > 1:
raise ValueError(
"The BARON writer has detected multiple active "
"objective functions on model %s, but "
"currently only handles a single objective." % (model.name)
)
# create symbol
symbol_map.createSymbol(objective_data, c_labeler)
symbol_map.alias(objective_data, "__default_objective__")
if objective_data.is_minimizing():
output_file.write("minimize ")
else:
output_file.write("maximize ")
variables = OrderedSet()
# print(symbol_map.byObject.keys())
obj_string = expression_to_string(
objective_data.expr, variables, smap=symbol_map
)
# print(symbol_map.byObject.keys())
referenced_variable_ids.update(variables)
output_file.write(obj_string + ";\n\n")
# referenced_variable_ids.update(symbol_map.byObject.keys())
return referenced_variable_ids, branching_priorities_suffixes
def __call__(self, model, output_filename, solver_capability, io_options):
if output_filename is None:
output_filename = model.name + ".bar"
# If the user provides a file name, we will use the opened file
# as a context manager to ensure it gets closed. If the user
# provided something else (e.g., a file-like object), we will
# use a nullcontext manager to prevent closing it.
if isinstance(output_filename, str):
output_file = open(output_filename, "w")
else:
output_file = nullcontext(output_filename)
with output_file as FILE:
symbol_map = self._write_bar_file(
model, FILE, solver_capability, io_options
)
return output_filename, symbol_map
def _write_bar_file(self, model, output_file, solver_capability, io_options):
# Make sure not to modify the user's dictionary, they may be
# reusing it outside of this call
io_options = dict(io_options)
# NOTE: io_options is a simple dictionary of keyword-value
# pairs specific to this writer.
symbolic_solver_labels = io_options.pop("symbolic_solver_labels", False)
labeler = io_options.pop("labeler", None)
# How much effort do we want to put into ensuring the
# LP file is written deterministically for a Pyomo model:
# 0 : None
# 1 : sort keys of indexed components (default)
# 2 : sort keys AND sort names (over declaration order)
file_determinism = io_options.pop("file_determinism", 1)
sorter = SortComponents.unsorted
if file_determinism >= 1:
sorter = sorter | SortComponents.indices
if file_determinism >= 2:
sorter = sorter | SortComponents.alphabetical
output_fixed_variable_bounds = io_options.pop(
"output_fixed_variable_bounds", False
)
# Skip writing constraints whose body section is fixed (i.e.,
# no variables)
skip_trivial_constraints = io_options.pop("skip_trivial_constraints", False)
# Note: Baron does not allow specification of runtime
# option outside of this file, so we add support
# for them here
solver_options = io_options.pop("solver_options", {})
if len(io_options):
raise ValueError(
"ProblemWriter_baron_writer passed unrecognized io_options:\n\t"
+ "\n\t".join("%s = %s" % (k, v) for k, v in io_options.items())
)
if symbolic_solver_labels and (labeler is not None):
raise ValueError(
"Baron problem writer: Using both the "
"'symbolic_solver_labels' and 'labeler' "
"I/O options is forbidden"
)
# Make sure there are no strange ActiveComponents. The expression
# walker will handle strange things in constraints later.
model_ctypes = model.collect_ctypes(active=True)
invalids = set()
for t in model_ctypes - valid_active_ctypes_minlp:
if issubclass(t, ActiveComponent):
invalids.add(t)
if len(invalids):
invalids = [t.__name__ for t in invalids]
raise RuntimeError(
"Unallowable active component(s) %s.\nThe BARON writer cannot "
"export models with this component type." % ", ".join(invalids)
)
# Process the options. Rely on baron to catch
# and reset bad option values
output_file.write("OPTIONS {\n")
summary_found = False
if len(solver_options):
for key, val in solver_options.items():
if key.lower() == 'summary':
summary_found = True
if key.endswith("Name"):
output_file.write(key + ": \"" + str(val) + "\";\n")
else:
output_file.write(key + ": " + str(val) + ";\n")
if not summary_found:
# The 'summary option is defaulted to 0, so that no
# summary file is generated in the directory where the
# user calls baron. Check if a user explicitly asked for
# a summary file.
output_file.write("Summary: 0;\n")
output_file.write("}\n\n")
if symbolic_solver_labels:
# Note that the Var and Constraint labelers must use the
# same labeler, so that we can correctly detect name
# collisions (which can arise when we truncate the labels to
# the max allowable length. BARON requires all identifiers
# to start with a letter. We will (randomly) choose "s_"
# (for 'shortened')
v_labeler = c_labeler = ShortNameLabeler(
15,
prefix='s_',
suffix='_',
caseInsensitive=True,
legalRegex='^[a-zA-Z]',
)
elif labeler is None:
v_labeler = NumericLabeler('x')
c_labeler = NumericLabeler('c')
else:
v_labeler = c_labeler = labeler
symbol_map = SymbolMap()
symbol_map.default_labeler = v_labeler
# sm_bySymbol = symbol_map.bySymbol
# Cache the list of model blocks so we don't have to call
# model.block_data_objects() many many times, which is slow
# for indexed blocks
all_blocks_list = list(
model.block_data_objects(active=True, sort=sorter, descend_into=True)
)
active_components_data_var = {}
# for block in all_blocks_list:
# tmp = active_components_data_var[id(block)] = \
# list(obj for obj in block.component_data_objects(Var,
# sort=sorter,
# descend_into=False))
# create_symbols_func(symbol_map, tmp, labeler)
# GAH: Not sure this is necessary, and also it would break for
# non-mutable indexed params so I am commenting out for now.
# for param_data in active_components_data(block, Param, sort=sorter):
# instead of checking if param_data.mutable:
# if not param_data.is_constant():
# create_symbol_func(symbol_map, param_data, labeler)
# symbol_map_variable_ids = set(symbol_map.byObject.keys())
# object_symbol_dictionary = symbol_map.byObject
#
# Go through the objectives and constraints and generate
# the output so that we can obtain the set of referenced
# variables.
#
equation_section_stream = StringIO()
referenced_variable_ids, branching_priorities_suffixes = (
self._write_equations_section(
model,
equation_section_stream,
all_blocks_list,
active_components_data_var,
symbol_map,
c_labeler,
output_fixed_variable_bounds,
skip_trivial_constraints,
sorter,
)
)
#
# BINARY_VARIABLES, INTEGER_VARIABLES, POSITIVE_VARIABLES, VARIABLES
#
BinVars = []
IntVars = []
PosVars = []
Vars = []
for vid in referenced_variable_ids:
name = symbol_map.byObject[vid]
var_data = symbol_map.bySymbol[name]
if var_data.is_continuous():
if var_data.has_lb() and (value(var_data.lb) >= 0):
TypeList = PosVars
else:
TypeList = Vars
elif var_data.is_binary():
TypeList = BinVars
elif var_data.is_integer():
TypeList = IntVars
else:
assert False
TypeList.append(name)
if len(BinVars) > 0:
BinVars.sort()
output_file.write('BINARY_VARIABLES ')
output_file.write(", ".join(BinVars))
output_file.write(';\n\n')
if len(IntVars) > 0:
IntVars.sort()
output_file.write('INTEGER_VARIABLES ')
output_file.write(", ".join(IntVars))
output_file.write(';\n\n')
PosVars.append('ONE_VAR_CONST__')
PosVars.sort()
output_file.write('POSITIVE_VARIABLES ')
output_file.write(", ".join(PosVars))
output_file.write(';\n\n')
if len(Vars) > 0:
Vars.sort()
output_file.write('VARIABLES ')
output_file.write(", ".join(Vars))
output_file.write(';\n\n')
#
# LOWER_BOUNDS
#
lbounds = {}
for vid in referenced_variable_ids:
name = symbol_map.byObject[vid]
var_data = symbol_map.bySymbol[name]
if var_data.fixed:
if output_fixed_variable_bounds:
var_data_lb = ftoa(var_data.value, False)
else:
var_data_lb = None
else:
var_data_lb = None
if var_data.has_lb():
var_data_lb = ftoa(var_data.lb, False)
if var_data_lb is not None:
name_to_output = symbol_map.getSymbol(var_data)
lbounds[name_to_output] = '%s: %s;\n' % (name_to_output, var_data_lb)
if len(lbounds) > 0:
output_file.write("LOWER_BOUNDS{\n")
output_file.write("".join(lbounds[key] for key in sorted(lbounds.keys())))
output_file.write("}\n\n")
lbounds = None
#
# UPPER_BOUNDS
#
ubounds = {}
for vid in referenced_variable_ids:
name = symbol_map.byObject[vid]
var_data = symbol_map.bySymbol[name]
if var_data.fixed:
if output_fixed_variable_bounds:
var_data_ub = ftoa(var_data.value, False)
else:
var_data_ub = None
else:
var_data_ub = None
if var_data.has_ub():
var_data_ub = ftoa(var_data.ub, False)
if var_data_ub is not None:
name_to_output = symbol_map.getSymbol(var_data)
ubounds[name_to_output] = '%s: %s;\n' % (name_to_output, var_data_ub)
if len(ubounds) > 0:
output_file.write("UPPER_BOUNDS{\n")
output_file.write("".join(ubounds[key] for key in sorted(ubounds.keys())))
output_file.write("}\n\n")
ubounds = None
#
# BRANCHING_PRIORITIES
#
# Specifying priorities requires that the pyomo model has established an
# EXTERNAL, float suffix called 'branching_priorities' on the model
# object, indexed by the relevant variable
BranchingPriorityHeader = False
for suffix in branching_priorities_suffixes:
for var, priority in suffix.items():
if var.is_indexed():
var_iter = var.values()
else:
var_iter = (var,)
for var_data in var_iter:
if id(var_data) not in referenced_variable_ids:
continue
if priority is not None:
if not BranchingPriorityHeader:
output_file.write('BRANCHING_PRIORITIES{\n')
BranchingPriorityHeader = True
output_file.write(
"%s: %s;\n" % (symbol_map.getSymbol(var_data), priority)
)
if BranchingPriorityHeader:
output_file.write("}\n\n")
#
# Now write the objective and equations section
#
output_file.write(equation_section_stream.getvalue())
#
# STARTING_POINT
#
output_file.write('STARTING_POINT{\nONE_VAR_CONST__: 1;\n')
tmp = {}
for vid in referenced_variable_ids:
name = symbol_map.byObject[vid]
var_data = symbol_map.bySymbol[name]
starting_point = var_data.value
if starting_point is not None:
var_name = symbol_map.getSymbol(var_data)
tmp[var_name] = "%s: %s;\n" % (var_name, ftoa(starting_point, False))
output_file.write("".join(tmp[key] for key in sorted(tmp.keys())))
output_file.write('}\n\n')
return symbol_map