From f9102c6c45eb358076175e67662f20047848d467 Mon Sep 17 00:00:00 2001 From: Pantelis Sopasakis Date: Thu, 10 Sep 2020 16:21:47 +0100 Subject: [PATCH 1/7] tcp server takes command-line arguments addresses issue #198 --- .../opengen/templates/tcp/tcp_server.rs | 50 ++++++++++++++++--- .../templates/tcp/tcp_server_cargo.toml | 1 + 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/open-codegen/opengen/templates/tcp/tcp_server.rs b/open-codegen/opengen/templates/tcp/tcp_server.rs index 2db14b20..d672757d 100644 --- a/open-codegen/opengen/templates/tcp/tcp_server.rs +++ b/open-codegen/opengen/templates/tcp/tcp_server.rs @@ -5,25 +5,43 @@ use optimization_engine::alm::*; use serde::{Deserialize, Serialize}; +#[macro_use] +extern crate clap; + use std::{ io::{prelude::Read, Write}, net::TcpListener, }; +use clap::{Arg, App}; + use {{ meta.optimizer_name }}::*; #[macro_use] extern crate log; /// IP of server (use 0.0.0.0 to bind to any IP) -const BIND_IP: &str = "{{tcp_server_config.bind_ip}}"; +/// Can be overriden by the user +const BIND_IP_DEFAULT: &str = "{{tcp_server_config.bind_ip}}"; /// Port -const BIND_PORT: i32 = {{tcp_server_config.bind_port}}; +/// Can be overriden by the user +const BIND_PORT_DEFAULT: u32 = {{tcp_server_config.bind_port}}; /// Size of read buffer +/// Can be overriden by the user const READ_BUFFER_SIZE: usize = 1024; +/// Configuration of TCP server (provided by the user +/// as command-line parameters) +#[derive(Debug)] +struct TcpServerConfiguration<'a> { + /// Bind IP + ip: &'a str, + /// Port + port: u32, +} + #[derive(Deserialize, Debug)] struct ExecutionParameter { /// Parameter @@ -174,14 +192,14 @@ fn execution_handler( } } -fn run_server() { +fn run_server(tcp_config: &TcpServerConfiguration) { info!("Initializing cache..."); let mut p = [0.0; {{meta.optimizer_name|upper}}_NUM_PARAMETERS]; let mut cache = initialize_solver(); info!("Done"); - let listener = TcpListener::bind(format!("{}:{}", BIND_IP, BIND_PORT)).unwrap(); + let listener = TcpListener::bind(format!("{}:{}", tcp_config.ip, tcp_config.port)).unwrap(); let mut u = [0.0; {{meta.optimizer_name|upper}}_NUM_DECISION_VARIABLES]; - info!("listening started, ready to accept"); + info!("listening started, ready to accept connections at {}:{}", tcp_config.ip, tcp_config.port); for stream in listener.incoming() { let mut stream = stream.unwrap(); @@ -227,7 +245,27 @@ fn run_server() { } fn main() { + let matches = App::new("OpEn TCP Server [potato]") + .version("0.1.0") + .author("Pantelis Sopasakis") + .about("TCP interface of OpEn optimizer") + .arg(Arg::with_name("ip") + .short("ip") + .long("ip") + .takes_value(true) + .help("TCP server bind IP (0.0.0.0 for no restrictions)")) + .arg(Arg::with_name("port") + .short("p") + .long("port") + .takes_value(true) + .help("TCP server port")) + .get_matches(); + let port = value_t!(matches, "port", u32).unwrap_or(BIND_PORT_DEFAULT); + let ip = matches.value_of("ip").unwrap_or(BIND_IP_DEFAULT); + let server_config = TcpServerConfiguration {ip, port}; + pretty_env_logger::init(); - run_server(); + info!("{:?}", server_config); + run_server(&server_config); info!("Exiting... (adios!)"); } diff --git a/open-codegen/opengen/templates/tcp/tcp_server_cargo.toml b/open-codegen/opengen/templates/tcp/tcp_server_cargo.toml index 6a0fb6b3..c599af8b 100644 --- a/open-codegen/opengen/templates/tcp/tcp_server_cargo.toml +++ b/open-codegen/opengen/templates/tcp/tcp_server_cargo.toml @@ -21,6 +21,7 @@ publish=false [dependencies] +clap = "*" {% if build_config.local_path is not none -%} optimization_engine = {path = "{{build_config.local_path}}"} {% else -%} From 81fc625e26b6e9dedaa9b93362eb93304e18df57 Mon Sep 17 00:00:00 2001 From: Pantelis Sopasakis Date: Thu, 10 Sep 2020 17:40:45 +0100 Subject: [PATCH 2/7] ip/port are now configurable --- open-codegen/opengen/main.py | 14 ++- .../opengen/tcp/optimizer_tcp_manager.py | 96 +++++++++++++------ 2 files changed, 81 insertions(+), 29 deletions(-) diff --git a/open-codegen/opengen/main.py b/open-codegen/opengen/main.py index d273401f..8762e04a 100644 --- a/open-codegen/opengen/main.py +++ b/open-codegen/opengen/main.py @@ -1,6 +1,9 @@ +import logging import casadi.casadi as cs import opengen as og +logging.getLogger().setLevel(5) + u = cs.SX.sym("u", 5) # decision variable (nu = 5) p = cs.SX.sym("p", 2) # parameter (np = 2) phi = cs.dot(u, u) # cost function @@ -18,11 +21,18 @@ .with_build_directory('my_optimizers') \ .with_build_mode(og.config.BuildConfiguration.DEBUG_MODE) \ .with_open_version('0.7.1-alpha') \ - .with_tcp_interface_config(tcp_interface_config=tcp_config) + .with_tcp_interface_config() builder = og.builder.OpEnOptimizerBuilder(problem, meta, build_config, og.config.SolverConfiguration()) -builder.build() +# builder.build() + +mng = og.tcp.OptimizerTcpManager('my_optimizers/halfspace_optimizer', ip='0.0.0.0', port=3311) +mng.start() +print(mng.ping()) +resp = mng.call(p=[1., 2.]) +print(resp.get().solution) +mng.kill() diff --git a/open-codegen/opengen/tcp/optimizer_tcp_manager.py b/open-codegen/opengen/tcp/optimizer_tcp_manager.py index 98ba658f..bf729f70 100644 --- a/open-codegen/opengen/tcp/optimizer_tcp_manager.py +++ b/open-codegen/opengen/tcp/optimizer_tcp_manager.py @@ -20,25 +20,53 @@ class OptimizerTcpManager: """ def __init__(self, optimizer_path=None, ip=None, port=None): - """Constructs instance of OptimizerTcpManager + """ + Constructs instance of OptimizerTcpManager + + There are three ways to use this constructor: + + - OptimizerTcpManager(optimizer_path): creates a TCP manager for a local + TCP server using the default IP and port of that TCP server (specified + upon code generation) + - OptimizerTcpManager(optimizer_path, ip, port): creates a TCP manager + for a local TCP server, but overrides the default IP and port. This way + the user can set the address '0.0.0.0', so that the TCP server binds on + all IPs, or '127.0.0.1' so that it is accessible only locally, or a VPN + IP address, so that the optimizer is accessible only over a private + network. + - OptimizerTcpManager(ip, port): If a path is not provided, then the + TCP manager can be used to connect to a remote TCP server, as a client, + but cannot be used to start the server. Args: - optimizer_path: path to auto-generated optimizer (just to - be clear: this is the folder that contains optimizer.yml) + :param optimizer_path: + path to auto-generated optimizer (just to be clear: this is + the folder that contains optimizer.yml) + + :param ip: + the user can provide the IP of a remote TCP server (must be up and + running) so as to establish a remote connection. In that case `path` + must be equal to `None` (see examples above) + + :param port: see ip Returns: New instance of OptimizerTcpManager """ self.__optimizer_path = optimizer_path if optimizer_path is not None: - self.__optimizer_details_from_yml = None + self.__optimizer_details = None # create attribute (including IP and port) self.__load_tcp_details() + if ip is not None: + self.__optimizer_details['tcp']['ip'] = ip + if port is not None: + self.__optimizer_details['tcp']['port'] = port elif ip is not None and port is not None: - self.__optimizer_details_from_yml = {"tcp": {"ip": ip, "port": port}} + self.__optimizer_details = {"tcp": {"ip": ip, "port": port}} else: raise Exception("Illegal arguments") # Check whether the optimizer was built with the current version of opengen - opengen_version = self.__optimizer_details_from_yml['build']['opengen_version'] + opengen_version = self.__optimizer_details['build']['opengen_version'] current_opengen_version = pkg_resources.require("opengen")[0].version if current_opengen_version != opengen_version: logging.warn('the target optimizer was build with a different version of opengen (%s)' % opengen_version) @@ -48,26 +76,13 @@ def __load_tcp_details(self): logging.info("loading TCP/IP details") yaml_file = os.path.join(self.__optimizer_path, "optimizer.yml") with open(yaml_file, 'r') as stream: - self.__optimizer_details_from_yml = yaml.safe_load(stream) - details = self.__optimizer_details_from_yml + self.__optimizer_details = yaml.safe_load(stream) + details = self.__optimizer_details logging.info("TCP/IP details: %s:%d", details['tcp']['ip'], details['tcp']['port']) - def __threaded_start(self): - optimizer_details = self.__optimizer_details_from_yml - logging.info("Starting TCP/IP server at %s:%d (in a detached thread)", - optimizer_details['tcp']['ip'], - optimizer_details['tcp']['port']) - command = ['cargo', 'run', '-q'] - if optimizer_details['build']['build_mode'] == 'release': - command.append('--release') - tcp_dir_name = "tcp_iface_" + optimizer_details['meta']['optimizer_name'] - tcp_iface_directory = os.path.join(self.__optimizer_path, tcp_dir_name) - p = subprocess.Popen(command, cwd=tcp_iface_directory) - p.wait() - @retry(tries=10, delay=1) def __obtain_socket_connection(self): - tcp_data = self.__optimizer_details_from_yml + tcp_data = self.__optimizer_details ip = tcp_data['tcp']['ip'] port = tcp_data['tcp']['port'] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) @@ -101,25 +116,52 @@ def ping(self): return json.loads(data) def __check_if_server_is_running(self): - tcp_data = self.__optimizer_details_from_yml + tcp_data = self.__optimizer_details ip = tcp_data['tcp']['ip'] port = tcp_data['tcp']['port'] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) return 0 == s.connect_ex((ip, port)) def start(self): - """Starts the TCP server""" - # start the server in a separate thread + """Starts the TCP server + + Note: this method starts a *local* server whose path must have been + provided - we cannot start a remote server. + + The server starts on a separate thread, so this method does not block + the execution of the caller's programme. + """ + + # Check if a path has been provided; if not, if self.__optimizer_path is None: raise Exception("No optimizer path provided - cannot start a remote server") + # Server start data + tcp_data = self.__optimizer_details + ip = tcp_data['tcp']['ip'] + port = tcp_data['tcp']['port'] + + # Check if any of the ip/port pairs is occupied if self.__check_if_server_is_running(): - msg = "Port %d not available" % self.__optimizer_details_from_yml['tcp']['port'] + msg = "Port %d not available" % port raise Exception(msg) + def threaded_start(): + optimizer_details = self.__optimizer_details + logging.info("Starting TCP/IP server at %s:%d (in a detached thread)", + ip, port) + command = ['cargo', 'run', '-q', '--', '--port=%d' % port, '--ip=%s' % ip] + if optimizer_details['build']['build_mode'] == 'release': + command.append('--release') + tcp_dir_name = "tcp_iface_" + optimizer_details['meta']['optimizer_name'] + tcp_iface_directory = os.path.join(self.__optimizer_path, tcp_dir_name) + p = subprocess.Popen(command, cwd=tcp_iface_directory) + p.wait() + + # start the server in a separate thread logging.info("Starting TCP/IP server thread") - thread = Thread(target=self.__threaded_start) + thread = Thread(target=threaded_start) thread.start() # ping the server until it responds so that we know it's From 8c5388325fe91582b04e38b66029fc53a333ed8b Mon Sep 17 00:00:00 2001 From: Pantelis Sopasakis Date: Thu, 10 Sep 2020 19:36:15 +0100 Subject: [PATCH 3/7] unit tests and docs --- docs/python-advanced.md | 33 ++++++++++++++---- open-codegen/opengen/main.py | 33 +++++++++++++----- .../opengen/tcp/optimizer_tcp_manager.py | 13 ++++--- .../opengen/templates/tcp/tcp_server.rs | 6 ++-- open-codegen/opengen/test/test.py | 34 ++++++++++++++----- 5 files changed, 89 insertions(+), 30 deletions(-) diff --git a/docs/python-advanced.md b/docs/python-advanced.md index 7ef3a83c..f353c4ae 100644 --- a/docs/python-advanced.md +++ b/docs/python-advanced.md @@ -126,7 +126,7 @@ for requests (e.g., for remote connections), you may crate an ```python tcp_config = og.config.TcpServerConfiguration('10.8.0.12', 9555) ``` - + and then provide it to the builder configuration using ```python @@ -138,10 +138,13 @@ builder_config.with_tcp_interface_config(tcp_config) There are two ways to connect to a generated TCP server and call the auto-generated optimizer: -1. *Connect to a local optimizer* by providing the path of the optimizer +
+Connect to a local optimizer by providing the path of the optimizer directory. For that purpose, we need to create an instance of -`OptimizerTcpManager` and specify the path to the auto-generated optimizer; -for example, +OptimizerTcpManager and specify the path to the auto-generated optimizer. +
+ +For example, ```python mng = og.tcp.OptimizerTcpManager('python_build/the_optimizer') @@ -150,10 +153,28 @@ mng = og.tcp.OptimizerTcpManager('python_build/the_optimizer') we can then `start` the optimizer. The TCP manager known what IP and port to link to, so we can `call` it directly. -2. *Connect to a remote optimizer* by providing its IP and port. In that +
+Connect to a local optimizer and customize its IP and port. +This is particularly useful if you need to start multiple instances of a TCP +server (with different ports). +
+ +For example, + +```python +ip = '0.0.0.0' +port = 5678 +mng = og.tcp.OptimizerTcpManager('python_build/the_optimizer', ip, port) +``` + +
+Connect to a remote optimizer by providing its IP and port. In that case we assume that an optimizer is up an running at some remote address and listens for connections at a certain port. In that case, we cannot -`start` the optimizer remotely using the TCP manager. +start the optimizer remotely using the TCP manager. +
+ + For example to connect to a *remote* TCP server at `10.8.0.7:5678`, we can create a TCP manager as follows: diff --git a/open-codegen/opengen/main.py b/open-codegen/opengen/main.py index 8762e04a..88e8de65 100644 --- a/open-codegen/opengen/main.py +++ b/open-codegen/opengen/main.py @@ -1,3 +1,4 @@ +import time import logging import casadi.casadi as cs import opengen as og @@ -14,25 +15,39 @@ .with_constraints(bounds) meta = og.config.OptimizerMeta() \ - .with_optimizer_name("halfspace_optimizer") + .with_optimizer_name("halfspace_optimizer") \ + .with_authors(["P. Sopasakis", "S. Author"]).with_version("0.1.56") tcp_config = og.config.TcpServerConfiguration(bind_port=3305) build_config = og.config.BuildConfiguration() \ .with_build_directory('my_optimizers') \ .with_build_mode(og.config.BuildConfiguration.DEBUG_MODE) \ - .with_open_version('0.7.1-alpha') \ .with_tcp_interface_config() builder = og.builder.OpEnOptimizerBuilder(problem, meta, build_config, og.config.SolverConfiguration()) -# builder.build() +builder.build() -mng = og.tcp.OptimizerTcpManager('my_optimizers/halfspace_optimizer', ip='0.0.0.0', port=3311) -mng.start() +all_managers = [] +for i in range(10): + all_managers += [og.tcp.OptimizerTcpManager( + optimizer_path='my_optimizers/halfspace_optimizer', + ip='0.0.0.0', + port=3311+i)] -print(mng.ping()) -resp = mng.call(p=[1., 2.]) -print(resp.get().solution) -mng.kill() +for m in all_managers: + m.start() + +time.sleep(4) + +for m in all_managers: + print(m.details) + resp = m.call(p=[1., 2.]) + print(resp.get().solution) + +# mng.kill() +time.sleep(6) +for m in all_managers: + m.kill() diff --git a/open-codegen/opengen/tcp/optimizer_tcp_manager.py b/open-codegen/opengen/tcp/optimizer_tcp_manager.py index bf729f70..05baa853 100644 --- a/open-codegen/opengen/tcp/optimizer_tcp_manager.py +++ b/open-codegen/opengen/tcp/optimizer_tcp_manager.py @@ -72,13 +72,14 @@ def __init__(self, optimizer_path=None, ip=None, port=None): logging.warn('the target optimizer was build with a different version of opengen (%s)' % opengen_version) logging.warn('you are running opengen version %s' % current_opengen_version) + logging.info("TCP/IP details: %s:%d", + self.__optimizer_details['tcp']['ip'], + self.__optimizer_details['tcp']['port']) + def __load_tcp_details(self): - logging.info("loading TCP/IP details") yaml_file = os.path.join(self.__optimizer_path, "optimizer.yml") with open(yaml_file, 'r') as stream: self.__optimizer_details = yaml.safe_load(stream) - details = self.__optimizer_details - logging.info("TCP/IP details: %s:%d", details['tcp']['ip'], details['tcp']['port']) @retry(tries=10, delay=1) def __obtain_socket_connection(self): @@ -122,6 +123,10 @@ def __check_if_server_is_running(self): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) return 0 == s.connect_ex((ip, port)) + @property + def details(self): + return self.__optimizer_details + def start(self): """Starts the TCP server @@ -167,7 +172,7 @@ def threaded_start(): # ping the server until it responds so that we know it's # up and running logging.info("Waiting for server to start") - time.sleep(2) + time.sleep(0.1) self.ping() def kill(self): diff --git a/open-codegen/opengen/templates/tcp/tcp_server.rs b/open-codegen/opengen/templates/tcp/tcp_server.rs index d672757d..c029126e 100644 --- a/open-codegen/opengen/templates/tcp/tcp_server.rs +++ b/open-codegen/opengen/templates/tcp/tcp_server.rs @@ -245,9 +245,9 @@ fn run_server(tcp_config: &TcpServerConfiguration) { } fn main() { - let matches = App::new("OpEn TCP Server [potato]") - .version("0.1.0") - .author("Pantelis Sopasakis") + let matches = App::new("OpEn TCP Server [{{meta.optimizer_name}}]") + .version("{{meta.version}}") + .author("{{meta.authors | join(', ')}}") .about("TCP interface of OpEn optimizer") .arg(Arg::with_name("ip") .short("ip") diff --git a/open-codegen/opengen/test/test.py b/open-codegen/opengen/test/test.py index 89216516..740b0834 100644 --- a/open-codegen/opengen/test/test.py +++ b/open-codegen/opengen/test/test.py @@ -2,7 +2,7 @@ import casadi.casadi as cs import opengen as og import subprocess - +import logging class RustBuildTestCase(unittest.TestCase): @@ -185,17 +185,11 @@ def setUpHalfspace(cls): @classmethod def setUpClass(cls): - print('---><---- Setting up: ROS package generation ----------><---') cls.setUpRosPackageGeneration() - print('---><---- Setting up: F1-only --------------------------><---') cls.setUpOnlyF1() - print('---><---- Setting up: F2-only --------------------------><---') cls.setUpOnlyF2() - print('---><---- Setting up: Simple problem -------------------><---') cls.setUpPlain() - print('---><---- Setting up: Parametric F2 --------------------><---') cls.setUpOnlyParametricF2() - print('---><---- Setting up: Halfspace ------------------------><---') cls.setUpHalfspace() def test_rectangle_empty(self): @@ -250,8 +244,31 @@ def test_solver_config_wrong_max_inner_iterations(self): with self.assertRaises(Exception) as __context: og.config.SolverConfiguration().with_max_inner_iterations() + def test_start_multiple_servers(self): + all_managers = [] + for i in range(50): + all_managers += [og.tcp.OptimizerTcpManager( + optimizer_path='my_optimizers/halfspace_optimizer', + ip='0.0.0.0', + port=15311+i)] + + # Start all servers + for m in all_managers: + m.start() + + # Ping all + for m in all_managers: + m.ping() + + # Kill all + for m in all_managers: + m.kill() + def test_rust_build_only_f1(self): - mng = og.tcp.OptimizerTcpManager(RustBuildTestCase.TEST_DIR + '/only_f1') + # Start the server using a custom bind IP and port + mng = og.tcp.OptimizerTcpManager(RustBuildTestCase.TEST_DIR + '/only_f1', + ip='0.0.0.0', + port=13757) mng.start() pong = mng.ping() # check if the server is alive self.assertEqual(1, pong["Pong"]) @@ -419,4 +436,5 @@ def test_c_bindings(self): if __name__ == '__main__': + logging.getLogger('retry').setLevel(logging.ERROR) unittest.main() From 8092e98eea1655add9cc441f7881df6ca6f21770 Mon Sep 17 00:00:00 2001 From: Pantelis Sopasakis Date: Thu, 10 Sep 2020 20:16:06 +0100 Subject: [PATCH 4/7] clippy in travis --- .travis.yml | 3 +++ ci/script.sh | 13 ++++++++++++ .../templates/c/optimizer_cinterface.rs | 21 ++++++++----------- open-codegen/opengen/templates/optimizer.rs | 17 +++++++-------- .../opengen/templates/tcp/tcp_server.rs | 6 +++--- 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08c04612..5bf44bee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,6 +75,9 @@ before_install: install: - bash ci/install.sh +before_script: + - rustup component add clippy + script: - bash ci/script.sh diff --git a/ci/script.sh b/ci/script.sh index bdd7f2aa..546e43a8 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -6,6 +6,9 @@ regular_test() { # ------------------------------------ cargo test + # Run Clippy + # ------------------------------------ + cargo clippy --all-targets --all-features # Run Python tests # ------------------------------------ @@ -29,6 +32,16 @@ regular_test() { export PYTHONPATH=. python -W ignore test/test_constraints.py -v python -W ignore test/test.py -v + + + # Run Clippy for generated optimizers + # ------------------------------------ + cd .python_test_build/only_f1/tcp_iface_only_f1 + cargo clippy --all-targets --all-features + cd ../../only_f2/tcp_iface_only_f2 + cargo clippy --all-targets --all-features + cd ../../rosenbrock_ros + cargo clippy --all-targets --all-features } test_docker() { diff --git a/open-codegen/opengen/templates/c/optimizer_cinterface.rs b/open-codegen/opengen/templates/c/optimizer_cinterface.rs index 27842c8d..0012a91f 100644 --- a/open-codegen/opengen/templates/c/optimizer_cinterface.rs +++ b/open-codegen/opengen/templates/c/optimizer_cinterface.rs @@ -98,9 +98,8 @@ pub extern "C" fn {{meta.optimizer_name|lower}}_new() -> *mut {{meta.optimizer_n /// Instance of `{{meta.optimizer_name}}SolverStatus`, with the solver status /// (e.g., number of inner/outer iterations, measures of accuracy, solver time, /// and the array of Lagrange multipliers at the solution). -/// #[no_mangle] -pub extern "C" fn {{meta.optimizer_name|lower}}_solve( +pub unsafe extern "C" fn {{meta.optimizer_name|lower}}_solve( instance: *mut {{meta.optimizer_name}}Cache, u: *mut c_double, params: *const c_double, @@ -109,19 +108,19 @@ pub extern "C" fn {{meta.optimizer_name|lower}}_solve( ) -> {{meta.optimizer_name}}SolverStatus { // Convert all pointers into the required data structures - let instance: &mut {{meta.optimizer_name}}Cache = unsafe { + let instance: &mut {{meta.optimizer_name}}Cache = { assert!(!instance.is_null()); &mut *instance }; // "*mut c_double" to "&mut [f64]" - let u : &mut [f64] = unsafe { + let u : &mut [f64] = { assert!(!u.is_null()); std::slice::from_raw_parts_mut(u as *mut f64, {{meta.optimizer_name|upper}}_NUM_DECISION_VARIABLES) }; // "*const c_double" to "&[f64]" - let params : &[f64] = unsafe { + let params : &[f64] = { assert!(!params.is_null()); std::slice::from_raw_parts(params as *const f64, {{meta.optimizer_name|upper}}_NUM_PARAMETERS) }; @@ -129,13 +128,13 @@ pub extern "C" fn {{meta.optimizer_name|lower}}_solve( let c0_option: Option = if c0.is_null() { None:: } else { - Some(unsafe { *c0 }) + Some(*c0) }; let y0_option: Option> = if y0.is_null() { None::> } else { - Some(unsafe { std::slice::from_raw_parts(y0 as *mut f64, {{meta.optimizer_name|upper}}_N1).to_vec() }) + Some(std::slice::from_raw_parts(y0 as *mut f64, {{meta.optimizer_name|upper}}_N1).to_vec()) }; // Invoke `solve` @@ -199,11 +198,9 @@ pub extern "C" fn {{meta.optimizer_name|lower}}_solve( /// Deallocate the solver's memory, which has been previously allocated /// using `{{meta.optimizer_name|lower}}_new` #[no_mangle] -pub extern "C" fn {{meta.optimizer_name|lower}}_free(instance: *mut {{meta.optimizer_name}}Cache) { +pub unsafe extern "C" fn {{meta.optimizer_name|lower}}_free(instance: *mut {{meta.optimizer_name}}Cache) { // Add impl - unsafe { - assert!(!instance.is_null()); - Box::from_raw(instance); - } + assert!(!instance.is_null()); + Box::from_raw(instance); } {% endif %} \ No newline at end of file diff --git a/open-codegen/opengen/templates/optimizer.rs b/open-codegen/opengen/templates/optimizer.rs index b54f1515..bdf748d7 100644 --- a/open-codegen/opengen/templates/optimizer.rs +++ b/open-codegen/opengen/templates/optimizer.rs @@ -5,7 +5,6 @@ // Generated at: {{timestamp_created}} // -use icasadi_{{meta.optimizer_name}}; {% if activate_clib_generation -%} use libc::{c_double, c_ulong, c_ulonglong}; {% endif %} @@ -133,30 +132,30 @@ const SET_Y_XMAX :Option<&[f64]> = {% if problem.alm_set_y.xmax is not none %}So fn make_constraints() -> impl Constraint { {% if 'Ball2' == problem.constraints.__class__.__name__ -%} // - Euclidean ball: - let bounds = Ball2::new(CONSTRAINTS_BALL_XC, CONSTRAINTS_BALL_RADIUS); + Ball2::new(CONSTRAINTS_BALL_XC, CONSTRAINTS_BALL_RADIUS) {% elif 'BallInf' == problem.constraints.__class__.__name__ -%} // - Infinity ball: - let bounds = BallInf::new(CONSTRAINTS_BALL_XC, CONSTRAINTS_BALL_RADIUS); + BallInf::new(CONSTRAINTS_BALL_XC, CONSTRAINTS_BALL_RADIUS) {% elif 'Rectangle' == problem.constraints.__class__.__name__ -%} // - Rectangle: - let bounds = Rectangle::new(CONSTRAINTS_XMIN, CONSTRAINTS_XMAX); + Rectangle::new(CONSTRAINTS_XMIN, CONSTRAINTS_XMAX) {% elif 'FiniteSet' == problem.constraints.__class__.__name__ -%} // - Finite Set: let data: &[&[f64]] = &[ {% for point in problem.constraints.points %}&[{{point|join(', ')}}],{% endfor %} ]; - let bounds = FiniteSet::new(data); + FiniteSet::new(data) {% elif 'Halfspace' == problem.constraints.__class__.__name__ -%} // - Halfspace: let offset: f64 = {{problem.constraints.offset}}; let normal_vector: &[f64] = &[{{problem.constraints.normal_vector | join(', ')}}]; - let bounds = Halfspace::new(&normal_vector, offset); + Halfspace::new(&normal_vector, offset) {% elif 'NoConstraints' == problem.constraints.__class__.__name__ -%} // - No constraints (whole Rn): - let bounds = NoConstraints::new(); + NoConstraints::new() {% elif 'Zero' == problem.constraints.__class__.__name__ -%} // - Zero! - let bounds = Zero::new(); + Zero::new() {% elif 'CartesianProduct' == problem.constraints.__class__.__name__ -%} // - Cartesian product of constraints: let bounds = CartesianProduct::new(); @@ -198,9 +197,9 @@ fn make_constraints() -> impl Constraint { {% elif 'Zero' == set_i.__class__.__name__ -%} let bounds = bounds.add_constraint(idx_{{loop.index}}, Zero::new()); {% endif -%} + bounds {% endfor %} {% endif -%} - bounds } {% if problem.alm_set_c is not none -%} diff --git a/open-codegen/opengen/templates/tcp/tcp_server.rs b/open-codegen/opengen/templates/tcp/tcp_server.rs index c029126e..6bb06db7 100644 --- a/open-codegen/opengen/templates/tcp/tcp_server.rs +++ b/open-codegen/opengen/templates/tcp/tcp_server.rs @@ -87,7 +87,7 @@ fn pong(stream: &mut std::net::TcpStream, code: i32) { code ); stream - .write(error_message.as_bytes()) + .write_all(error_message.as_bytes()) .expect("cannot write to stream"); } @@ -100,7 +100,7 @@ fn write_error_message(stream: &mut std::net::TcpStream, code: i32, error_msg: & ); warn!("Invalid request {:?}", code); stream - .write(error_message.as_bytes()) + .write_all(error_message.as_bytes()) .expect("cannot write to stream"); } @@ -128,7 +128,7 @@ fn return_solution_to_client( }; let solution_json = serde_json::to_string_pretty(&solution).unwrap(); stream - .write(solution_json.as_bytes()) + .write_all(solution_json.as_bytes()) .expect("cannot write to stream"); } From 5522d918220c00dcdb95e25bffa1e3df7dddd5a6 Mon Sep 17 00:00:00 2001 From: Pantelis Sopasakis Date: Thu, 10 Sep 2020 20:18:23 +0100 Subject: [PATCH 5/7] fixed issue in test.py --- open-codegen/opengen/test/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open-codegen/opengen/test/test.py b/open-codegen/opengen/test/test.py index 740b0834..ef56f523 100644 --- a/open-codegen/opengen/test/test.py +++ b/open-codegen/opengen/test/test.py @@ -248,7 +248,7 @@ def test_start_multiple_servers(self): all_managers = [] for i in range(50): all_managers += [og.tcp.OptimizerTcpManager( - optimizer_path='my_optimizers/halfspace_optimizer', + optimizer_path=RustBuildTestCase.TEST_DIR + '/only_f1', ip='0.0.0.0', port=15311+i)] From be74217d714127a30cfb6e961cb569f1d109d790 Mon Sep 17 00:00:00 2001 From: Pantelis Sopasakis Date: Thu, 10 Sep 2020 20:47:43 +0100 Subject: [PATCH 6/7] more clippy-ing --- .../templates/c/optimizer_cinterface.rs | 6 ++--- open-codegen/opengen/templates/optimizer.rs | 27 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/open-codegen/opengen/templates/c/optimizer_cinterface.rs b/open-codegen/opengen/templates/c/optimizer_cinterface.rs index 0012a91f..8992610a 100644 --- a/open-codegen/opengen/templates/c/optimizer_cinterface.rs +++ b/open-codegen/opengen/templates/c/optimizer_cinterface.rs @@ -163,14 +163,14 @@ pub unsafe extern "C" fn {{meta.optimizer_name|lower}}_solve( y_array.copy_from_slice(&y); y_array {% else %} - 0 as *const c_double + std::ptr::null::() {% endif %} }, None => { {%- if problem.dim_constraints_aug_lagrangian() > 0 %} [0.0; {{meta.optimizer_name|upper}}_N1] {% else %} - 0 as *const c_double + std::ptr::null::() {% endif -%} } } @@ -190,7 +190,7 @@ pub unsafe extern "C" fn {{meta.optimizer_name|lower}}_solve( cost: std::f64::INFINITY as c_double, lagrange: {%- if problem.dim_constraints_aug_lagrangian() > 0 -%} [0.0; {{meta.optimizer_name|upper}}_N1] - {%- else -%}0 as *const c_double{%- endif %} + {%- else -%}std::ptr::null::(){%- endif %} }, } } diff --git a/open-codegen/opengen/templates/optimizer.rs b/open-codegen/opengen/templates/optimizer.rs index bdf748d7..77b2149a 100644 --- a/open-codegen/opengen/templates/optimizer.rs +++ b/open-codegen/opengen/templates/optimizer.rs @@ -206,15 +206,15 @@ fn make_constraints() -> impl Constraint { /// Make set C fn make_set_c() -> impl Constraint { {% if 'Ball2' == problem.alm_set_c.__class__.__name__ -%} - let set_c = Ball2::new(SET_C_BALL_XC, SET_C_BALL_RADIUS); + Ball2::new(SET_C_BALL_XC, SET_C_BALL_RADIUS) {% elif 'BallInf' == problem.alm_set_c.__class__.__name__ -%} - let set_c = BallInf::new(SET_C_BALL_XC, SET_C_BALL_RADIUS); + BallInf::new(SET_C_BALL_XC, SET_C_BALL_RADIUS) {% elif 'Rectangle' == problem.alm_set_c.__class__.__name__ -%} - let set_c = Rectangle::new(SET_C_XMIN, SET_C_XMAX); + Rectangle::new(SET_C_XMIN, SET_C_XMAX) {% elif 'NoConstraints' == problem.alm_set_c.__class__.__name__ -%} - let set_c = NoConstraints::new(); + NoConstraints::new() {% elif 'Zero' == problem.alm_set_c.__class__.__name__ -%} - let set_c = Zero::new(); + Zero::new() {% elif 'CartesianProduct' == problem.alm_set_c.__class__.__name__ -%} // Cartesian product of constraints (Set C) let set_c = CartesianProduct::new(); @@ -254,8 +254,8 @@ fn make_set_c() -> impl Constraint { let set_c = set_c.add_constraint(idx_{{loop.index}}, Zero::new()); {% endif -%} {% endfor %} - {% endif -%} set_c + {% endif -%} } {% endif %} @@ -263,17 +263,16 @@ fn make_set_c() -> impl Constraint { /// Make set Y fn make_set_y() -> impl Constraint { {% if 'Ball2' == problem.alm_set_y.__class__.__name__ -%} - let set_y = Ball2::new(SET_Y_BALL_XC, SET_Y_BALL_RADIUS); + Ball2::new(SET_Y_BALL_XC, SET_Y_BALL_RADIUS) {% elif 'BallInf' == problem.alm_set_y.__class__.__name__ -%} - let set_y = BallInf::new(SET_Y_BALL_XC, SET_Y_BALL_RADIUS); + BallInf::new(SET_Y_BALL_XC, SET_Y_BALL_RADIUS) {% elif 'Rectangle' == problem.alm_set_y.__class__.__name__ -%} - let set_y = Rectangle::new(SET_Y_XMIN, SET_Y_XMAX); + Rectangle::new(SET_Y_XMIN, SET_Y_XMAX) {% elif 'NoConstraints' == problem.alm_set_y.__class__.__name__ -%} - let set_y = NoConstraints::new(); + NoConstraints::new() {% elif 'Zero' == problem.alm_set_y.__class__.__name__ -%} - let set_c = Zero::new(); + Zero::new() {% endif -%} - set_y } {% endif %} @@ -286,9 +285,7 @@ pub fn initialize_solver() -> AlmCache { {% if solver_config.cbfgs_alpha is not none and solver_config.cbfgs_epsilon is not none -%} let panoc_cache = panoc_cache.with_cbfgs_parameters({{solver_config.cbfgs_alpha}}, {{solver_config.cbfgs_epsilon}}, {{solver_config.cbfgs_sy_epsilon}}); {% endif -%} - let alm_cache = AlmCache::new(panoc_cache, {{meta.optimizer_name|upper}}_N1, {{meta.optimizer_name|upper}}_N2); - - alm_cache + AlmCache::new(panoc_cache, {{meta.optimizer_name|upper}}_N1, {{meta.optimizer_name|upper}}_N2) } From 8ece43e938bb29b2ac244f9c3acf4ddf80b0f810 Mon Sep 17 00:00:00 2001 From: Pantelis Sopasakis Date: Thu, 10 Sep 2020 21:10:33 +0100 Subject: [PATCH 7/7] updated changelog and version set zip_safe flag to False --- open-codegen/CHANGELOG.md | 8 ++++++++ open-codegen/VERSION | 2 +- open-codegen/opengen/test/test.py | 1 + open-codegen/setup.py | 3 ++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/open-codegen/CHANGELOG.md b/open-codegen/CHANGELOG.md index e4543721..a6ee4c1d 100644 --- a/open-codegen/CHANGELOG.md +++ b/open-codegen/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Note: This is the Changelog file of `opengen` - the Python interface of OpEn +## [0.6.1] - 2020-09-10 + +### Changed + +* `OptimizerTcpManager`: ip and port can be set dynamically + + ## [0.6.0] - 2020-09-03 ### Added @@ -55,6 +62,7 @@ Note: This is the Changelog file of `opengen` - the Python interface of OpEn * Fixed `lbfgs` typo +[0.6.1]: https://github.com/alphaville/optimization-engine/compare/opengen-v0.6.0...opengen-0.6.1 [0.6.0]: https://github.com/alphaville/optimization-engine/compare/opengen-v0.5.0...opengen-0.6.0 [0.5.0]: https://github.com/alphaville/optimization-engine/compare/opengen-0.4.1...opengen-v0.5.0 [0.4.1]: https://github.com/alphaville/optimization-engine/compare/opengen-0.4.1...master diff --git a/open-codegen/VERSION b/open-codegen/VERSION index a918a2aa..ee6cdce3 100644 --- a/open-codegen/VERSION +++ b/open-codegen/VERSION @@ -1 +1 @@ -0.6.0 +0.6.1 diff --git a/open-codegen/opengen/test/test.py b/open-codegen/opengen/test/test.py index ef56f523..c9230163 100644 --- a/open-codegen/opengen/test/test.py +++ b/open-codegen/opengen/test/test.py @@ -4,6 +4,7 @@ import subprocess import logging + class RustBuildTestCase(unittest.TestCase): TEST_DIR = ".python_test_build" diff --git a/open-codegen/setup.py b/open-codegen/setup.py index 13574a23..aee0accd 100755 --- a/open-codegen/setup.py +++ b/open-codegen/setup.py @@ -53,4 +53,5 @@ keywords=['optimization', 'nonconvex', 'embedded'], url=( 'https://github.com/alphaville/optimization-engine' - )) + ), + zip_safe=False)