roby 0.7
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.
- data/.gitignore +29 -0
- data/History.txt +4 -0
- data/License-fr.txt +519 -0
- data/License.txt +515 -0
- data/Manifest.txt +245 -0
- data/NOTES +4 -0
- data/README.txt +163 -0
- data/Rakefile +161 -0
- data/TODO.txt +146 -0
- data/app/README.txt +24 -0
- data/app/Rakefile +8 -0
- data/app/config/ROBOT.rb +5 -0
- data/app/config/app.yml +91 -0
- data/app/config/init.rb +7 -0
- data/app/config/roby.yml +3 -0
- data/app/controllers/.gitattributes +0 -0
- data/app/controllers/ROBOT.rb +2 -0
- data/app/data/.gitattributes +0 -0
- data/app/planners/ROBOT/main.rb +6 -0
- data/app/planners/main.rb +5 -0
- data/app/scripts/distributed +3 -0
- data/app/scripts/generate/bookmarks +3 -0
- data/app/scripts/replay +3 -0
- data/app/scripts/results +3 -0
- data/app/scripts/run +3 -0
- data/app/scripts/server +3 -0
- data/app/scripts/shell +3 -0
- data/app/scripts/test +3 -0
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby +210 -0
- data/bin/roby-log +168 -0
- data/bin/roby-shell +25 -0
- data/doc/images/event_generalization.png +0 -0
- data/doc/images/exception_propagation_1.png +0 -0
- data/doc/images/exception_propagation_2.png +0 -0
- data/doc/images/exception_propagation_3.png +0 -0
- data/doc/images/exception_propagation_4.png +0 -0
- data/doc/images/exception_propagation_5.png +0 -0
- data/doc/images/replay_handler_error.png +0 -0
- data/doc/images/replay_handler_error_0.png +0 -0
- data/doc/images/replay_handler_error_1.png +0 -0
- data/doc/images/roby_cycle_overview.png +0 -0
- data/doc/images/roby_replay_02.png +0 -0
- data/doc/images/roby_replay_03.png +0 -0
- data/doc/images/roby_replay_04.png +0 -0
- data/doc/images/roby_replay_event_representation.png +0 -0
- data/doc/images/roby_replay_first_state.png +0 -0
- data/doc/images/roby_replay_relations.png +0 -0
- data/doc/images/roby_replay_startup.png +0 -0
- data/doc/images/task_event_generalization.png +0 -0
- data/doc/papers.rdoc +11 -0
- data/doc/styles/allison.css +314 -0
- data/doc/styles/allison.js +316 -0
- data/doc/styles/allison.rb +276 -0
- data/doc/styles/jamis.rb +593 -0
- data/doc/tutorials/01-GettingStarted.rdoc +86 -0
- data/doc/tutorials/02-GoForward.rdoc +220 -0
- data/doc/tutorials/03-PlannedPath.rdoc +268 -0
- data/doc/tutorials/04-EventPropagation.rdoc +236 -0
- data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
- data/doc/tutorials/06-Overview.rdoc +40 -0
- data/doc/videos.rdoc +69 -0
- data/ext/droby/dump.cc +175 -0
- data/ext/droby/extconf.rb +3 -0
- data/ext/graph/algorithm.cc +746 -0
- data/ext/graph/extconf.rb +7 -0
- data/ext/graph/graph.cc +529 -0
- data/ext/graph/graph.hh +183 -0
- data/ext/graph/iterator_sequence.hh +102 -0
- data/ext/graph/undirected_dfs.hh +226 -0
- data/ext/graph/undirected_graph.hh +421 -0
- data/lib/roby.rb +41 -0
- data/lib/roby/app.rb +870 -0
- data/lib/roby/app/rake.rb +56 -0
- data/lib/roby/app/run.rb +14 -0
- data/lib/roby/app/scripts/distributed.rb +13 -0
- data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
- data/lib/roby/app/scripts/replay.rb +31 -0
- data/lib/roby/app/scripts/results.rb +15 -0
- data/lib/roby/app/scripts/run.rb +26 -0
- data/lib/roby/app/scripts/server.rb +18 -0
- data/lib/roby/app/scripts/shell.rb +88 -0
- data/lib/roby/app/scripts/test.rb +40 -0
- data/lib/roby/basic_object.rb +151 -0
- data/lib/roby/config.rb +5 -0
- data/lib/roby/control.rb +747 -0
- data/lib/roby/decision_control.rb +17 -0
- data/lib/roby/distributed.rb +32 -0
- data/lib/roby/distributed/base.rb +440 -0
- data/lib/roby/distributed/communication.rb +871 -0
- data/lib/roby/distributed/connection_space.rb +592 -0
- data/lib/roby/distributed/distributed_object.rb +206 -0
- data/lib/roby/distributed/drb.rb +62 -0
- data/lib/roby/distributed/notifications.rb +539 -0
- data/lib/roby/distributed/peer.rb +550 -0
- data/lib/roby/distributed/protocol.rb +529 -0
- data/lib/roby/distributed/proxy.rb +343 -0
- data/lib/roby/distributed/subscription.rb +311 -0
- data/lib/roby/distributed/transaction.rb +498 -0
- data/lib/roby/event.rb +897 -0
- data/lib/roby/exceptions.rb +234 -0
- data/lib/roby/executives/simple.rb +30 -0
- data/lib/roby/graph.rb +166 -0
- data/lib/roby/interface.rb +390 -0
- data/lib/roby/log.rb +3 -0
- data/lib/roby/log/chronicle.rb +303 -0
- data/lib/roby/log/console.rb +72 -0
- data/lib/roby/log/data_stream.rb +197 -0
- data/lib/roby/log/dot.rb +279 -0
- data/lib/roby/log/event_stream.rb +151 -0
- data/lib/roby/log/file.rb +340 -0
- data/lib/roby/log/gui/basic_display.ui +83 -0
- data/lib/roby/log/gui/chronicle.rb +26 -0
- data/lib/roby/log/gui/chronicle_view.rb +40 -0
- data/lib/roby/log/gui/chronicle_view.ui +70 -0
- data/lib/roby/log/gui/data_displays.rb +172 -0
- data/lib/roby/log/gui/data_displays.ui +155 -0
- data/lib/roby/log/gui/notifications.rb +26 -0
- data/lib/roby/log/gui/relations.rb +248 -0
- data/lib/roby/log/gui/relations.ui +123 -0
- data/lib/roby/log/gui/relations_view.rb +185 -0
- data/lib/roby/log/gui/relations_view.ui +149 -0
- data/lib/roby/log/gui/replay.rb +327 -0
- data/lib/roby/log/gui/replay_controls.rb +200 -0
- data/lib/roby/log/gui/replay_controls.ui +259 -0
- data/lib/roby/log/gui/runtime.rb +130 -0
- data/lib/roby/log/hooks.rb +185 -0
- data/lib/roby/log/logger.rb +202 -0
- data/lib/roby/log/notifications.rb +244 -0
- data/lib/roby/log/plan_rebuilder.rb +470 -0
- data/lib/roby/log/relations.rb +1056 -0
- data/lib/roby/log/server.rb +550 -0
- data/lib/roby/log/sqlite.rb +47 -0
- data/lib/roby/log/timings.rb +164 -0
- data/lib/roby/plan-object.rb +247 -0
- data/lib/roby/plan.rb +762 -0
- data/lib/roby/planning.rb +13 -0
- data/lib/roby/planning/loops.rb +302 -0
- data/lib/roby/planning/model.rb +906 -0
- data/lib/roby/planning/task.rb +151 -0
- data/lib/roby/propagation.rb +562 -0
- data/lib/roby/query.rb +619 -0
- data/lib/roby/relations.rb +583 -0
- data/lib/roby/relations/conflicts.rb +70 -0
- data/lib/roby/relations/ensured.rb +20 -0
- data/lib/roby/relations/error_handling.rb +23 -0
- data/lib/roby/relations/events.rb +9 -0
- data/lib/roby/relations/executed_by.rb +193 -0
- data/lib/roby/relations/hierarchy.rb +239 -0
- data/lib/roby/relations/influence.rb +10 -0
- data/lib/roby/relations/planned_by.rb +63 -0
- data/lib/roby/robot.rb +7 -0
- data/lib/roby/standard_errors.rb +218 -0
- data/lib/roby/state.rb +5 -0
- data/lib/roby/state/events.rb +221 -0
- data/lib/roby/state/information.rb +55 -0
- data/lib/roby/state/pos.rb +110 -0
- data/lib/roby/state/shapes.rb +32 -0
- data/lib/roby/state/state.rb +353 -0
- data/lib/roby/support.rb +92 -0
- data/lib/roby/task-operations.rb +182 -0
- data/lib/roby/task.rb +1618 -0
- data/lib/roby/test/common.rb +399 -0
- data/lib/roby/test/distributed.rb +214 -0
- data/lib/roby/test/tasks/empty_task.rb +9 -0
- data/lib/roby/test/tasks/goto.rb +36 -0
- data/lib/roby/test/tasks/simple_task.rb +23 -0
- data/lib/roby/test/testcase.rb +519 -0
- data/lib/roby/test/tools.rb +160 -0
- data/lib/roby/thread_task.rb +87 -0
- data/lib/roby/transactions.rb +462 -0
- data/lib/roby/transactions/proxy.rb +292 -0
- data/lib/roby/transactions/updates.rb +139 -0
- data/plugins/fault_injection/History.txt +4 -0
- data/plugins/fault_injection/README.txt +37 -0
- data/plugins/fault_injection/Rakefile +18 -0
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +52 -0
- data/plugins/fault_injection/fault_injection.rb +89 -0
- data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
- data/plugins/subsystems/README.txt +40 -0
- data/plugins/subsystems/Rakefile +18 -0
- data/plugins/subsystems/app.rb +171 -0
- data/plugins/subsystems/test/app/README +24 -0
- data/plugins/subsystems/test/app/Rakefile +8 -0
- data/plugins/subsystems/test/app/config/app.yml +71 -0
- data/plugins/subsystems/test/app/config/init.rb +9 -0
- data/plugins/subsystems/test/app/config/roby.yml +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +20 -0
- data/plugins/subsystems/test/app/scripts/distributed +3 -0
- data/plugins/subsystems/test/app/scripts/replay +3 -0
- data/plugins/subsystems/test/app/scripts/results +3 -0
- data/plugins/subsystems/test/app/scripts/run +3 -0
- data/plugins/subsystems/test/app/scripts/server +3 -0
- data/plugins/subsystems/test/app/scripts/shell +3 -0
- data/plugins/subsystems/test/app/scripts/test +3 -0
- data/plugins/subsystems/test/app/tasks/services.rb +15 -0
- data/plugins/subsystems/test/test_subsystems.rb +71 -0
- data/test/distributed/test_communication.rb +178 -0
- data/test/distributed/test_connection.rb +282 -0
- data/test/distributed/test_execution.rb +373 -0
- data/test/distributed/test_mixed_plan.rb +341 -0
- data/test/distributed/test_plan_notifications.rb +238 -0
- data/test/distributed/test_protocol.rb +516 -0
- data/test/distributed/test_query.rb +102 -0
- data/test/distributed/test_remote_plan.rb +491 -0
- data/test/distributed/test_transaction.rb +463 -0
- data/test/mockups/tasks.rb +27 -0
- data/test/planning/test_loops.rb +380 -0
- data/test/planning/test_model.rb +427 -0
- data/test/planning/test_task.rb +106 -0
- data/test/relations/test_conflicts.rb +42 -0
- data/test/relations/test_ensured.rb +38 -0
- data/test/relations/test_executed_by.rb +149 -0
- data/test/relations/test_hierarchy.rb +158 -0
- data/test/relations/test_planned_by.rb +54 -0
- data/test/suite_core.rb +24 -0
- data/test/suite_distributed.rb +9 -0
- data/test/suite_planning.rb +3 -0
- data/test/suite_relations.rb +8 -0
- data/test/test_bgl.rb +508 -0
- data/test/test_control.rb +399 -0
- data/test/test_event.rb +894 -0
- data/test/test_exceptions.rb +592 -0
- data/test/test_interface.rb +37 -0
- data/test/test_log.rb +114 -0
- data/test/test_log_server.rb +132 -0
- data/test/test_plan.rb +584 -0
- data/test/test_propagation.rb +210 -0
- data/test/test_query.rb +266 -0
- data/test/test_relations.rb +180 -0
- data/test/test_state.rb +414 -0
- data/test/test_support.rb +16 -0
- data/test/test_task.rb +938 -0
- data/test/test_testcase.rb +122 -0
- data/test/test_thread_task.rb +73 -0
- data/test/test_transactions.rb +569 -0
- data/test/test_transactions_proxy.rb +198 -0
- metadata +570 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{Previous tutorial}[link:files/doc/tutorials/05-ErrorHandling_rdoc.html]
|
|
2
|
+
= Overview of other Roby features
|
|
3
|
+
|
|
4
|
+
=== Transactions
|
|
5
|
+
|
|
6
|
+
Transactions are a central tool for plan modification in Roby. They are a
|
|
7
|
+
representation of a plan _modification_, i.e. of the changes that are necessary
|
|
8
|
+
to bring the plan which is being executed in a new, desired state. It is
|
|
9
|
+
therefore a safe tool for asynchronous plan modification and execution:
|
|
10
|
+
|
|
11
|
+
* the decision tools can build a new plan without taking into account the
|
|
12
|
+
changes brought by execution
|
|
13
|
+
* the plan execution can safely assess if the new plans are still compatible
|
|
14
|
+
with the built transactions, and refuse applying a transaction if it is not
|
|
15
|
+
safe.
|
|
16
|
+
|
|
17
|
+
Ideally, a cooperation protocol should be implemented to properly handle
|
|
18
|
+
conflicting situations (where the transaction cannot be applied). For now, only
|
|
19
|
+
very crude means are used. This is one of the future evolutions of Roby.
|
|
20
|
+
|
|
21
|
+
Please refer to my PhD thesis for a more extensive presentation of this feature.
|
|
22
|
+
|
|
23
|
+
=== Multi-robot execution
|
|
24
|
+
|
|
25
|
+
All models presented here are valid in multi-robot systems. In particular, the
|
|
26
|
+
transactions tool allow to build cooperatively a new plan (something which may
|
|
27
|
+
take time) and apply the plan when a common ground has been found by all the
|
|
28
|
+
present systems.
|
|
29
|
+
|
|
30
|
+
The communication layer, as it is implemented for now, is only valid for
|
|
31
|
+
bi-robot systems. One of its most interesting features is that it does not take
|
|
32
|
+
the presence of a communication link for granted. It is possible for the two
|
|
33
|
+
robots to execute the parts of the plan that do not need communication, and to
|
|
34
|
+
diagnose the loss of communication (for instance, deciding that the joint plan
|
|
35
|
+
is not valid anymore after a given timeout).
|
|
36
|
+
|
|
37
|
+
The communication layer extension to a true multi-robot setup is also for a
|
|
38
|
+
future evolution of Roby. It can be easily be done to a certain extent.
|
|
39
|
+
Reaching a full multi-robot plan execution would take more time.
|
|
40
|
+
|
data/doc/videos.rdoc
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
= Videos
|
|
2
|
+
|
|
3
|
+
== Normal operations
|
|
4
|
+
|
|
5
|
+
Those videos don't show what Roby itself does, only results showing
|
|
6
|
+
robots (real and in simulation) acting as they are controlled by a
|
|
7
|
+
Roby application.
|
|
8
|
+
|
|
9
|
+
=== Single robot navigation
|
|
10
|
+
|
|
11
|
+
http://roby.rubyforge.org/videos/perception_loops.avi
|
|
12
|
+
|
|
13
|
+
What we see in this video is the perception loop (whose visible part is the DEM
|
|
14
|
+
building) being handled by Roby. The perception updates are triggered by Roby
|
|
15
|
+
on the basis of state events: they are triggered by the translation of the
|
|
16
|
+
robot and the change of heading, to reduce the number of times it is actually done.
|
|
17
|
+
|
|
18
|
+
=== Bi-robot navigation
|
|
19
|
+
|
|
20
|
+
http://roby.rubyforge.org/videos/birobot.avi
|
|
21
|
+
|
|
22
|
+
What we see in this video is the cooperation between a rover and an UAV for a
|
|
23
|
+
navigation task. Both robots, as well as the common part of their joint plan is
|
|
24
|
+
written in and managed by Roby. Including part of the data transfer process.
|
|
25
|
+
This bi-robot setup has also successfully been tested on real robots, with an
|
|
26
|
+
iRobot ATRV and a Yamaha RMAX helicopter.
|
|
27
|
+
|
|
28
|
+
In this video, the rover plans its path (the line on the ground) in a
|
|
29
|
+
traversability map (red/green map: red is non-traversable, green is
|
|
30
|
+
traversable). It also generates a set of regions of interest for him. Those
|
|
31
|
+
regions are then considered by the UAV which decides how it will schedule its
|
|
32
|
+
own perception. When the UAV does have perceived a zone, it informs the rover
|
|
33
|
+
and sends it the relevant data. The rover can then update its own map.
|
|
34
|
+
|
|
35
|
+
== Error handling
|
|
36
|
+
=== Rflex repaired
|
|
37
|
+
|
|
38
|
+
http://roby.rubyforge.org/videos/rflex_repaired.avi
|
|
39
|
+
|
|
40
|
+
This is a simple example of asynchronous repairs. In this video, the
|
|
41
|
+
microcontroller which drives the robot's motors can give us spurious
|
|
42
|
+
<tt>BRAKES_ON</tt> messages. Our problem is that the Roby controller must
|
|
43
|
+
determine if the message is spurious, or if brakes are actually set by the means
|
|
44
|
+
of an emergency switch for instance. To do that, an error handling is set up,
|
|
45
|
+
which wait for a few seconds and tests the <tt>BRAKES_ON</tt> state of the
|
|
46
|
+
robot. If the brakes are reported as off, then the robot can start moving again.
|
|
47
|
+
Otherwise, the error was a rightful one and should be handled by other means.
|
|
48
|
+
|
|
49
|
+
=== P3d repaired
|
|
50
|
+
|
|
51
|
+
http://roby.rubyforge.org/videos/p3d_repaired.avi.
|
|
52
|
+
|
|
53
|
+
In this video, the system handles a problem with DEM generation ("DEM" means
|
|
54
|
+
"Digital Elevation Map". It is a representation of the terrain the robot is on).
|
|
55
|
+
Due to localization issues, it is possible to have a very bad DEM in which the
|
|
56
|
+
robot cannot move. If that happens, the locomotion activity (P3d::Track) emits
|
|
57
|
+
the +blocked+ event. Our way to handle it in three steps:
|
|
58
|
+
1. a new DEM perception is done. As the robot is not moving, it should give a
|
|
59
|
+
better result. This is called the "Stage 1 handler" in the video.
|
|
60
|
+
2. if the robot is still blocked, the "Stage 2 handler" completely reinitializes
|
|
61
|
+
the DEM and do a local update.
|
|
62
|
+
3. if the fault remains, the problem does not lie in the DEM perception process,
|
|
63
|
+
but in the fact that the robot is actually blocked. The error must therefore be
|
|
64
|
+
handled by other means.
|
|
65
|
+
|
|
66
|
+
At all times, if the robot moves more than a given threshold, the problem was
|
|
67
|
+
actually the DEM perception process and the error handler is reset at the first
|
|
68
|
+
stage for following operations.
|
|
69
|
+
|
data/ext/droby/dump.cc
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <intern.h>
|
|
3
|
+
#include <st.h>
|
|
4
|
+
#include <set>
|
|
5
|
+
|
|
6
|
+
static VALUE mRoby;
|
|
7
|
+
static VALUE mRobyDistributed;
|
|
8
|
+
static VALUE cDRbObject;
|
|
9
|
+
static VALUE cSet;
|
|
10
|
+
static VALUE cValueSet;
|
|
11
|
+
static ID id_droby_dump;
|
|
12
|
+
static ID id_remote_id;
|
|
13
|
+
static ID id_append;
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* Document-class: Roby::Distributed
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/* call-seq:
|
|
20
|
+
* format(object, peer) => formatted_object
|
|
21
|
+
*
|
|
22
|
+
* Formats +object+ so that it is ready to be dumped by Marshal.dump for
|
|
23
|
+
* sending to +peer+. This means that if the object has a droby_dump method, it
|
|
24
|
+
* is called to get a marshallable object which represents +object+. Moreover,
|
|
25
|
+
* if +peer+ responds to #incremental_dump?(object), this is called to
|
|
26
|
+
* determine wether a full dump is required or if sending a
|
|
27
|
+
* Roby::Distributed::RemoteID for remote reference is enough.
|
|
28
|
+
*
|
|
29
|
+
* If the object is not a DRbObject and does not define a #droby_dump method,
|
|
30
|
+
* it is proxied through a DRbObject if it present in
|
|
31
|
+
* Distributed.allow_remote_access. Otherwise, we will try to dump it as-is.
|
|
32
|
+
*/
|
|
33
|
+
static VALUE droby_format(int argc, VALUE* argv, VALUE self)
|
|
34
|
+
{
|
|
35
|
+
VALUE object, destination;
|
|
36
|
+
rb_scan_args(argc, argv, "11", &object, &destination);
|
|
37
|
+
|
|
38
|
+
if (RTEST(rb_obj_is_kind_of(object, cDRbObject)))
|
|
39
|
+
return object;
|
|
40
|
+
|
|
41
|
+
if (RTEST(rb_respond_to(object, id_droby_dump)))
|
|
42
|
+
{
|
|
43
|
+
if (!NIL_P(destination) && RTEST(rb_funcall(destination, rb_intern("incremental_dump?"), 1, object)))
|
|
44
|
+
return rb_funcall(object, id_remote_id, 0);
|
|
45
|
+
return rb_funcall(object, id_droby_dump, 1, destination);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
VALUE remote_access = rb_iv_get(self, "@allowed_remote_access");
|
|
49
|
+
int i;
|
|
50
|
+
for (i = 0; i < RARRAY(remote_access)->len; ++i)
|
|
51
|
+
{
|
|
52
|
+
if (rb_obj_is_kind_of(object, RARRAY(remote_access)->ptr[i]))
|
|
53
|
+
return rb_class_new_instance(1, &object, cDRbObject);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return object;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
typedef struct DROBY_DUMP_ITERATION_ARG
|
|
60
|
+
{
|
|
61
|
+
VALUE result;
|
|
62
|
+
VALUE dest;
|
|
63
|
+
} DROBY_DUMP_ITERATION_ARG;
|
|
64
|
+
|
|
65
|
+
static VALUE array_dump_element(VALUE element, DROBY_DUMP_ITERATION_ARG* arg)
|
|
66
|
+
{
|
|
67
|
+
VALUE args[2] = { element, arg->dest };
|
|
68
|
+
rb_ary_push(arg->result, droby_format(2, args, mRobyDistributed));
|
|
69
|
+
return Qnil;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// call-seq:
|
|
73
|
+
// droby_dump(dest) => dumped_array
|
|
74
|
+
//
|
|
75
|
+
// Creates a copy of this Array with all its values formatted for marshalling
|
|
76
|
+
// using Distributed.format.
|
|
77
|
+
static VALUE array_droby_dump(VALUE self, VALUE dest)
|
|
78
|
+
{
|
|
79
|
+
VALUE result = rb_ary_new();
|
|
80
|
+
struct RArray* array = RARRAY(self);
|
|
81
|
+
int i;
|
|
82
|
+
|
|
83
|
+
VALUE el[2] = { Qnil, dest };
|
|
84
|
+
for (i = 0; i < array->len; ++i)
|
|
85
|
+
{
|
|
86
|
+
el[0] = array->ptr[i];
|
|
87
|
+
rb_ary_push(result, droby_format(2, el, mRobyDistributed));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static int hash_dump_element(VALUE key, VALUE value, DROBY_DUMP_ITERATION_ARG* arg)
|
|
94
|
+
{
|
|
95
|
+
VALUE args_key[2] = { key, arg->dest };
|
|
96
|
+
key = droby_format(2, args_key, mRobyDistributed);
|
|
97
|
+
VALUE args_value[2] = { value, arg->dest };
|
|
98
|
+
value = droby_format(2, args_value, mRobyDistributed);
|
|
99
|
+
rb_hash_aset(arg->result, key, value);
|
|
100
|
+
return ST_CONTINUE;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// call-seq:
|
|
104
|
+
// droby_dump => dumped_hash
|
|
105
|
+
//
|
|
106
|
+
// Creates a copy of this Hash with all its values formatted for marshalling
|
|
107
|
+
// using Distributed.format. The keys are not modified.
|
|
108
|
+
static VALUE hash_droby_dump(VALUE self, VALUE dest)
|
|
109
|
+
{
|
|
110
|
+
DROBY_DUMP_ITERATION_ARG arg = { rb_hash_new(), dest };
|
|
111
|
+
rb_hash_foreach(self, (int(*)(ANYARGS)) hash_dump_element, (VALUE)&arg);
|
|
112
|
+
return arg.result;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static VALUE appendable_dump_element(VALUE value, DROBY_DUMP_ITERATION_ARG* arg)
|
|
116
|
+
{
|
|
117
|
+
VALUE args[2] = { value, arg->dest };
|
|
118
|
+
rb_funcall(arg->result, id_append, 1, droby_format(2, args, mRobyDistributed));
|
|
119
|
+
return Qnil;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Creates a copy of this Set with all its values formatted for marshalling
|
|
123
|
+
// using Distributed.format
|
|
124
|
+
static VALUE set_droby_dump(VALUE self, VALUE dest)
|
|
125
|
+
{
|
|
126
|
+
DROBY_DUMP_ITERATION_ARG arg = { rb_class_new_instance(0, 0, cSet), dest };
|
|
127
|
+
rb_iterate(rb_each, self, RUBY_METHOD_FUNC(appendable_dump_element), (VALUE)&arg);
|
|
128
|
+
return arg.result;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Creates a copy of this ValueSet with all its values formatted for
|
|
132
|
+
// marshalling using Distributed.format
|
|
133
|
+
static VALUE value_set_droby_dump(VALUE self, VALUE dest)
|
|
134
|
+
{
|
|
135
|
+
VALUE result = rb_class_new_instance(0, 0, cValueSet);
|
|
136
|
+
std::set<VALUE>* result_set;
|
|
137
|
+
Data_Get_Struct(result, std::set<VALUE>, result_set);
|
|
138
|
+
|
|
139
|
+
std::set<VALUE> const * source_set;
|
|
140
|
+
Data_Get_Struct(self, std::set<VALUE>, source_set);
|
|
141
|
+
|
|
142
|
+
VALUE el[2] = { Qnil, dest };
|
|
143
|
+
for (std::set<VALUE>::const_iterator it = source_set->begin(); it != source_set->end(); ++it)
|
|
144
|
+
{
|
|
145
|
+
el[0] = *it;
|
|
146
|
+
result_set->insert(droby_format(2, el, mRobyDistributed));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
extern "C" void Init_roby_marshalling()
|
|
153
|
+
{
|
|
154
|
+
id_droby_dump = rb_intern("droby_dump");
|
|
155
|
+
id_remote_id = rb_intern("remote_id");
|
|
156
|
+
id_append = rb_intern("<<");
|
|
157
|
+
|
|
158
|
+
cDRbObject = rb_const_get(rb_cObject, rb_intern("DRbObject"));
|
|
159
|
+
cValueSet = rb_const_get(rb_cObject, rb_intern("ValueSet"));
|
|
160
|
+
cSet = rb_const_get(rb_cObject, rb_intern("Set"));
|
|
161
|
+
|
|
162
|
+
/* */
|
|
163
|
+
mRoby = rb_define_module("Roby");
|
|
164
|
+
/* */
|
|
165
|
+
mRobyDistributed = rb_define_module_under(mRoby, "Distributed");
|
|
166
|
+
|
|
167
|
+
rb_define_method(rb_cArray , "droby_dump" , RUBY_METHOD_FUNC(array_droby_dump) , 1);
|
|
168
|
+
rb_define_method(rb_cHash , "droby_dump" , RUBY_METHOD_FUNC(hash_droby_dump) , 1);
|
|
169
|
+
rb_define_method(cSet , "droby_dump" , RUBY_METHOD_FUNC(set_droby_dump) , 1);
|
|
170
|
+
rb_define_method(cValueSet , "droby_dump" , RUBY_METHOD_FUNC(value_set_droby_dump) , 1);
|
|
171
|
+
|
|
172
|
+
rb_define_singleton_method(mRobyDistributed, "format", RUBY_METHOD_FUNC(droby_format), -1);
|
|
173
|
+
|
|
174
|
+
}
|
|
175
|
+
|
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
#include "graph.hh"
|
|
2
|
+
#include <boost/graph/depth_first_search.hpp>
|
|
3
|
+
#include <boost/graph/breadth_first_search.hpp>
|
|
4
|
+
#include <boost/iterator/transform_iterator.hpp>
|
|
5
|
+
#include <boost/iterator/filter_iterator.hpp>
|
|
6
|
+
#include <boost/graph/connected_components.hpp>
|
|
7
|
+
#include <boost/graph/topological_sort.hpp>
|
|
8
|
+
#include <boost/bind.hpp>
|
|
9
|
+
#include <boost/graph/reverse_graph.hpp>
|
|
10
|
+
#include "undirected_graph.hh"
|
|
11
|
+
#include "undirected_dfs.hh"
|
|
12
|
+
#include <queue>
|
|
13
|
+
#include <functional>
|
|
14
|
+
|
|
15
|
+
typedef RubyGraph::vertex_iterator vertex_iterator;
|
|
16
|
+
typedef RubyGraph::vertex_descriptor vertex_descriptor;
|
|
17
|
+
typedef RubyGraph::edge_iterator edge_iterator;
|
|
18
|
+
typedef RubyGraph::edge_descriptor edge_descriptor;
|
|
19
|
+
|
|
20
|
+
static VALUE graph_view_of(VALUE self)
|
|
21
|
+
{ return rb_iv_get(self, "@__bgl_real_graph__"); }
|
|
22
|
+
|
|
23
|
+
using namespace boost;
|
|
24
|
+
using namespace std;
|
|
25
|
+
|
|
26
|
+
static ID id_new;
|
|
27
|
+
static VALUE utilrbValueSet;
|
|
28
|
+
|
|
29
|
+
template<typename T>
|
|
30
|
+
struct Queue : std::queue<T>
|
|
31
|
+
{
|
|
32
|
+
T& top() { return this->front(); }
|
|
33
|
+
T const& top() const { return this->front(); }
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
namespace details {
|
|
37
|
+
// Reverse graphs do not have an adjacency_iterator
|
|
38
|
+
template<typename Graph>
|
|
39
|
+
struct vertex_range< boost::reverse_graph<Graph, Graph&>, false>
|
|
40
|
+
{
|
|
41
|
+
typedef typename Graph::adjacency_iterator iterator;
|
|
42
|
+
typedef std::pair<iterator, iterator> range;
|
|
43
|
+
|
|
44
|
+
static range get(RubyGraph::vertex_descriptor v,
|
|
45
|
+
boost::reverse_graph<Graph, Graph&> const& graph)
|
|
46
|
+
{ return adjacent_vertices(v, graph.m_g); }
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
template<typename Graph>
|
|
50
|
+
struct vertex_range< boost::reverse_graph<Graph, Graph const&>, false>
|
|
51
|
+
{
|
|
52
|
+
typedef typename Graph::adjacency_iterator iterator;
|
|
53
|
+
typedef std::pair<iterator, iterator> range;
|
|
54
|
+
|
|
55
|
+
static range get(RubyGraph::vertex_descriptor v,
|
|
56
|
+
boost::reverse_graph<Graph, Graph const&> const& graph)
|
|
57
|
+
{ return adjacent_vertices(v, graph.m_g); }
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* If +key+ is found in +assoc+, returns its value. Otherwise, initializes
|
|
62
|
+
* +key+ to +default_value+ in +assoc+ and returns it
|
|
63
|
+
*/
|
|
64
|
+
template<typename Key, typename Value>
|
|
65
|
+
Value& get(map<Key, Value>& assoc, Key const& key, Value const& default_value)
|
|
66
|
+
{
|
|
67
|
+
typename map<Key, Value>::iterator it = assoc.find(key);
|
|
68
|
+
if (it != assoc.end())
|
|
69
|
+
return it->second;
|
|
70
|
+
|
|
71
|
+
tie(it, tuples::ignore) = assoc.insert( make_pair(key, default_value) );
|
|
72
|
+
return it->second;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* If +key+ is found in +assoc+, returns its value. Otherwise, returns +default_value+
|
|
76
|
+
*/
|
|
77
|
+
template<typename Key, typename Value>
|
|
78
|
+
Value const& get(map<Key, Value> const& assoc, Key const& key, Value const& default_value)
|
|
79
|
+
{
|
|
80
|
+
typename map<Key, Value>::const_iterator it = assoc.find(key);
|
|
81
|
+
if (it != assoc.end())
|
|
82
|
+
return it->second;
|
|
83
|
+
|
|
84
|
+
return default_value;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ColorMap is a map with default value */
|
|
88
|
+
class ColorMap : private map<vertex_descriptor, default_color_type>
|
|
89
|
+
{
|
|
90
|
+
template<typename Key, typename Value>
|
|
91
|
+
friend Value& get(map<Key, Value>&, Key const&, Value const&);
|
|
92
|
+
|
|
93
|
+
default_color_type const default_value;
|
|
94
|
+
|
|
95
|
+
typedef map<vertex_descriptor, default_color_type> Super;
|
|
96
|
+
|
|
97
|
+
public:
|
|
98
|
+
|
|
99
|
+
typedef Super::key_type key_type;
|
|
100
|
+
typedef Super::value_type value_type;
|
|
101
|
+
|
|
102
|
+
Super::clear;
|
|
103
|
+
|
|
104
|
+
ColorMap()
|
|
105
|
+
: default_value(color_traits<default_color_type>::white()) {}
|
|
106
|
+
|
|
107
|
+
default_color_type& operator[](vertex_descriptor key)
|
|
108
|
+
{
|
|
109
|
+
default_color_type& c = get(*this, key, default_value);
|
|
110
|
+
return c;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
typedef list<vertex_descriptor> vertex_list;
|
|
116
|
+
|
|
117
|
+
struct vertex_recorder : public default_dfs_visitor
|
|
118
|
+
{
|
|
119
|
+
public:
|
|
120
|
+
set<VALUE>& component;
|
|
121
|
+
vertex_recorder( set<VALUE>& component )
|
|
122
|
+
: component(component) { }
|
|
123
|
+
|
|
124
|
+
template<typename G>
|
|
125
|
+
void discover_vertex(vertex_descriptor u, G const& g)
|
|
126
|
+
{ component.insert(g[u]); }
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
static std::set<VALUE>& rb_to_set(VALUE object)
|
|
131
|
+
{
|
|
132
|
+
if (!RTEST(rb_obj_is_kind_of(object, utilrbValueSet)))
|
|
133
|
+
rb_raise(rb_eArgError, "expected a ValueSet");
|
|
134
|
+
|
|
135
|
+
std::set<VALUE>* result_set;
|
|
136
|
+
Data_Get_Struct(object, set<VALUE>, result_set);
|
|
137
|
+
return *result_set;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Converts a std::set<VALUE> into a ValueSet object
|
|
141
|
+
* After this method, +source+ is empty */
|
|
142
|
+
static VALUE set_to_rb(set<VALUE>& source)
|
|
143
|
+
{
|
|
144
|
+
VALUE result = rb_funcall(utilrbValueSet, id_new, 0);
|
|
145
|
+
set<VALUE>* result_set;
|
|
146
|
+
Data_Get_Struct(result, set<VALUE>, result_set);
|
|
147
|
+
|
|
148
|
+
result_set->swap(source);
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
typedef std::set<VALUE> ValueSet;
|
|
153
|
+
/* Adds in +result+ all components generated by the items in [it, end). We
|
|
154
|
+
* assume that there is no component which includes more than one item in
|
|
155
|
+
* [it, end) */
|
|
156
|
+
template<typename Graph, typename Iterator>
|
|
157
|
+
static void graph_components_i(std::list<ValueSet>& result, Graph const& g, Iterator it, Iterator end, bool include_singletons)
|
|
158
|
+
{
|
|
159
|
+
ColorMap colors;
|
|
160
|
+
|
|
161
|
+
result.push_front(ValueSet());
|
|
162
|
+
for (; it != end; ++it)
|
|
163
|
+
{
|
|
164
|
+
if (0 == *it) // elements not in +g+ are handled by graph_result_root_descriptor
|
|
165
|
+
continue;
|
|
166
|
+
if (colors[*it] != color_traits<default_color_type>::white())
|
|
167
|
+
continue;
|
|
168
|
+
|
|
169
|
+
ValueSet& component(*result.begin());
|
|
170
|
+
depth_first_visit(g, *it, vertex_recorder(component), make_assoc_property_map(colors));
|
|
171
|
+
if (component.size() > 1 || include_singletons)
|
|
172
|
+
result.push_front(ValueSet());
|
|
173
|
+
else
|
|
174
|
+
component.clear();
|
|
175
|
+
}
|
|
176
|
+
result.pop_front();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** If +v+ is found in +g+, returns the corresponding vertex_descriptor. Otherwise,
|
|
180
|
+
* add a singleton component to +result+ and return NULL.
|
|
181
|
+
*/
|
|
182
|
+
static vertex_descriptor graph_components_root_descriptor(std::list<ValueSet>& result, VALUE v, VALUE g, bool include_singletons)
|
|
183
|
+
{
|
|
184
|
+
vertex_descriptor d;
|
|
185
|
+
bool exists;
|
|
186
|
+
tie(d, exists) = rb_to_vertex(v, g);
|
|
187
|
+
if (! exists)
|
|
188
|
+
{
|
|
189
|
+
if (include_singletons)
|
|
190
|
+
{
|
|
191
|
+
ValueSet component;
|
|
192
|
+
component.insert(v);
|
|
193
|
+
result.push_back(component);
|
|
194
|
+
}
|
|
195
|
+
return NULL;
|
|
196
|
+
}
|
|
197
|
+
return d;
|
|
198
|
+
}
|
|
199
|
+
template<typename Graph>
|
|
200
|
+
static VALUE graph_do_generated_subgraphs(int argc, VALUE* argv, Graph const& g, VALUE self)
|
|
201
|
+
{
|
|
202
|
+
VALUE roots = Qnil, include_singletons;
|
|
203
|
+
if (rb_scan_args(argc, argv, "11", &roots, &include_singletons) == 1)
|
|
204
|
+
include_singletons = Qtrue;
|
|
205
|
+
|
|
206
|
+
bool with_singletons = RTEST(include_singletons) ? true : false;
|
|
207
|
+
std::list<ValueSet> result;
|
|
208
|
+
if (NIL_P(roots))
|
|
209
|
+
{
|
|
210
|
+
RubyGraph::vertex_iterator it, end;
|
|
211
|
+
tie(it, end) = vertices(g);
|
|
212
|
+
// call graph_components_i with all root vertices
|
|
213
|
+
// in +graph+
|
|
214
|
+
graph_components_i(result, g,
|
|
215
|
+
make_filter_iterator(
|
|
216
|
+
bind(
|
|
217
|
+
vertex_has_adjacent_i<Graph, false>,
|
|
218
|
+
_1, ref(g)
|
|
219
|
+
), it, end
|
|
220
|
+
),
|
|
221
|
+
make_filter_iterator(
|
|
222
|
+
bind(
|
|
223
|
+
vertex_has_adjacent_i<Graph, false>,
|
|
224
|
+
_1, ref(g)
|
|
225
|
+
), end, end
|
|
226
|
+
), with_singletons
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
else
|
|
230
|
+
{
|
|
231
|
+
std::set<VALUE>& root_set = rb_to_set(roots);
|
|
232
|
+
std::set<VALUE>::const_iterator
|
|
233
|
+
begin = root_set.begin(),
|
|
234
|
+
end = root_set.end();
|
|
235
|
+
|
|
236
|
+
// call graph_components_i with all vertices given in as argument
|
|
237
|
+
graph_components_i(result, g,
|
|
238
|
+
make_transform_iterator(begin,
|
|
239
|
+
bind(graph_components_root_descriptor, ref(result), _1, self, with_singletons)
|
|
240
|
+
),
|
|
241
|
+
make_transform_iterator(end,
|
|
242
|
+
bind(graph_components_root_descriptor, ref(result), _1, self, with_singletons)
|
|
243
|
+
), with_singletons);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Now convert the result into a Ruby array
|
|
247
|
+
VALUE rb_result = rb_ary_new();
|
|
248
|
+
for (std::list<ValueSet>::iterator it = result.begin(); it != result.end(); ++it)
|
|
249
|
+
rb_ary_push(rb_result, set_to_rb(*it));
|
|
250
|
+
return rb_result;
|
|
251
|
+
}
|
|
252
|
+
/*
|
|
253
|
+
* call-seq:
|
|
254
|
+
* graph.components(seeds = nil, include_singletons = true) => components
|
|
255
|
+
*
|
|
256
|
+
* Returns an array of vertex sets. Each set is a connected component of
|
|
257
|
+
* +graph+. If a list of vertices +seeds+ is provided, returns only the
|
|
258
|
+
* components the vertices are part of. The graph is treated as if it were not
|
|
259
|
+
* directed.
|
|
260
|
+
*
|
|
261
|
+
* If +include_singletons+ is false and +seeds+ is non-nil, then +components+
|
|
262
|
+
* will not include the singleton components { v } where v is in +seeds+
|
|
263
|
+
*/
|
|
264
|
+
static VALUE graph_components(int argc, VALUE* argv, VALUE self)
|
|
265
|
+
{
|
|
266
|
+
VALUE seeds, include_singletons;
|
|
267
|
+
rb_scan_args(argc, argv, "02", &seeds, &include_singletons);
|
|
268
|
+
if (argc == 1)
|
|
269
|
+
include_singletons = Qtrue;
|
|
270
|
+
|
|
271
|
+
// Compute the connected components
|
|
272
|
+
RubyGraph const& g = graph_wrapped(self);
|
|
273
|
+
|
|
274
|
+
typedef std::map<vertex_descriptor, int> ComponentMap;
|
|
275
|
+
ComponentMap component_map;
|
|
276
|
+
ColorMap color_map;
|
|
277
|
+
int count = connected_components(utilmm::make_undirected_graph(g),
|
|
278
|
+
make_assoc_property_map(component_map),
|
|
279
|
+
boost::color_map( make_assoc_property_map(color_map) ));
|
|
280
|
+
|
|
281
|
+
VALUE ret = rb_ary_new2(count);
|
|
282
|
+
std::vector<bool> enabled_components;
|
|
283
|
+
std::vector<VALUE> components(count);
|
|
284
|
+
if (0 == argc)
|
|
285
|
+
enabled_components.resize(count, true);
|
|
286
|
+
else
|
|
287
|
+
{
|
|
288
|
+
enabled_components.resize(count, false);
|
|
289
|
+
std::set<VALUE>& seed_set = rb_to_set(seeds);
|
|
290
|
+
for (std::set<VALUE>::const_iterator it = seed_set.begin(); it != seed_set.end(); ++it)
|
|
291
|
+
{
|
|
292
|
+
VALUE rb_vertex = *it;
|
|
293
|
+
|
|
294
|
+
vertex_descriptor v; bool in_graph;
|
|
295
|
+
tie(v, in_graph) = rb_to_vertex(rb_vertex, self);
|
|
296
|
+
if (in_graph)
|
|
297
|
+
{
|
|
298
|
+
int v_c = component_map[v];
|
|
299
|
+
enabled_components[v_c] = true;
|
|
300
|
+
}
|
|
301
|
+
else if (RTEST(include_singletons))
|
|
302
|
+
rb_ary_push(ret, rb_ary_new3(1, rb_vertex));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Add empty array for all enabled components
|
|
307
|
+
for (int i = 0; i < count; ++i)
|
|
308
|
+
{
|
|
309
|
+
if (! enabled_components[i]) continue;
|
|
310
|
+
VALUE ary = components[i] = rb_ary_new();
|
|
311
|
+
rb_ary_store(ret, i, ary);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Add the vertices to their corresponding Ruby component
|
|
315
|
+
for (ComponentMap::const_iterator it = component_map.begin(); it != component_map.end(); ++it)
|
|
316
|
+
{
|
|
317
|
+
int c = it->second;
|
|
318
|
+
if (! enabled_components[c])
|
|
319
|
+
continue;
|
|
320
|
+
|
|
321
|
+
rb_ary_push(components[c], g[it->first]);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (argc > 0 && !RTEST(include_singletons))
|
|
325
|
+
{
|
|
326
|
+
// Remove the remaining singletons
|
|
327
|
+
for (int i = 0; i < count; ++i)
|
|
328
|
+
{
|
|
329
|
+
if (! enabled_components[i])
|
|
330
|
+
continue;
|
|
331
|
+
if (RARRAY(components[i])->len == 1)
|
|
332
|
+
rb_ary_store(ret, i, Qnil);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Remove all unused component slots (disabled components)
|
|
337
|
+
rb_funcall(ret, rb_intern("compact!"), 0);
|
|
338
|
+
return ret;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/*
|
|
342
|
+
* call-seq:
|
|
343
|
+
* undirected_graph.components(seeds = nil, include_singletons = true) => components
|
|
344
|
+
*
|
|
345
|
+
* Returns an array of vertex sets. Each set is a connected component of +graph+. If
|
|
346
|
+
* a list of vertices is provided, returns only the components the vertices are part of.
|
|
347
|
+
* The graph is treated as if it were not directed. It is equivalent to graph.components.
|
|
348
|
+
*/
|
|
349
|
+
static
|
|
350
|
+
VALUE graph_undirected_components(int argc, VALUE* argv, VALUE self)
|
|
351
|
+
{ return graph_components(argc, argv, graph_view_of(self)); }
|
|
352
|
+
|
|
353
|
+
/* call-seq:
|
|
354
|
+
* graph.generated_subgraph([v1, v2, ...][, include_singletons]) => components
|
|
355
|
+
*
|
|
356
|
+
* Returns an array of vertex sets. Each set is the component that can be
|
|
357
|
+
* reached from one of the given seed. If no initial vertex is given, the graph
|
|
358
|
+
* roots are taken.
|
|
359
|
+
*/
|
|
360
|
+
static VALUE graph_generated_subgraphs(int argc, VALUE* argv, VALUE self)
|
|
361
|
+
{ return graph_do_generated_subgraphs(argc, argv, graph_wrapped(self), self); }
|
|
362
|
+
|
|
363
|
+
/* call-seq:
|
|
364
|
+
* graph.generated_subgraph([v1, v2, ...]) => components
|
|
365
|
+
*
|
|
366
|
+
* Like Graph#generated_subgraph, but on the reverse graph of +graph+ (where edges has
|
|
367
|
+
* been swapped)
|
|
368
|
+
*/
|
|
369
|
+
static VALUE graph_reverse_generated_subgraphs(int argc, VALUE* argv, VALUE self)
|
|
370
|
+
{
|
|
371
|
+
VALUE real_graph = rb_iv_get(self, "@__bgl_real_graph__");
|
|
372
|
+
return graph_do_generated_subgraphs(argc, argv, make_reverse_graph(graph_wrapped(real_graph)), real_graph);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
static const int VISIT_TREE_EDGES = 1;
|
|
376
|
+
static const int VISIT_BACK_EDGES = 2;
|
|
377
|
+
static const int VISIT_FORWARD_OR_CROSS_EDGES = 4;
|
|
378
|
+
static const int VISIT_NON_TREE_EDGES = 6;
|
|
379
|
+
static const int VISIT_ALL_EDGES = 7;
|
|
380
|
+
|
|
381
|
+
struct ruby_dfs_visitor : public default_dfs_visitor
|
|
382
|
+
{
|
|
383
|
+
|
|
384
|
+
int m_mode;
|
|
385
|
+
ruby_dfs_visitor(int mode)
|
|
386
|
+
: m_mode(mode) { }
|
|
387
|
+
|
|
388
|
+
template<typename E, typename G>
|
|
389
|
+
void tree_edge(E e, G const& graph)
|
|
390
|
+
{ yield_edge(e, graph, VISIT_TREE_EDGES); }
|
|
391
|
+
template<typename E, typename G>
|
|
392
|
+
void back_edge(E e, G const& graph)
|
|
393
|
+
{ yield_edge(e, graph, VISIT_BACK_EDGES); }
|
|
394
|
+
template<typename E, typename G>
|
|
395
|
+
void forward_or_cross_edge(E e, G const& graph)
|
|
396
|
+
{ yield_edge(e, graph, VISIT_FORWARD_OR_CROSS_EDGES); }
|
|
397
|
+
|
|
398
|
+
template<typename E, typename G>
|
|
399
|
+
void yield_edge(E e, G const& graph, int what)
|
|
400
|
+
{
|
|
401
|
+
if (!(what & m_mode))
|
|
402
|
+
return;
|
|
403
|
+
|
|
404
|
+
VALUE rb_source = graph[source(e, graph)];
|
|
405
|
+
VALUE rb_target = graph[target(e, graph)];
|
|
406
|
+
VALUE info = graph[e].info;
|
|
407
|
+
rb_yield_values(4, rb_source, rb_target, info, INT2FIX(what));
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
template<typename G>
|
|
412
|
+
static bool search_terminator(vertex_descriptor u, G const& g)
|
|
413
|
+
{
|
|
414
|
+
VALUE thread = rb_thread_current();
|
|
415
|
+
bool result = RTEST(rb_thread_local_aref(thread, rb_intern("@prune")));
|
|
416
|
+
if (result)
|
|
417
|
+
rb_thread_local_aset(thread, rb_intern("@prune"), Qfalse);
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* call-seq:
|
|
422
|
+
* graph.prune
|
|
423
|
+
*
|
|
424
|
+
* In #each_dfs, call this method to stop developing the current branch
|
|
425
|
+
*/
|
|
426
|
+
static VALUE graph_prune(VALUE self)
|
|
427
|
+
{
|
|
428
|
+
VALUE thread = rb_thread_current();
|
|
429
|
+
rb_thread_local_aset(thread, rb_intern("@prune"), Qtrue);
|
|
430
|
+
return Qtrue;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
template<typename Graph>
|
|
434
|
+
static VALUE graph_each_dfs(VALUE self, Graph const& graph, VALUE root, VALUE mode)
|
|
435
|
+
{
|
|
436
|
+
rb_thread_local_aset(rb_thread_current(), rb_intern("@prune"), Qfalse);
|
|
437
|
+
|
|
438
|
+
vertex_descriptor v; bool exists;
|
|
439
|
+
tie(v, exists) = rb_to_vertex(root, self);
|
|
440
|
+
if (! exists)
|
|
441
|
+
return self;
|
|
442
|
+
|
|
443
|
+
map<vertex_descriptor, default_color_type> colors;
|
|
444
|
+
depth_first_visit(graph, v, ruby_dfs_visitor(FIX2INT(mode)),
|
|
445
|
+
make_assoc_property_map(colors), &search_terminator<Graph>);
|
|
446
|
+
return self;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/* call-seq:
|
|
450
|
+
* graph.each_dfs(root, mode) { |source, dest, info, kind| ... }
|
|
451
|
+
*
|
|
452
|
+
* Enumerates edges of the graph following a depth-first search order.
|
|
453
|
+
* +mode+ is a filter on the kind of edge which shall be enumerated (TREE,
|
|
454
|
+
* FORWARD_OR_CROSS, BACK and ALL) and +root+ is the source of the search
|
|
455
|
+
*/
|
|
456
|
+
static VALUE graph_direct_each_dfs(VALUE self, VALUE root, VALUE mode)
|
|
457
|
+
{
|
|
458
|
+
RubyGraph& graph = graph_wrapped(self);
|
|
459
|
+
return graph_each_dfs(self, graph, root, mode);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/* call-seq:
|
|
463
|
+
* graph.each_dfs(root, mode) { |source, dest, info, kind| ... }
|
|
464
|
+
*
|
|
465
|
+
* Enumerates edges of the graph following a depth-first search order.
|
|
466
|
+
* +mode+ is a filter on the kind of edge which shall be enumerated (TREE,
|
|
467
|
+
* NON_TREE and ALL) and +root+ is the source of the search
|
|
468
|
+
*/
|
|
469
|
+
static VALUE graph_reverse_each_dfs(VALUE self, VALUE root, VALUE mode)
|
|
470
|
+
{
|
|
471
|
+
VALUE real_graph = graph_view_of(self);
|
|
472
|
+
RubyGraph& graph = graph_wrapped(real_graph);
|
|
473
|
+
return graph_each_dfs(real_graph, make_reverse_graph(graph), root, mode);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/* call-seq:
|
|
477
|
+
* graph.each_dfs(root, mode) { |source, dest, info, kind| ... }
|
|
478
|
+
*
|
|
479
|
+
* Enumerates edges of the graph following a depth-first search order.
|
|
480
|
+
* +mode+ is a filter on the kind of edge which shall be enumerated (TREE,
|
|
481
|
+
* FORWARD_OR_CROSS, BACK and ALL) and +root+ is the source of the search
|
|
482
|
+
*/
|
|
483
|
+
static VALUE graph_undirected_each_dfs(VALUE self, VALUE root, VALUE mode)
|
|
484
|
+
{
|
|
485
|
+
VALUE real_graph = graph_view_of(self);
|
|
486
|
+
RubyGraph& graph = graph_wrapped(real_graph);
|
|
487
|
+
typedef utilmm::undirected_graph<RubyGraph> Undirected;
|
|
488
|
+
Undirected undirected(graph);
|
|
489
|
+
|
|
490
|
+
vertex_descriptor v; bool exists;
|
|
491
|
+
tie(v, exists) = rb_to_vertex(root, real_graph);
|
|
492
|
+
if (! exists)
|
|
493
|
+
return self;
|
|
494
|
+
|
|
495
|
+
ColorMap colors;
|
|
496
|
+
edge_iterator ei, ei_end;
|
|
497
|
+
for(tie(ei, ei_end) = edges(graph); ei != ei_end; ++ei)
|
|
498
|
+
graph[*ei].color = boost::white_color;
|
|
499
|
+
|
|
500
|
+
rb_thread_local_aset(rb_thread_current(), rb_intern("@prune"), Qfalse);
|
|
501
|
+
utilmm::undirected_depth_first_visit(undirected, v, ruby_dfs_visitor(FIX2INT(mode)),
|
|
502
|
+
make_assoc_property_map(colors),
|
|
503
|
+
utilmm::make_undirected_edge_map(get(&EdgeProperty::color, graph)),
|
|
504
|
+
&search_terminator<Undirected>);
|
|
505
|
+
return self;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
struct ruby_reachable_visitor : default_dfs_visitor
|
|
509
|
+
{
|
|
510
|
+
bool& m_found;
|
|
511
|
+
vertex_descriptor m_target;
|
|
512
|
+
|
|
513
|
+
ruby_reachable_visitor(bool& found, vertex_descriptor target)
|
|
514
|
+
: m_found(found), m_target(target) { m_found = false; }
|
|
515
|
+
|
|
516
|
+
template<typename E, typename G>
|
|
517
|
+
void tree_edge(E e, G const& graph)
|
|
518
|
+
{
|
|
519
|
+
if (m_target == target(e, graph))
|
|
520
|
+
m_found = true;
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
struct ruby_reachable_terminator
|
|
525
|
+
{
|
|
526
|
+
bool const& found;
|
|
527
|
+
ruby_reachable_terminator(bool const& found)
|
|
528
|
+
: found(found) { }
|
|
529
|
+
|
|
530
|
+
template<typename G>
|
|
531
|
+
bool operator()(vertex_descriptor u, G const& g) const { return found; }
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
/* call-seq:
|
|
535
|
+
* graph.reachable?(v1, v2)
|
|
536
|
+
*
|
|
537
|
+
* Returns true if v2 can be reached from v1
|
|
538
|
+
*/
|
|
539
|
+
VALUE graph_reachable_p(VALUE self, VALUE source, VALUE target)
|
|
540
|
+
{
|
|
541
|
+
RubyGraph& graph = graph_wrapped(self);
|
|
542
|
+
vertex_descriptor s, t; bool exists;
|
|
543
|
+
tie(s, exists) = rb_to_vertex(source, self);
|
|
544
|
+
if (! exists)
|
|
545
|
+
return Qfalse;
|
|
546
|
+
tie(t, exists) = rb_to_vertex(target, self);
|
|
547
|
+
if (! exists)
|
|
548
|
+
return Qfalse;
|
|
549
|
+
|
|
550
|
+
map<vertex_descriptor, default_color_type> colors;
|
|
551
|
+
bool found;
|
|
552
|
+
depth_first_visit(graph, s, ruby_reachable_visitor(found, t),
|
|
553
|
+
make_assoc_property_map(colors), ruby_reachable_terminator(found));
|
|
554
|
+
|
|
555
|
+
return found;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
struct ruby_bfs_visitor : public default_bfs_visitor
|
|
560
|
+
{
|
|
561
|
+
int m_mode;
|
|
562
|
+
ruby_bfs_visitor(int mode)
|
|
563
|
+
: m_mode(mode) { }
|
|
564
|
+
|
|
565
|
+
template<typename E, typename G>
|
|
566
|
+
void tree_edge(E e, G const& graph)
|
|
567
|
+
{ yield_edge(e, graph, VISIT_TREE_EDGES); }
|
|
568
|
+
template<typename E, typename G>
|
|
569
|
+
void non_tree_edge(E e, G const& graph)
|
|
570
|
+
{ yield_edge(e, graph, VISIT_NON_TREE_EDGES); }
|
|
571
|
+
template<typename E, typename G>
|
|
572
|
+
void yield_edge(E e, G const& graph, int what)
|
|
573
|
+
{
|
|
574
|
+
if (!(what & m_mode))
|
|
575
|
+
return;
|
|
576
|
+
|
|
577
|
+
VALUE source_vertex = graph[source(e, graph)];
|
|
578
|
+
VALUE target_vertex = graph[target(e, graph)];
|
|
579
|
+
VALUE info = graph[e].info;
|
|
580
|
+
rb_yield_values(4, source_vertex, target_vertex, info, INT2FIX(what));
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
template<typename Graph>
|
|
585
|
+
static VALUE graph_each_bfs(VALUE self, Graph const& graph, VALUE root, VALUE mode)
|
|
586
|
+
{
|
|
587
|
+
int intmode = FIX2INT(mode);
|
|
588
|
+
if ((intmode & VISIT_NON_TREE_EDGES) && ((intmode & VISIT_NON_TREE_EDGES) != VISIT_NON_TREE_EDGES))
|
|
589
|
+
rb_raise(rb_eArgError, "cannot use FORWARD_OR_CROSS and BACK");
|
|
590
|
+
|
|
591
|
+
vertex_descriptor v; bool exists;
|
|
592
|
+
tie(v, exists) = rb_to_vertex(root, self);
|
|
593
|
+
if (! exists)
|
|
594
|
+
return self;
|
|
595
|
+
|
|
596
|
+
rb_thread_local_aset(rb_thread_current(), rb_intern("@prune"), Qfalse);
|
|
597
|
+
map<vertex_descriptor, default_color_type> colors;
|
|
598
|
+
Queue<vertex_descriptor> queue;
|
|
599
|
+
breadth_first_search(graph, v, queue, ruby_bfs_visitor(intmode),
|
|
600
|
+
make_assoc_property_map(colors));
|
|
601
|
+
return self;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/* call-seq:
|
|
605
|
+
* graph.each_bfs(root, mode) { |source, dest, info, kind| ... }
|
|
606
|
+
*
|
|
607
|
+
* Enumerates edges of the graph following a breadth-first search order.
|
|
608
|
+
* +mode+ is a filter on the kind of edge which shall be enumerated (TREE,
|
|
609
|
+
* NON_TREE and ALL) and +root+ is the source of the search
|
|
610
|
+
*/
|
|
611
|
+
static VALUE graph_direct_each_bfs(VALUE self, VALUE root, VALUE mode)
|
|
612
|
+
{
|
|
613
|
+
RubyGraph& graph = graph_wrapped(self);
|
|
614
|
+
return graph_each_bfs(self, graph, root, mode);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/* call-seq:
|
|
618
|
+
* graph.each_bfs(root, mode) { |source, dest, info, kind| ... }
|
|
619
|
+
*
|
|
620
|
+
* Enumerates edges of the graph following a breadth-first search order.
|
|
621
|
+
* +mode+ is a filter on the kind of edge which shall be enumerated (TREE,
|
|
622
|
+
* NON_TREE and ALL) and +root+ is the source of the search
|
|
623
|
+
*/
|
|
624
|
+
static VALUE graph_reverse_each_bfs(VALUE self, VALUE root, VALUE mode)
|
|
625
|
+
{
|
|
626
|
+
VALUE real_graph = graph_view_of(self);
|
|
627
|
+
RubyGraph& graph = graph_wrapped(real_graph);
|
|
628
|
+
return graph_each_bfs(real_graph, make_reverse_graph(graph), root, mode);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* call-seq:
|
|
632
|
+
* graph.each_bfs(root, mode) { |source, dest, info, kind| ... }
|
|
633
|
+
*
|
|
634
|
+
* Enumerates edges of the graph following a breadth-first search order.
|
|
635
|
+
* +mode+ is a filter on the kind of edge which shall be enumerated (TREE,
|
|
636
|
+
* NON_TREE and ALL) and +root+ is the source of the search
|
|
637
|
+
*/
|
|
638
|
+
static VALUE graph_undirected_each_bfs(VALUE self, VALUE root, VALUE mode)
|
|
639
|
+
{
|
|
640
|
+
VALUE real_graph = graph_view_of(self);
|
|
641
|
+
RubyGraph& graph = graph_wrapped(real_graph);
|
|
642
|
+
return graph_each_bfs(real_graph, utilmm::make_undirected_graph(graph), root, mode);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* call-seq:
|
|
646
|
+
* graph.topological_sort => array
|
|
647
|
+
*
|
|
648
|
+
* Returns a topological sorting of this graph
|
|
649
|
+
*/
|
|
650
|
+
static VALUE graph_topological_sort(int argc, VALUE* argv, VALUE self)
|
|
651
|
+
{
|
|
652
|
+
VALUE rb_result;
|
|
653
|
+
rb_scan_args(argc, argv, "01", &rb_result);
|
|
654
|
+
if (NIL_P(rb_result))
|
|
655
|
+
rb_result = rb_ary_new();
|
|
656
|
+
else
|
|
657
|
+
rb_ary_clear(rb_result);
|
|
658
|
+
|
|
659
|
+
RubyGraph& graph = graph_wrapped(self);
|
|
660
|
+
typedef std::vector<RubyGraph::vertex_descriptor> Result;
|
|
661
|
+
Result result;
|
|
662
|
+
|
|
663
|
+
map<vertex_descriptor, default_color_type> colors;
|
|
664
|
+
try
|
|
665
|
+
{
|
|
666
|
+
topological_sort(graph, std::back_inserter(result),
|
|
667
|
+
boost::color_map(make_assoc_property_map(colors)));
|
|
668
|
+
|
|
669
|
+
for (int i = result.size() - 1; i >= 0; --i)
|
|
670
|
+
rb_ary_push(rb_result, graph[result[i]]);
|
|
671
|
+
return rb_result;
|
|
672
|
+
}
|
|
673
|
+
catch(boost::not_a_dag) {}
|
|
674
|
+
rb_raise(rb_eArgError, "the graph is not a DAG");
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**********************************************************************
|
|
678
|
+
* Extension initialization
|
|
679
|
+
*/
|
|
680
|
+
|
|
681
|
+
/*
|
|
682
|
+
* Document-class: BGL
|
|
683
|
+
*
|
|
684
|
+
* The BGL module defines a Graph class and a Vertex module. The Graph class can
|
|
685
|
+
* be used to manipulate graphs where vertices are referenced by a graph descriptor
|
|
686
|
+
* (Graph#add_edge, Graph#add_vertex, ...). However, the preferred way to us BGL is
|
|
687
|
+
* to mix Vertex in the vertex objects and use the associated methods:
|
|
688
|
+
*
|
|
689
|
+
* class MyNode
|
|
690
|
+
* include BGL::Graph
|
|
691
|
+
* end
|
|
692
|
+
* graph = Graph.new
|
|
693
|
+
* v1, v2 = MyNode.new, MyNode.new
|
|
694
|
+
* graph.link(v1, v2, [])
|
|
695
|
+
* ...
|
|
696
|
+
* v1.each_child_object { ... }
|
|
697
|
+
*/
|
|
698
|
+
|
|
699
|
+
/*
|
|
700
|
+
* Document-class: BGL::Graph
|
|
701
|
+
*
|
|
702
|
+
* A directed graph between Ruby objects. See BGL documentation.
|
|
703
|
+
*/
|
|
704
|
+
|
|
705
|
+
/*
|
|
706
|
+
* Document-class: BGL::Vertex
|
|
707
|
+
*
|
|
708
|
+
* A module to be mixed in objects used as vertices in Graph. It
|
|
709
|
+
* allows to use the same object in more than one graph.
|
|
710
|
+
*/
|
|
711
|
+
|
|
712
|
+
void Init_graph_algorithms()
|
|
713
|
+
{
|
|
714
|
+
id_new = rb_intern("new");
|
|
715
|
+
|
|
716
|
+
bglModule = rb_define_module("BGL");
|
|
717
|
+
bglGraph = rb_define_class_under(bglModule, "Graph", rb_cObject);
|
|
718
|
+
rb_define_const(bglGraph , "TREE" , INT2FIX(VISIT_TREE_EDGES));
|
|
719
|
+
rb_define_const(bglGraph , "FORWARD_OR_CROSS" , INT2FIX(VISIT_FORWARD_OR_CROSS_EDGES));
|
|
720
|
+
rb_define_const(bglGraph , "BACK" , INT2FIX(VISIT_BACK_EDGES));
|
|
721
|
+
rb_define_const(bglGraph , "NON_TREE" , INT2FIX(VISIT_NON_TREE_EDGES));
|
|
722
|
+
rb_define_const(bglGraph , "ALL" , INT2FIX(VISIT_ALL_EDGES));
|
|
723
|
+
|
|
724
|
+
rb_define_method(bglGraph, "components", RUBY_METHOD_FUNC(graph_components), -1);
|
|
725
|
+
rb_define_method(bglGraph, "generated_subgraphs", RUBY_METHOD_FUNC(graph_generated_subgraphs), -1);
|
|
726
|
+
rb_define_method(bglGraph, "each_dfs", RUBY_METHOD_FUNC(graph_direct_each_dfs), 2);
|
|
727
|
+
rb_define_method(bglGraph, "each_bfs", RUBY_METHOD_FUNC(graph_direct_each_bfs), 2);
|
|
728
|
+
rb_define_method(bglGraph, "reachable?", RUBY_METHOD_FUNC(graph_reachable_p), 2);
|
|
729
|
+
rb_define_method(bglGraph, "prune", RUBY_METHOD_FUNC(graph_prune), 0);
|
|
730
|
+
rb_define_method(bglGraph, "topological_sort", RUBY_METHOD_FUNC(graph_topological_sort), -1);
|
|
731
|
+
|
|
732
|
+
bglReverseGraph = rb_define_class_under(bglGraph, "Reverse", rb_cObject);
|
|
733
|
+
rb_define_method(bglReverseGraph, "generated_subgraphs",RUBY_METHOD_FUNC(graph_reverse_generated_subgraphs), -1);
|
|
734
|
+
rb_define_method(bglReverseGraph, "each_dfs", RUBY_METHOD_FUNC(graph_reverse_each_dfs), 2);
|
|
735
|
+
rb_define_method(bglReverseGraph, "each_bfs", RUBY_METHOD_FUNC(graph_reverse_each_bfs), 2);
|
|
736
|
+
rb_define_method(bglReverseGraph, "prune", RUBY_METHOD_FUNC(graph_prune), 0);
|
|
737
|
+
|
|
738
|
+
bglUndirectedGraph = rb_define_class_under(bglGraph, "Undirected", rb_cObject);
|
|
739
|
+
rb_define_method(bglUndirectedGraph, "generated_subgraphs", RUBY_METHOD_FUNC(graph_undirected_components), -1);
|
|
740
|
+
rb_define_method(bglUndirectedGraph, "each_dfs", RUBY_METHOD_FUNC(graph_undirected_each_dfs), 2);
|
|
741
|
+
rb_define_method(bglUndirectedGraph, "each_bfs", RUBY_METHOD_FUNC(graph_undirected_each_bfs), 2);
|
|
742
|
+
rb_define_method(bglUndirectedGraph, "prune", RUBY_METHOD_FUNC(graph_prune), 0);
|
|
743
|
+
|
|
744
|
+
utilrbValueSet = rb_define_class("ValueSet", rb_cObject);
|
|
745
|
+
}
|
|
746
|
+
|